File indexing completed on 2024-06-16 04:18:02

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_warp_transform_strategy.h"
0008 
0009 #include <algorithm>
0010 
0011 #include <QPointF>
0012 #include <QPainter>
0013 #include <QPainterPath>
0014 
0015 #include "kis_coordinates_converter.h"
0016 #include "tool_transform_args.h"
0017 #include "transform_transaction_properties.h"
0018 #include "kis_painting_tweaks.h"
0019 #include "kis_cursor.h"
0020 #include "kis_transform_utils.h"
0021 #include "kis_algebra_2d.h"
0022 #include "KisHandlePainterHelper.h"
0023 #include "kis_signal_compressor.h"
0024 
0025 
0026 
0027 struct KisWarpTransformStrategy::Private
0028 {
0029     Private(KisWarpTransformStrategy *_q,
0030             const KisCoordinatesConverter *_converter,
0031             ToolTransformArgs &_currentArgs,
0032             TransformTransactionProperties &_transaction)
0033         : q(_q),
0034           converter(_converter),
0035           currentArgs(_currentArgs),
0036           transaction(_transaction),
0037           recalculateSignalCompressor(40, KisSignalCompressor::FIRST_ACTIVE)
0038     {
0039     }
0040 
0041     KisWarpTransformStrategy * const q;
0042 
0043     /// standard members ///
0044 
0045     const KisCoordinatesConverter *converter {0};
0046 
0047     //////
0048     ToolTransformArgs &currentArgs;
0049     //////
0050     TransformTransactionProperties &transaction;
0051 
0052     QTransform paintingTransform;
0053     QPointF paintingOffset;
0054 
0055     QTransform handlesTransform;
0056 
0057     /// custom members ///
0058 
0059     QImage transformedImage;
0060 
0061     int pointIndexUnderCursor {0};
0062 
0063     enum Mode {
0064         OVER_POINT = 0,
0065         MULTIPLE_POINT_SELECTION,
0066         MOVE_MODE,
0067         ROTATE_MODE,
0068         SCALE_MODE,
0069         NOTHING
0070     };
0071     Mode mode {NOTHING};
0072 
0073     QVector<int> pointsInAction;
0074     int lastNumPoints {0};
0075 
0076     bool drawConnectionLines {false}; // useful while developing
0077     bool drawOrigPoints {false};
0078     bool drawTransfPoints {true};
0079     bool closeOnStartPointClick {false};
0080     bool clipOriginalPointsPosition {true};
0081     QPointF pointPosOnClick;
0082     bool pointWasDragged {false};
0083 
0084     QPointF lastMousePos;
0085 
0086     // cage transform also uses this logic. This helps this class know what transform type we are using
0087     TransformType transformType = TransformType::WARP_TRANSFORM;
0088     KisSignalCompressor recalculateSignalCompressor;
0089 
0090     void recalculateTransformations();
0091     inline QPointF imageToThumb(const QPointF &pt, bool useFlakeOptimization);
0092 
0093     bool shouldCloseTheCage() const;
0094     QVector<QPointF*> getSelectedPoints(QPointF *center, bool limitToSelectedOnly = false) const;
0095 };
0096 
0097 KisWarpTransformStrategy::KisWarpTransformStrategy(const KisCoordinatesConverter *converter,
0098                                                    KoSnapGuide *snapGuide,
0099                                                    ToolTransformArgs &currentArgs,
0100                                                    TransformTransactionProperties &transaction)
0101     : KisSimplifiedActionPolicyStrategy(converter, snapGuide),
0102       m_d(new Private(this, converter, currentArgs, transaction))
0103 {
0104     connect(&m_d->recalculateSignalCompressor, SIGNAL(timeout()),
0105             SLOT(recalculateTransformations()));
0106 }
0107 
0108 KisWarpTransformStrategy::~KisWarpTransformStrategy()
0109 {
0110 }
0111 
0112 void KisWarpTransformStrategy::setTransformFunction(const QPointF &mousePos, bool perspectiveModifierActive, bool shiftModifierActive, bool altModifierActive)
0113 {
0114     Q_UNUSED(shiftModifierActive);
0115     Q_UNUSED(altModifierActive);
0116 
0117     const double handleRadius = KisTransformUtils::effectiveHandleGrabRadius(m_d->converter);
0118 
0119     bool cursorOverPoint = false;
0120     m_d->pointIndexUnderCursor = -1;
0121 
0122     KisTransformUtils::HandleChooser<Private::Mode>
0123         handleChooser(mousePos, Private::NOTHING);
0124 
0125     const QVector<QPointF> &points = m_d->currentArgs.transfPoints();
0126     for (int i = 0; i < points.size(); ++i) {
0127         if (handleChooser.addFunction(points[i],
0128                                       handleRadius, Private::NOTHING)) {
0129 
0130             cursorOverPoint = true;
0131             m_d->pointIndexUnderCursor = i;
0132         }
0133     }
0134 
0135     if (cursorOverPoint) {
0136         m_d->mode = perspectiveModifierActive &&
0137             !m_d->currentArgs.isEditingTransformPoints() ?
0138             Private::MULTIPLE_POINT_SELECTION : Private::OVER_POINT;
0139 
0140     } else if (!m_d->currentArgs.isEditingTransformPoints()) {
0141         QPolygonF polygon(m_d->currentArgs.transfPoints());
0142         bool insidePolygon = polygon.boundingRect().contains(mousePos);
0143         m_d->mode = insidePolygon ? Private::MOVE_MODE :
0144             !perspectiveModifierActive ? Private::ROTATE_MODE :
0145             Private::SCALE_MODE;
0146     } else {
0147         m_d->mode = Private::NOTHING;
0148     }
0149 }
0150 
0151 QCursor KisWarpTransformStrategy::getCurrentCursor() const
0152 {
0153     QCursor cursor;
0154 
0155     switch (m_d->mode) {
0156     case Private::OVER_POINT:
0157         cursor = KisCursor::pointingHandCursor();
0158         break;
0159     case Private::MULTIPLE_POINT_SELECTION:
0160         cursor = KisCursor::crossCursor();
0161         break;
0162     case Private::MOVE_MODE:
0163         cursor = KisCursor::moveCursor();
0164         break;
0165     case Private::ROTATE_MODE:
0166         cursor = KisCursor::rotateCursor();
0167         break;
0168     case Private::SCALE_MODE:
0169         cursor = KisCursor::sizeVerCursor();
0170         break;
0171     case Private::NOTHING:
0172         cursor = KisCursor::arrowCursor();
0173         break;
0174     }
0175 
0176     return cursor;
0177 }
0178 
0179 void KisWarpTransformStrategy::overrideDrawingItems(bool drawConnectionLines,
0180                                                     bool drawOrigPoints,
0181                                                     bool drawTransfPoints)
0182 {
0183     m_d->drawConnectionLines = drawConnectionLines;
0184     m_d->drawOrigPoints = drawOrigPoints;
0185     m_d->drawTransfPoints = drawTransfPoints;
0186 }
0187 
0188 void KisWarpTransformStrategy::setCloseOnStartPointClick(bool value)
0189 {
0190     m_d->closeOnStartPointClick = value;
0191 }
0192 
0193 void KisWarpTransformStrategy::setClipOriginalPointsPosition(bool value)
0194 {
0195     m_d->clipOriginalPointsPosition = value;
0196 }
0197 
0198 void KisWarpTransformStrategy::setTransformType(TransformType type) {
0199     m_d->transformType = type;
0200 }
0201 
0202 void KisWarpTransformStrategy::drawConnectionLines(QPainter &gc,
0203                                                    const QVector<QPointF> &origPoints,
0204                                                    const QVector<QPointF> &transfPoints,
0205                                                    bool isEditingPoints)
0206 {
0207     Q_UNUSED(isEditingPoints);
0208 
0209     QPen antsPen;
0210     QPen outlinePen;
0211 
0212     KisPaintingTweaks::initAntsPen(&antsPen, &outlinePen);
0213     antsPen.setWidth(decorationThickness());
0214     outlinePen.setWidth(decorationThickness());
0215 
0216     const int numPoints = origPoints.size();
0217 
0218     for (int i = 0; i < numPoints; ++i) {
0219         gc.setPen(outlinePen);
0220         gc.drawLine(transfPoints[i], origPoints[i]);
0221         gc.setPen(antsPen);
0222         gc.drawLine(transfPoints[i], origPoints[i]);
0223     }
0224 }
0225 
0226 void KisWarpTransformStrategy::paint(QPainter &gc)
0227 {
0228     // Draw preview image
0229 
0230     gc.save();
0231 
0232     gc.setOpacity(m_d->transaction.basePreviewOpacity());
0233     gc.setTransform(m_d->paintingTransform, true);
0234     gc.drawImage(m_d->paintingOffset, m_d->transformedImage);
0235 
0236     gc.restore();
0237 
0238 
0239     gc.save();
0240     gc.setTransform(m_d->handlesTransform, true);
0241 
0242     if (m_d->drawConnectionLines) {
0243         gc.setOpacity(0.5);
0244 
0245         drawConnectionLines(gc,
0246                             m_d->currentArgs.origPoints(),
0247                             m_d->currentArgs.transfPoints(),
0248                             m_d->currentArgs.isEditingTransformPoints());
0249     }
0250 
0251 
0252     QPen mainPen(Qt::black);
0253     mainPen.setCosmetic(true);
0254     mainPen.setWidth(decorationThickness());
0255     QPen outlinePen(Qt::white);
0256     outlinePen.setCosmetic(true);
0257     outlinePen.setWidth(decorationThickness());
0258 
0259     // draw handles
0260     {
0261         const int numPoints = m_d->currentArgs.origPoints().size();
0262 
0263 
0264 
0265         qreal handlesExtraScale = KisTransformUtils::scaleFromAffineMatrix(m_d->handlesTransform);
0266 
0267         qreal dstIn = 8 / handlesExtraScale;
0268         qreal dstOut = 10 / handlesExtraScale;
0269         qreal srcIn = 6 / handlesExtraScale;
0270         qreal srcOut = 6 / handlesExtraScale;
0271 
0272         QRectF handleRect1(-0.5 * dstIn, -0.5 * dstIn, dstIn, dstIn);
0273         QRectF handleRect2(-0.5 * dstOut, -0.5 * dstOut, dstOut, dstOut);
0274 
0275         if (m_d->drawTransfPoints) {
0276             gc.setOpacity(1.0);
0277 
0278             for (int i = 0; i < numPoints; ++i) {
0279                 gc.setPen(outlinePen);
0280                 gc.drawEllipse(handleRect2.translated(m_d->currentArgs.transfPoints()[i]));
0281                 gc.setPen(mainPen);
0282                 gc.drawEllipse(handleRect1.translated(m_d->currentArgs.transfPoints()[i]));
0283             }
0284 
0285             QPointF center;
0286             QVector<QPointF*> selectedPoints = m_d->getSelectedPoints(&center, true);
0287 
0288             QBrush selectionBrush = selectedPoints.size() > 1 ? Qt::red : Qt::black;
0289 
0290             QBrush oldBrush = gc.brush();
0291             gc.setBrush(selectionBrush);
0292             Q_FOREACH (const QPointF *pt, selectedPoints) {
0293                 gc.drawEllipse(handleRect1.translated(*pt));
0294             }
0295             gc.setBrush(oldBrush);
0296 
0297         }
0298 
0299         if (m_d->drawOrigPoints) {
0300             QPainterPath inLine;
0301             inLine.moveTo(-0.5 * srcIn,            0);
0302             inLine.lineTo( 0.5 * srcIn,            0);
0303             inLine.moveTo(           0, -0.5 * srcIn);
0304             inLine.lineTo(           0,  0.5 * srcIn);
0305 
0306             QPainterPath outLine;
0307             outLine.moveTo(-0.5 * srcOut, -0.5 * srcOut);
0308             outLine.lineTo( 0.5 * srcOut, -0.5 * srcOut);
0309             outLine.lineTo( 0.5 * srcOut,  0.5 * srcOut);
0310             outLine.lineTo(-0.5 * srcOut,  0.5 * srcOut);
0311             outLine.lineTo(-0.5 * srcOut, -0.5 * srcOut);
0312 
0313             gc.setOpacity(0.5);
0314 
0315             for (int i = 0; i < numPoints; ++i) {
0316                 gc.setPen(outlinePen);
0317                 gc.drawPath(outLine.translated(m_d->currentArgs.origPoints()[i]));
0318                 gc.setPen(mainPen);
0319                 gc.drawPath(inLine.translated(m_d->currentArgs.origPoints()[i]));
0320             }
0321         }
0322 
0323     }
0324 
0325     // draw grid lines only if we are using the GRID mode. Also only use this logic for warp, not cage transforms
0326     if (m_d->currentArgs.warpCalculation() == KisWarpTransformWorker::WarpCalculation::GRID &&
0327         m_d->transformType == TransformType::WARP_TRANSFORM ) {
0328 
0329     // see how many rows we have. we are only going to do lines up to 6 divisions/
0330     // it is almost impossible to use with 6 even.
0331     const int numPoints = m_d->currentArgs.origPoints().size();
0332 
0333     // grid is always square, so get the square root to find # of rows
0334     int rowsInWarp = sqrt(m_d->currentArgs.origPoints().size());
0335 
0336 
0337         KisHandlePainterHelper handlePainter(&gc, 0.0, decorationThickness());
0338         handlePainter.setHandleStyle(KisHandleStyle::primarySelection());
0339 
0340         // draw horizontal lines
0341         for (int i = 0; i < numPoints; i++) {
0342             if (i != 0 &&  i % rowsInWarp == rowsInWarp -1) {
0343                 // skip line if it is the last in the row
0344             } else {
0345                 handlePainter.drawConnectionLine(m_d->currentArgs.transfPoints()[i], m_d->currentArgs.transfPoints()[i+1]  );
0346             }
0347         }
0348 
0349         // draw vertical lines
0350         for (int i = 0; i < numPoints; i++) {
0351 
0352             if ( (numPoints - i - 1) < rowsInWarp ) {
0353                 // last row doesn't need to draw vertical lines
0354             } else {
0355                 handlePainter.drawConnectionLine(m_d->currentArgs.transfPoints()[i], m_d->currentArgs.transfPoints()[i+rowsInWarp] );
0356             }
0357         }
0358 
0359     } // end if statement
0360 
0361     gc.restore();
0362 }
0363 
0364 void KisWarpTransformStrategy::externalConfigChanged()
0365 {
0366     if (m_d->lastNumPoints != m_d->currentArgs.transfPoints().size()) {
0367         m_d->pointsInAction.clear();
0368     }
0369 
0370     m_d->recalculateTransformations();
0371 }
0372 
0373 bool KisWarpTransformStrategy::beginPrimaryAction(const QPointF &pt)
0374 {
0375     const bool isEditingPoints = m_d->currentArgs.isEditingTransformPoints();
0376     bool retval = false;
0377 
0378     if (m_d->mode == Private::OVER_POINT ||
0379         m_d->mode == Private::MULTIPLE_POINT_SELECTION ||
0380         m_d->mode == Private::MOVE_MODE ||
0381         m_d->mode == Private::ROTATE_MODE ||
0382         m_d->mode == Private::SCALE_MODE) {
0383 
0384         retval = true;
0385 
0386     } else if (isEditingPoints) {
0387         QPointF newPos = m_d->clipOriginalPointsPosition ?
0388             KisTransformUtils::clipInRect(pt, m_d->transaction.originalRect()) :
0389             pt;
0390 
0391         m_d->currentArgs.refOriginalPoints().append(newPos);
0392         m_d->currentArgs.refTransformedPoints().append(newPos);
0393 
0394         m_d->mode = Private::OVER_POINT;
0395         m_d->pointIndexUnderCursor = m_d->currentArgs.origPoints().size() - 1;
0396 
0397         m_d->recalculateSignalCompressor.start();
0398 
0399         retval = true;
0400     }
0401 
0402     if (m_d->mode == Private::OVER_POINT) {
0403         m_d->pointPosOnClick =
0404             m_d->currentArgs.transfPoints()[m_d->pointIndexUnderCursor];
0405         m_d->pointWasDragged = false;
0406 
0407         m_d->pointsInAction.clear();
0408         m_d->pointsInAction << m_d->pointIndexUnderCursor;
0409         m_d->lastNumPoints = m_d->currentArgs.transfPoints().size();
0410     } else if (m_d->mode == Private::MULTIPLE_POINT_SELECTION) {
0411         QVector<int>::iterator it =
0412             std::find(m_d->pointsInAction.begin(),
0413                       m_d->pointsInAction.end(),
0414                       m_d->pointIndexUnderCursor);
0415 
0416         if (it != m_d->pointsInAction.end()) {
0417             m_d->pointsInAction.erase(it);
0418         } else {
0419             m_d->pointsInAction << m_d->pointIndexUnderCursor;
0420         }
0421 
0422         m_d->lastNumPoints = m_d->currentArgs.transfPoints().size();
0423     }
0424 
0425     m_d->lastMousePos = pt;
0426     return retval;
0427 }
0428 
0429 QVector<QPointF*> KisWarpTransformStrategy::Private::getSelectedPoints(QPointF *center, bool limitToSelectedOnly) const
0430 {
0431     QVector<QPointF> &points = currentArgs.refTransformedPoints();
0432 
0433     QRectF boundingRect;
0434     QVector<QPointF*> selectedPoints;
0435     if (limitToSelectedOnly || pointsInAction.size() > 1) {
0436         Q_FOREACH (int index, pointsInAction) {
0437             selectedPoints << &points[index];
0438             KisAlgebra2D::accumulateBounds(points[index], &boundingRect);
0439         }
0440     } else {
0441         QVector<QPointF>::iterator it = points.begin();
0442         QVector<QPointF>::iterator end = points.end();
0443         for (; it != end; ++it) {
0444             selectedPoints << &(*it);
0445             KisAlgebra2D::accumulateBounds(*it, &boundingRect);
0446         }
0447     }
0448 
0449     *center = boundingRect.center();
0450     return selectedPoints;
0451 }
0452 
0453 void KisWarpTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shiftModifierActive, bool altModifierActive)
0454 {
0455     Q_UNUSED(shiftModifierActive);
0456     Q_UNUSED(altModifierActive);
0457 
0458     // toplevel code switches to HOVER mode if nothing is selected
0459     KIS_ASSERT_RECOVER_RETURN(m_d->mode == Private::MOVE_MODE ||
0460                               m_d->mode == Private::ROTATE_MODE ||
0461                               m_d->mode == Private::SCALE_MODE ||
0462                               (m_d->mode == Private::OVER_POINT &&
0463                                m_d->pointIndexUnderCursor >= 0 &&
0464                                m_d->pointsInAction.size() == 1) ||
0465                               (m_d->mode == Private::MULTIPLE_POINT_SELECTION &&
0466                                m_d->pointIndexUnderCursor >= 0));
0467 
0468     if (m_d->mode == Private::OVER_POINT) {
0469         if (m_d->currentArgs.isEditingTransformPoints()) {
0470             QPointF newPos = m_d->clipOriginalPointsPosition ?
0471                 KisTransformUtils::clipInRect(pt, m_d->transaction.originalRect()) :
0472                 pt;
0473             m_d->currentArgs.origPoint(m_d->pointIndexUnderCursor) = newPos;
0474             m_d->currentArgs.transfPoint(m_d->pointIndexUnderCursor) = newPos;
0475         } else {
0476             m_d->currentArgs.transfPoint(m_d->pointIndexUnderCursor) = pt;
0477         }
0478 
0479 
0480         const qreal handleRadiusSq = pow2(KisTransformUtils::effectiveHandleGrabRadius(m_d->converter));
0481         qreal dist =
0482             kisSquareDistance(
0483                 m_d->currentArgs.transfPoint(m_d->pointIndexUnderCursor),
0484                 m_d->pointPosOnClick);
0485 
0486         if (dist > handleRadiusSq) {
0487             m_d->pointWasDragged = true;
0488         }
0489     } else if (m_d->mode == Private::MOVE_MODE) {
0490         QPointF center;
0491         QVector<QPointF*> selectedPoints = m_d->getSelectedPoints(&center);
0492 
0493         QPointF diff = pt - m_d->lastMousePos;
0494 
0495         QVector<QPointF*>::iterator it = selectedPoints.begin();
0496         QVector<QPointF*>::iterator end = selectedPoints.end();
0497         for (; it != end; ++it) {
0498             **it += diff;
0499         }
0500     } else if (m_d->mode == Private::ROTATE_MODE) {
0501         QPointF center;
0502         QVector<QPointF*> selectedPoints = m_d->getSelectedPoints(&center);
0503 
0504         QPointF oldDirection = m_d->lastMousePos - center;
0505         QPointF newDirection = pt - center;
0506 
0507         qreal rotateAngle = KisAlgebra2D::angleBetweenVectors(oldDirection, newDirection);
0508         QTransform R;
0509         R.rotateRadians(rotateAngle);
0510 
0511         QTransform t =
0512             QTransform::fromTranslate(-center.x(), -center.y()) *
0513             R *
0514             QTransform::fromTranslate(center.x(), center.y());
0515 
0516         QVector<QPointF*>::iterator it = selectedPoints.begin();
0517         QVector<QPointF*>::iterator end = selectedPoints.end();
0518         for (; it != end; ++it) {
0519             **it = t.map(**it);
0520         }
0521     } else if (m_d->mode == Private::SCALE_MODE) {
0522         QPointF center;
0523         QVector<QPointF*> selectedPoints = m_d->getSelectedPoints(&center);
0524 
0525         QPolygonF polygon(m_d->currentArgs.origPoints());
0526         QSizeF maxSize = polygon.boundingRect().size();
0527         qreal maxDimension = qMax(maxSize.width(), maxSize.height());
0528 
0529         qreal scale = 1.0 - (pt - m_d->lastMousePos).y() / maxDimension;
0530 
0531         QTransform t =
0532             QTransform::fromTranslate(-center.x(), -center.y()) *
0533             QTransform::fromScale(scale, scale) *
0534             QTransform::fromTranslate(center.x(), center.y());
0535 
0536         QVector<QPointF*>::iterator it = selectedPoints.begin();
0537         QVector<QPointF*>::iterator end = selectedPoints.end();
0538         for (; it != end; ++it) {
0539             **it = t.map(**it);
0540         }
0541     }
0542 
0543     m_d->lastMousePos = pt;
0544     m_d->recalculateSignalCompressor.start();
0545 
0546 }
0547 
0548 bool KisWarpTransformStrategy::Private::shouldCloseTheCage() const
0549 {
0550     return currentArgs.isEditingTransformPoints() &&
0551         closeOnStartPointClick &&
0552         pointIndexUnderCursor == 0 &&
0553         currentArgs.origPoints().size() > 2 &&
0554         !pointWasDragged;
0555 }
0556 
0557 bool KisWarpTransformStrategy::acceptsClicks() const
0558 {
0559     return m_d->shouldCloseTheCage() ||
0560         m_d->currentArgs.isEditingTransformPoints();
0561 }
0562 
0563 bool KisWarpTransformStrategy::endPrimaryAction()
0564 {
0565     if (m_d->shouldCloseTheCage()) {
0566         m_d->currentArgs.setEditingTransformPoints(false);
0567     }
0568 
0569     return true;
0570 }
0571 
0572 inline QPointF KisWarpTransformStrategy::Private::imageToThumb(const QPointF &pt, bool useFlakeOptimization)
0573 {
0574     return useFlakeOptimization ? converter->imageToDocument(converter->documentToFlake((pt))) : q->thumbToImageTransform().inverted().map(pt);
0575 }
0576 
0577 void KisWarpTransformStrategy::Private::recalculateTransformations()
0578 {
0579     QTransform scaleTransform = KisTransformUtils::imageToFlakeTransform(converter);
0580 
0581     QTransform resultThumbTransform = q->thumbToImageTransform() * scaleTransform;
0582     qreal scale = KisTransformUtils::scaleFromAffineMatrix(resultThumbTransform);
0583     bool useFlakeOptimization = scale < 1.0 &&
0584         !KisTransformUtils::thumbnailTooSmall(resultThumbTransform, q->originalImage().rect());
0585 
0586     QVector<QPointF> thumbOrigPoints(currentArgs.numPoints());
0587     QVector<QPointF> thumbTransfPoints(currentArgs.numPoints());
0588 
0589     for (int i = 0; i < currentArgs.numPoints(); ++i) {
0590         thumbOrigPoints[i] = imageToThumb(currentArgs.origPoints()[i], useFlakeOptimization);
0591         thumbTransfPoints[i] = imageToThumb(currentArgs.transfPoints()[i], useFlakeOptimization);
0592     }
0593 
0594     paintingOffset = transaction.originalTopLeft();
0595 
0596     if (!q->originalImage().isNull() && !currentArgs.isEditingTransformPoints()) {
0597         QPointF origTLInFlake = imageToThumb(transaction.originalTopLeft(), useFlakeOptimization);
0598 
0599         if (useFlakeOptimization) {
0600             transformedImage = q->originalImage().transformed(resultThumbTransform);
0601             paintingTransform = QTransform();
0602         } else {
0603             transformedImage = q->originalImage();
0604             paintingTransform = resultThumbTransform;
0605 
0606         }
0607 
0608         transformedImage = q->calculateTransformedImage(currentArgs,
0609                                                         transformedImage,
0610                                                         thumbOrigPoints,
0611                                                         thumbTransfPoints,
0612                                                         origTLInFlake,
0613                                                         &paintingOffset);
0614     } else {
0615         transformedImage = q->originalImage();
0616         paintingOffset = imageToThumb(transaction.originalTopLeft(), false);
0617         paintingTransform = resultThumbTransform;
0618     }
0619 
0620     handlesTransform = scaleTransform;
0621     emit q->requestCanvasUpdate();
0622     emit q->requestImageRecalculation();
0623 }
0624 
0625 QImage KisWarpTransformStrategy::calculateTransformedImage(ToolTransformArgs &currentArgs,
0626                                                            const QImage &srcImage,
0627                                                            const QVector<QPointF> &origPoints,
0628                                                            const QVector<QPointF> &transfPoints,
0629                                                            const QPointF &srcOffset,
0630                                                            QPointF *dstOffset)
0631 {
0632     return KisWarpTransformWorker::transformQImage(
0633         currentArgs.warpType(),
0634         origPoints, transfPoints,
0635         currentArgs.alpha(),
0636         srcImage,
0637         srcOffset, dstOffset);
0638 }
0639 
0640 #include "moc_kis_warp_transform_strategy.cpp"