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 ¤tArgs; 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 ¤tArgs, 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(¢er, 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(¢er); 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(¢er); 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(¢er); 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 ¤tArgs, 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"