Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_transform_worker.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de> filters
3 * SPDX-FileCopyrightText: 2005-2007 C. Boemann <cbo@boemann.dk>
4 * SPDX-FileCopyrightText: 2005, 2010 Boudewijn Rempt <boud@valdyas.org>
5 * SPDX-FileCopyrightText: 2010 Marc Pegon <pe.marc@free.fr>
6 * SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
12
13#include <qmath.h>
14#include <klocalizedstring.h>
15
16#include <QTransform>
17
18#include <KoColorSpace.h>
20#include <KoColor.h>
21
22#include "kis_paint_device.h"
23#include "kis_debug.h"
24#include "kis_selection.h"
25#include "kis_iterator_ng.h"
27#include "kis_filter_strategy.h"
28#include "kis_painter.h"
31#include "kis_pixel_selection.h"
32#include "kis_image.h"
33
34
36 double xscale, double yscale,
37 double xshear, double yshear,
38 double rotation,
39 qreal xtranslate, qreal ytranslate,
40 KoUpdaterPtr progress,
41 KisFilterStrategy *filter)
42{
43 m_dev = dev;
44 m_xscale = xscale;
45 m_yscale = yscale;
46 m_xshear = xshear;
47 m_yshear = yshear;
48 m_rotation = rotation,
49 m_xtranslate = xtranslate;
50 m_ytranslate = ytranslate;
51 m_progressUpdater = progress;
52 m_filter = filter;
53}
54
58
60{
61 QTransform SC = QTransform::fromScale(m_xscale, m_yscale);
62 QTransform S; S.shear(0, m_yshear); S.shear(m_xshear, 0);
63 QTransform R; R.rotateRadians(m_rotation);
64 QTransform T = QTransform::fromTranslate(m_xtranslate, m_ytranslate);
65
66 return SC * S * R * T;
67}
68
70{
71 if (pixelSelection->outlineCacheValid()) {
72 QPainterPath outlineCache = pixelSelection->outlineCache();
73 pixelSelection->setOutlineCache(transform().map(outlineCache));
74 }
75}
76
77QRect rotateWithTf(int rotation, KisPaintDeviceSP dev,
78 QRect boundRect,
79 KoUpdaterPtr progressUpdater,
80 int portion)
81{
82 qint32 pixelSize = dev->pixelSize();
83 QRect r(boundRect);
84
86 tmp->prepareClone(dev);
87
89 KisRandomAccessorSP tmpAcc = tmp->createRandomAccessorNG();
90 KisProgressUpdateHelper progressHelper(progressUpdater, portion, r.height());
91
92 QTransform tf;
93 tf = tf.rotate(rotation);
94
95 int ty = 0;
96 int tx = 0;
97
98 for (qint32 y = r.y(); y <= r.height() + r.y(); ++y) {
99 for (qint32 x = r.x(); x <= r.width() + r.x(); ++x) {
100 tf.map(x, y, &tx, &ty);
101 devAcc->moveTo(x, y);
102 tmpAcc->moveTo(tx, ty);
103
104 memcpy(tmpAcc->rawData(), devAcc->rawData(), pixelSize);
105 }
106 progressHelper.step();
107 }
108
109 dev->makeCloneFrom(tmp, tmp->region().boundingRect());
110 return r;
111}
112
114 QRect boundRect,
115 KoUpdaterPtr progressUpdater,
116 int portion)
117{
118 QRect r = rotateWithTf(90, dev, boundRect, progressUpdater, portion);
119 dev->moveTo(dev->x() - 1, dev->y());
120 return QRect(- r.top() - r.height(), r.x(), r.height(), r.width());
121}
122
124 QRect boundRect,
125 KoUpdaterPtr progressUpdater,
126 int portion)
127{
128 QRect r = rotateWithTf(270, dev, boundRect, progressUpdater, portion);
129 dev->moveTo(dev->x(), dev->y() - 1);
130 return QRect(r.top(), - r.x() - r.width(), r.height(), r.width());
131}
132
134 QRect boundRect,
135 KoUpdaterPtr progressUpdater,
136 int portion)
137{
138 QRect r = rotateWithTf(180, dev, boundRect, progressUpdater, portion);
139 dev->moveTo(dev->x() - 1, dev->y() -1);
140 return QRect(- r.x() - r.width(), - r.top() - r.height(), r.width(), r.height());
141}
142
147
152
153template <class iter> void calcDimensions(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines);
154
156(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
157{
158 srcStart = rc.x();
159 srcLen = rc.width();
160 firstLine = rc.y();
161 numLines = rc.height();
162}
163
165(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
166{
167 srcStart = rc.y();
168 srcLen = rc.height();
169 firstLine = rc.x();
170 numLines = rc.width();
171
172}
173
174template <class iter>
175void updateBounds(QRect &boundRect,
176 const KisFilterWeightsApplicator::LinePos &newBounds);
177
178template <>
180{
181 boundRect.setLeft(newBounds.start());
182 boundRect.setWidth(newBounds.size());
183}
184
185template <>
187{
188 boundRect.setTop(newBounds.start());
189 boundRect.setHeight(newBounds.size());
190}
191
192template <class T>
194 double floatscale, double shear, double dx,
195 KisFilterStrategy *filterStrategy,
196 int portion)
197{
198 bool clampToEdge = shear == 0.0;
199
200 qint32 srcStart, srcLen, firstLine, numLines;
201 calcDimensions<T>(m_boundRect, srcStart, srcLen, firstLine, numLines);
202
203 KisProgressUpdateHelper progressHelper(m_progressUpdater, portion, numLines);
204 KisFilterWeightsBuffer buf(filterStrategy, qAbs(floatscale));
205 KisFilterWeightsApplicator applicator(src, dst, floatscale, shear, dx, clampToEdge);
206
208
209 for (int i = firstLine; i < firstLine + numLines; i++) {
211 KisFilterWeightsApplicator::LinePos srcPos(srcStart, srcLen);
212
213 dstPos = applicator.processLine<T>(srcPos, i, &buf, filterStrategy->support(buf.weightsPositionScale().toFloat()));
214 dstBounds.unite(dstPos);
215
216 progressHelper.step();
217 }
218
219 updateBounds<T>(m_boundRect, dstBounds);
220}
221
222template<typename T>
223void swapValues(T *a, T *b) {
224 T c = *a;
225 *a = *b;
226 *b = c;
227}
228
230{
231 return runPartial(m_dev->exactBounds());
232}
233
234bool KisTransformWorker::runPartial(const QRect &processRect)
235{
236 /* Check for nonsense and let the user know, this helps debugging.
237 Otherwise the program will crash at a later point, in a very obscure way, probably by division by zero */
238 /* Note that because KisFilterWeightsBuffer uses KisFixedPoint, we need to check the fixed point as well
239 * since it has different precision than qreal or double */
240 KisFixedPoint m_xscale_fixedPoint = KisFixedPoint(m_xscale);
241 KisFixedPoint m_yscale_fixedPoint = KisFixedPoint(m_yscale);
242 Q_ASSERT_X(m_xscale != 0 && m_xscale_fixedPoint != 0, "KisTransformer::run() validation step", "xscale == 0");
243 Q_ASSERT_X(m_yscale != 0 && m_yscale_fixedPoint != 0, "KisTransformer::run() validation step", "yscale == 0");
244
245 // Fallback safety line in case Krita is compiled without ASSERTS
246 if (m_xscale == 0 || m_yscale == 0 || m_xscale_fixedPoint == 0 || m_yscale_fixedPoint == 0) return false;
247
248 m_boundRect = processRect;
249
250 if (m_boundRect.isNull()) {
251 if (!m_progressUpdater.isNull()) {
252 m_progressUpdater->setProgress(100);
253 }
254 return true;
255 }
256
257 double xscale = m_xscale;
258 double yscale = m_yscale;
259 double rotation = m_rotation;
260 qreal xtranslate = m_xtranslate;
261 qreal ytranslate = m_ytranslate;
262
263 // Apply shearX/Y separately. In Krita it is demanded separately
264 // most of the times.
265 if (m_xshear != 0 || m_yshear != 0) {
266 int portion = 50;
267
268 bool scalePresent = !(qFuzzyCompare(xscale, 1.0) && qFuzzyCompare(yscale, 1.0));
269 bool xShearPresent = !qFuzzyCompare(m_xshear, 0.0);
270 bool yShearPresent = !qFuzzyCompare(m_yshear, 0.0);
271
272 if (scalePresent || (xShearPresent && yShearPresent)) {
273 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), xscale, yscale * m_xshear, 0, m_filter, portion);
274 transformPass <KisVLineIteratorSP>(m_dev.data(), m_dev.data(), yscale, m_yshear, 0, m_filter, portion);
275 } else if (xShearPresent) {
276 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), xscale, m_xshear, 0, m_filter, portion);
277 } else if (yShearPresent) {
278 transformPass <KisVLineIteratorSP>(m_dev.data(), m_dev.data(), yscale, m_yshear, 0, m_filter, portion);
279 }
280
281 yscale = 1.;
282 xscale = 1.;
283 }
284
285 if (rotation < 0.0) {
286 rotation = -fmod(-rotation, 2 * M_PI) + 2 * M_PI;
287 } else {
288 rotation = fmod(rotation, 2 * M_PI);
289 }
290
291 int rotQuadrant = int(rotation / (M_PI / 2) + 0.5) & 3;
292 rotation -= rotQuadrant * M_PI / 2;
293
294
306 const bool simpleTransform =
308 (qFuzzyCompare(rotation, 0.0)) &&
309 (qFuzzyCompare(xscale, 1.0) ||
310 qFuzzyCompare(xscale, -1.0)) &&
311 (qFuzzyCompare(yscale, 1.0) ||
312 qFuzzyCompare(yscale, -1.0));
313
314
315 int progressTotalSteps = qMax(1, 2 * (!simpleTransform) + (rotQuadrant != 0));
316 int progressPortion = 100 / progressTotalSteps;
317
323 switch (rotQuadrant) {
324 case 1:
325 swapValues(&xscale, &yscale);
327 break;
328 case 2:
330 break;
331 case 3:
332 swapValues(&xscale, &yscale);
334 break;
335 default:
336 /* do nothing */
337 break;
338 }
339
340 if (simpleTransform) {
341
342 // Flipping horizontally
343 if (qFuzzyCompare(xscale, -1.0)) {
344 QRect bounds = m_dev->exactBounds();
345 double center_x = bounds.topLeft().x() + bounds.width() / 2.0;
346 xtranslate -= 2 * center_x;
347 mirrorX(m_dev);
348 }
349
350 // Flipping vertically
351 if (qFuzzyCompare(yscale, -1.0)) {
352 QRect bounds = m_dev->exactBounds();
353 double center_y = bounds.topLeft().y() + bounds.height() / 2.0;
354 ytranslate -= 2 * center_y;
355 mirrorY(m_dev);
356 }
357
358 // Simple translation
359 const int intXTranslate = qRound(xtranslate);
360 const int intYTranslate = qRound(ytranslate);
361
362 m_boundRect.translate(intXTranslate, intYTranslate);
363 m_dev->moveTo(m_dev->x() + intXTranslate, m_dev->y() + intYTranslate);
364
365 } else {
366 QTransform SC = QTransform::fromScale(xscale, yscale);
367 QTransform R; R.rotateRadians(rotation);
368 QTransform T = QTransform::fromTranslate(xtranslate, ytranslate);
369 QTransform m = SC * R * T;
370
377 qreal a = m.m11();
378 qreal b = m.m21();
379 qreal c = m.m31();
380 qreal d = m.m12() / m.m11();
381 qreal e = m.m22() - m.m21() * m.m12() / m.m11();
382 qreal f = m.m32() - m.m31() * m.m12() / m.m11();
383
384 // First Pass (X)
385 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), a, b, c, m_filter, progressPortion);
386
387 // Second Pass (Y)
388 transformPass <KisVLineIteratorSP>(m_dev.data(), m_dev.data(), e, d, f, m_filter, progressPortion);
389
390#if 0
391 /************************************************************/
398 qreal a = m.m11() - m.m21() * m.m12() / m.m22();
399 qreal b = m.m21() / m.m22();
400 qreal c = m.m31() - m.m21() * m.m32() / m.m22();
401 qreal d = m.m12();
402 qreal e = m.m22();
403 qreal f = m.m32();
404 // First Pass (X)
405 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), a, b, c, m_filter, progressPortion);
406 // Second Pass (Y)
407 transformPass <KisVLineIteratorSP>(m_dev.data(), m_dev.data(), e, d, f, m_filter, progressPortion);
408 /************************************************************/
409#endif /* 0 */
410
411#if 0
412 /************************************************************/
413 // Old three-pass implementation (for testing purposes)
414 yshear = sin(rotation);
415 xshear = -tan(rotation / 2);
416 xtranslate -= int(xshear * ytranslate);
417
418 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), xscale, yscale*xshear, 0, m_filter, 0);
419 transformPass <KisVLineIteratorSP>(m_dev.data(), m_dev.data(), yscale, yshear, ytranslate, m_filter, 0);
420 if (xshear != 0.0) {
421 transformPass <KisHLineIteratorSP>(m_dev.data(), m_dev.data(), 1.0, xshear, xtranslate, m_filter, 0);
422 } else {
423 m_dev->move(m_dev->x() + xtranslate, m_dev->y());
425 }
426 /************************************************************/
427#endif /* 0 */
428
429 }
430
431 if (!m_progressUpdater.isNull()) {
432 m_progressUpdater->setProgress(100);
433 }
434
440
441 return true;
442}
443
444void mirror_impl(KisPaintDeviceSP dev, qreal axis, bool isHorizontal)
445{
446 KIS_ASSERT_RECOVER_RETURN(qFloor(axis) == axis || (axis - qFloor(axis) == 0.5));
447
448 QRect mirrorRect = dev->exactBounds();
449 if (mirrorRect.width() <= 1) return;
450
473 int leftStart;
474 int rightEnd;
475
476 if (isHorizontal) {
477 leftStart = mirrorRect.x();
478 rightEnd = mirrorRect.x() + mirrorRect.width();
479 } else {
480 leftStart = mirrorRect.y();
481 rightEnd = mirrorRect.y() + mirrorRect.height();
482 }
483
488 const bool axisNonAligned = qFloor(axis) < axis;
489
490 int leftCenterPoint = qFloor(axis);
491 int leftEnd = qMin(leftCenterPoint, rightEnd);
492
493 int rightCenterPoint = axisNonAligned ? qCeil(axis) : qFloor(axis);
494 int rightStart = qMax(rightCenterPoint, leftStart);
495
496 int leftSize = qMax(0, leftEnd - leftStart);
497 int rightSize = qMax(0, rightEnd - rightStart);
498
499 int maxDistanceToAxis = qMax(leftCenterPoint - leftStart,
500 rightEnd - rightCenterPoint);
501
502
503 // Main variables for controlling the stages of the algorithm
504 bool moveLeftToRight = leftSize > rightSize;
505 int moveAmount = qAbs(leftSize - rightSize);
506 int swapAmount = qMin(leftSize, rightSize);
507
508 // Initial position of 'left' and 'right' block iterators
509 int initialLeftCol = leftCenterPoint - maxDistanceToAxis;
510 int initialRightCol = rightCenterPoint + maxDistanceToAxis - 1;
511
512
515 const KoColor defaultPixelObject = dev->defaultPixel();
516 const quint8 *defaultPixel = defaultPixelObject.data();
517
518 const int pixelSize = dev->pixelSize();
519 QByteArray buf(pixelSize, 0);
520
521 // Map (column, row) -> (x, y)
522 int rowsRemaining;
523 int row;
524
525 if (isHorizontal) {
526 rowsRemaining = mirrorRect.height();
527 row = mirrorRect.y();
528 } else {
529 rowsRemaining = mirrorRect.width();
530 row = mirrorRect.x();
531 }
532
533 int leftColPos = 0;
534 int rightColPos = 0;
535
536 const int &leftX = isHorizontal ? leftColPos : row;
537 const int &leftY = isHorizontal ? row : leftColPos;
538
539 const int &rightX = isHorizontal ? rightColPos : row;
540 const int &rightY = isHorizontal ? row : rightColPos;
541
542 while (rowsRemaining) {
543 leftColPos = initialLeftCol;
544 rightColPos = initialRightCol;
545
546 int rows = qMin(rowsRemaining, isHorizontal ? leftIt->numContiguousRows(leftY) : leftIt->numContiguousColumns(leftX));
547 int rowStride = isHorizontal ? leftIt->rowStride(leftX, leftY) : pixelSize;
548
549 if (moveLeftToRight) {
550 for (int i = 0; i < moveAmount; i++) {
551 leftIt->moveTo(leftX, leftY);
552 rightIt->moveTo(rightX, rightY);
553
554 quint8 *leftPtr = leftIt->rawData();
555 quint8 *rightPtr = rightIt->rawData();
556
557 for (int j = 0; j < rows; j++) {
558 // left-to-right move
559 memcpy(rightPtr, leftPtr, pixelSize);
560 memcpy(leftPtr, defaultPixel, pixelSize);
561
562 leftPtr += rowStride;
563 rightPtr += rowStride;
564 }
565
566 leftColPos++;
567 rightColPos--;
568 }
569 } else {
570 for (int i = 0; i < moveAmount; i++) {
571 leftIt->moveTo(leftX, leftY);
572 rightIt->moveTo(rightX, rightY);
573
574 quint8 *leftPtr = leftIt->rawData();
575 quint8 *rightPtr = rightIt->rawData();
576
577 for (int j = 0; j < rows; j++) {
578 // right-to-left move
579 memcpy(leftPtr, rightPtr, pixelSize);
580 memcpy(rightPtr, defaultPixel, pixelSize);
581
582 leftPtr += rowStride;
583 rightPtr += rowStride;
584 }
585
586 leftColPos++;
587 rightColPos--;
588 }
589 }
590
591 for (int i = 0; i < swapAmount; i++) {
592 leftIt->moveTo(leftX, leftY);
593 rightIt->moveTo(rightX, rightY);
594
595 quint8 *leftPtr = leftIt->rawData();
596 quint8 *rightPtr = rightIt->rawData();
597
598 for (int j = 0; j < rows; j++) {
599 // swap operation
600 memcpy(buf.data(), leftPtr, pixelSize);
601 memcpy(leftPtr, rightPtr, pixelSize);
602 memcpy(rightPtr, buf.data(), pixelSize);
603
604 leftPtr += rowStride;
605 rightPtr += rowStride;
606 }
607
608 leftColPos++;
609 rightColPos--;
610 }
611
612 rowsRemaining -= rows;
613 row += rows;
614 }
615}
616
618{
619 mirror_impl(dev, axis, true);
620}
621
623{
624 mirror_impl(dev, axis, false);
625}
626
628{
629 QRect bounds = dev->exactBounds();
630 mirrorX(dev, bounds.x() + 0.5 * bounds.width());
631}
632
634{
635 QRect bounds = dev->exactBounds();
636 mirrorY(dev, bounds.y() + 0.5 * bounds.height());
637}
638
639void KisTransformWorker::mirror(KisPaintDeviceSP dev, qreal axis, Qt::Orientation orientation)
640{
641 mirror_impl(dev, axis, orientation == Qt::Horizontal);
642}
643
644void KisTransformWorker::offset(KisPaintDeviceSP device, const QPoint& offsetPosition, const QRect& wrapRect)
645{
646 Q_ASSERT(wrapRect == wrapRect.normalized());
647
648 // inspired by gimp offset code, only wrap mode supported
649 int sx = wrapRect.x();
650 int sy = wrapRect.y();
651
652 int width = wrapRect.width();
653 int height = wrapRect.height();
654
655 // offset coords are relative to space wrapRect
656 int offsetX = offsetPosition.x();
657 int offsetY = offsetPosition.y();
658
659 while (offsetX < 0)
660 {
661 offsetX += width;
662 }
663
664 while (offsetY < 0)
665 {
666 offsetY += height;
667 }
668
669 if ((offsetX == 0) && (offsetY == 0))
670 {
671 return;
672 }
673
674 KisPaintDeviceSP offsetDevice = new KisPaintDevice(device->colorSpace());
675
676 int srcX = 0;
677 int srcY = 0;
678
679 int destX = offsetX;
680 int destY = offsetY;
681
682 width = qBound<int>(0, width - offsetX, width);
683 height = qBound<int>(0, height - offsetY, height);
684
685 if ((width != 0) && (height != 0)) {
686 // convert back to paint device space
687 KisPainter::copyAreaOptimized(QPoint(destX + sx, destY + sy), device, offsetDevice, QRect(srcX + sx, srcY + sy, width, height));
688 }
689
690 srcX = wrapRect.width() - offsetX;
691 srcY = wrapRect.height() - offsetY;
692
693 destX = (srcX + offsetX) % wrapRect.width();
694 destY = (srcY + offsetY) % wrapRect.height();
695
696 if (offsetX != 0 && offsetY != 0) {
697 KisPainter::copyAreaOptimized(QPoint(destX + sx, destY + sy), device, offsetDevice, QRect(srcX + sx, srcY + sy, offsetX, offsetY));
698 }
699
700 if (offsetX != 0) {
701 KisPainter::copyAreaOptimized(QPoint(destX + sx, (destY + offsetY) + sy), device, offsetDevice, QRect(srcX + sx, 0 + sy, offsetX, wrapRect.height() - offsetY));
702 }
703
704 if (offsetY != 0) {
705 KisPainter::copyAreaOptimized(QPoint((destX + offsetX) + sx, destY + sy), device, offsetDevice, QRect(0 + sx, srcY + sy, wrapRect.width() - offsetX, offsetY));
706 }
707
708 // bitblt the result back
709 QRect resultRect(sx, sy, wrapRect.width(), wrapRect.height());
710 KisPainter::copyAreaOptimized(resultRect.topLeft(), offsetDevice, device, resultRect);
711}
712
713
float value(const T *src, size_t ch)
Eigen::Matrix< double, 4, 2 > S
Eigen::Matrix< double, 4, 2 > R
virtual quint8 * rawData()=0
virtual qreal support(qreal weightsPositionScale)
LinePos processLine(LinePos srcLine, int line, KisFilterWeightsBuffer *buffer, qreal filterSupport)
quint32 pixelSize() const
QRect exactBounds() const
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
void moveTo(qint32 x, qint32 y)
void makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
KisRandomAccessorSP createRandomAccessorNG()
static void copyAreaOptimized(const QPoint &dstPt, KisPaintDeviceSP src, KisPaintDeviceSP dst, const QRect &originalSrcRect)
virtual qint32 rowStride(qint32 x, qint32 y) const =0
virtual qint32 numContiguousRows(qint32 y) const =0
virtual void moveTo(qint32 x, qint32 y)=0
virtual qint32 numContiguousColumns(qint32 x) const =0
void transformPixelSelectionOutline(KisPixelSelectionSP pixelSelection) const
static QRect rotate180(KisPaintDeviceSP dev, QRect boundRect, KoUpdaterPtr progressUpdater, int portion)
bool runPartial(const QRect &processRect)
static QRect rotateRight90(KisPaintDeviceSP dev, QRect boundRect, KoUpdaterPtr progressUpdater, int portion)
static void mirrorY(KisPaintDeviceSP dev, qreal axis)
void setForceSubPixelTranslation(bool value)
static void mirror(KisPaintDeviceSP dev, qreal axis, Qt::Orientation orientation)
static void offset(KisPaintDeviceSP device, const QPoint &offsetPosition, const QRect &wrapRect)
void transformPass(KisPaintDevice *src, KisPaintDevice *dst, double xscale, double shear, double dx, KisFilterStrategy *filterStrategy, int portion)
static QRect rotateLeft90(KisPaintDeviceSP dev, QRect boundRect, KoUpdaterPtr progressUpdater, int portion)
bool forceSubPixelTranslation() const
QTransform transform() const
static void mirrorX(KisPaintDeviceSP dev, qreal axis)
KisTransformWorker(KisPaintDeviceSP dev, double xscale, double yscale, double xshear, double yshear, double rotation, qreal xtranslate, qreal ytranslate, KoUpdaterPtr progress, KisFilterStrategy *filter)
KisFilterStrategy * m_filter
quint8 * data()
Definition KoColor.h:144
static bool qFuzzyCompare(half p1, half p2)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define bounds(x, a, b)
#define M_PI
Definition kis_global.h:111
void calcDimensions(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
void swapValues(T *a, T *b)
void updateBounds< KisVLineIteratorSP >(QRect &boundRect, const KisFilterWeightsApplicator::LinePos &newBounds)
void updateBounds< KisHLineIteratorSP >(QRect &boundRect, const KisFilterWeightsApplicator::LinePos &newBounds)
QRect rotateWithTf(int rotation, KisPaintDeviceSP dev, QRect boundRect, KoUpdaterPtr progressUpdater, int portion)
void calcDimensions< KisHLineIteratorSP >(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
void mirror_impl(KisPaintDeviceSP dev, qreal axis, bool isHorizontal)
void updateBounds(QRect &boundRect, const KisFilterWeightsApplicator::LinePos &newBounds)
void calcDimensions< KisVLineIteratorSP >(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
void setOutlineCache(const QPainterPath &cache)