Commit ae915410 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix double-mirrored dabs when painting with precision < 5

Even though KisDabRenderingQueue returns mutable dabs, it doesn't
guarantee that they will be unique in the returned list. We should
be very careful with them and ensure that we don't mirror the same
paint device twice.

BUG:397831
parent 126608b4
......@@ -2973,12 +2973,12 @@ void KisPainter::mirrorRect(Qt::Orientation direction, QRect *rc) const
KritaUtils::mirrorRect(direction, effectiveAxesCenter, rc);
}
void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab) const
void KisPainter::mirrorDab(Qt::Orientation direction, KisRenderedDab *dab, bool skipMirrorPixels) const
{
KisLodTransform t(d->device);
QPoint effectiveAxesCenter = t.map(d->axesCenter).toPoint();
KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab);
KritaUtils::mirrorDab(direction, effectiveAxesCenter, dab, skipMirrorPixels);
}
namespace {
......
......@@ -673,7 +673,7 @@ public:
* Mirror \p dab in the requested direction around the center point defined
* in the painter. The dab's offset is adjusted automatically.
*/
void mirrorDab(Qt::Orientation direction, KisRenderedDab *dab) const;
void mirrorDab(Qt::Orientation direction, KisRenderedDab *dab, bool skipMirrorPixels = false) const;
/**
* Calculate the list of the mirrored rects that will be painted on the
......
......@@ -461,19 +461,23 @@ namespace KritaUtils
return qreal(numTransparentPixels) / numPixels;
}
void mirrorDab(Qt::Orientation dir, const QPoint &center, KisRenderedDab *dab)
void mirrorDab(Qt::Orientation dir, const QPoint &center, KisRenderedDab *dab, bool skipMirrorPixels)
{
const QRect rc = dab->realBounds();
if (dir == Qt::Horizontal) {
const int mirrorX = -((rc.x() + rc.width()) - center.x()) + center.x();
dab->device->mirror(true, false);
if (!skipMirrorPixels) {
dab->device->mirror(true, false);
}
dab->offset.rx() = mirrorX;
} else /* if (dir == Qt::Vertical) */ {
const int mirrorY = -((rc.y() + rc.height()) - center.y()) + center.y();
dab->device->mirror(false, true);
if (!skipMirrorPixels) {
dab->device->mirror(false, true);
}
dab->offset.ry() = mirrorY;
}
}
......
......@@ -115,7 +115,7 @@ namespace KritaUtils
qreal KRITAIMAGE_EXPORT estimatePortionOfTransparentPixels(KisPaintDeviceSP dev, const QRect &rect, qreal samplePortion);
void KRITAIMAGE_EXPORT mirrorDab(Qt::Orientation dir, const QPoint &center, KisRenderedDab *dab);
void KRITAIMAGE_EXPORT mirrorDab(Qt::Orientation dir, const QPoint &center, KisRenderedDab *dab, bool skipMirrorPixels = false);
void KRITAIMAGE_EXPORT mirrorRect(Qt::Orientation dir, const QPoint &center, QRect *rc);
void KRITAIMAGE_EXPORT mirrorPoint(Qt::Orientation dir, const QPoint &center, QPointF *pt);
}
......
......@@ -202,13 +202,25 @@ void KisBrushOp::addMirroringJobs(Qt::Orientation direction,
{
jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL));
/**
* Some KisRenderedDab may share their devices, so we should mirror them
* carefully, avoiding doing that twice. KisDabRenderingQueue is implemented in
* a way that duplicated dabs can go only sequentially, one after another, so
* we don't have to use complex deduplication algorithms here.
*/
KisFixedPaintDeviceSP prevDabDevice = 0;
for (KisRenderedDab &dab : state->dabsQueue) {
const bool skipMirrorPixels = prevDabDevice && prevDabDevice == dab.device;
jobs.append(
new KisRunnableStrokeJobData(
[state, &dab, direction] () {
state->painter->mirrorDab(direction, &dab);
[state, &dab, direction, skipMirrorPixels] () {
state->painter->mirrorDab(direction, &dab, skipMirrorPixels);
},
KisStrokeJobData::CONCURRENT));
prevDabDevice = dab.device;
}
jobs.append(new KisRunnableStrokeJobData(0, KisStrokeJobData::SEQUENTIAL));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment