File indexing completed on 2024-06-23 04:28:29
0001 /* 0002 * kis_tool_transform.h - part of Krita 0003 * 0004 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org> 0005 * SPDX-FileCopyrightText: 2005 C. Boemann <cbo@boemann.dk> 0006 * SPDX-FileCopyrightText: 2010 Marc Pegon <pe.marc@free.fr> 0007 * 0008 * SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #ifndef KIS_TOOL_TRANSFORM_H_ 0012 #define KIS_TOOL_TRANSFORM_H_ 0013 0014 #include <kis_icon.h> 0015 0016 #include <QPoint> 0017 #include <QPointF> 0018 #include <QVector2D> 0019 #include <QVector3D> 0020 #include <QButtonGroup> 0021 #include <QPointer> 0022 0023 #include <QKeySequence> 0024 0025 #include <KoToolFactoryBase.h> 0026 0027 #include <kis_shape_selection.h> 0028 #include <kis_undo_adapter.h> 0029 #include <kis_types.h> 0030 #include <flake/kis_node_shape.h> 0031 #include <kis_tool.h> 0032 #include <kis_canvas2.h> 0033 #include <kis_action.h> 0034 0035 #include <KisToolPaintFactoryBase.h> 0036 0037 0038 #include "tool_transform_args.h" 0039 #include "KisToolChangesTracker.h" 0040 #include "kis_tool_transform_config_widget.h" 0041 #include "transform_transaction_properties.h" 0042 #include "kis_signal_auto_connection.h" 0043 0044 #include "strokes/inplace_transform_stroke_strategy.h" 0045 0046 class QTouchEvent; 0047 class KisTransformStrategyBase; 0048 class KisWarpTransformStrategy; 0049 class KisCageTransformStrategy; 0050 class KisLiquifyTransformStrategy; 0051 class KisFreeTransformStrategy; 0052 class KisPerspectiveTransformStrategy; 0053 class KisMeshTransformStrategy; 0054 0055 0056 /** 0057 * Transform tool 0058 * This tool offers several modes. 0059 * - Free Transform mode allows the user to translate, scale, shear, rotate and 0060 * apply a perspective transformation to a selection or the whole canvas. 0061 * - Warp mode allows the user to warp the selection of the canvas by grabbing 0062 * and moving control points placed on the image. The user can either work 0063 * with default control points, like a grid whose density can be modified, or 0064 * place the control points manually. The modifications made on the selected 0065 * pixels are applied only when the user clicks the Apply button : the 0066 * semi-transparent image displayed until the user click that button is only a 0067 * preview. 0068 * - Cage transform is similar to warp transform with control points exactly 0069 * placed on the outer boundary. The user draws a boundary polygon, the 0070 * vertices of which become control points. 0071 * - Perspective transform applies a two-point perspective transformation. The 0072 * user can manipulate the corners of the selection. If the vanishing points 0073 * of the resulting quadrilateral are on screen, the user can manipulate those 0074 * as well. 0075 * - Liquify transform transforms the selection by painting motions, as if the 0076 * user was finger painting. 0077 */ 0078 class KisToolTransform : public KisTool 0079 { 0080 0081 Q_OBJECT 0082 0083 Q_PROPERTY(TransformToolMode transformMode READ transformMode WRITE setTransformMode NOTIFY transformModeChanged) 0084 0085 Q_PROPERTY(double translateX READ translateX WRITE setTranslateX NOTIFY freeTransformChanged) 0086 Q_PROPERTY(double translateY READ translateY WRITE setTranslateY NOTIFY freeTransformChanged) 0087 0088 Q_PROPERTY(double rotateX READ rotateX WRITE setRotateX NOTIFY freeTransformChanged) 0089 Q_PROPERTY(double rotateY READ rotateY WRITE setRotateY NOTIFY freeTransformChanged) 0090 Q_PROPERTY(double rotateZ READ rotateZ WRITE setRotateZ NOTIFY freeTransformChanged) 0091 0092 Q_PROPERTY(double scaleX READ scaleX WRITE setScaleX NOTIFY freeTransformChanged) 0093 Q_PROPERTY(double scaleY READ scaleY WRITE setScaleY NOTIFY freeTransformChanged) 0094 0095 Q_PROPERTY(double shearX READ shearX WRITE setShearX NOTIFY freeTransformChanged) 0096 Q_PROPERTY(double shearY READ shearY WRITE setShearY NOTIFY freeTransformChanged) 0097 0098 Q_PROPERTY(WarpType warpType READ warpType WRITE setWarpType NOTIFY warpTransformChanged) 0099 Q_PROPERTY(double warpFlexibility READ warpFlexibility WRITE setWarpFlexibility NOTIFY warpTransformChanged) 0100 Q_PROPERTY(int warpPointDensity READ warpPointDensity WRITE setWarpPointDensity NOTIFY warpTransformChanged) 0101 0102 0103 0104 public: 0105 enum TransformToolMode { 0106 FreeTransformMode, 0107 WarpTransformMode, 0108 CageTransformMode, 0109 LiquifyTransformMode, 0110 PerspectiveTransformMode, 0111 MeshTransformMode 0112 }; 0113 Q_ENUMS(TransformToolMode) 0114 0115 enum WarpType { 0116 RigidWarpType, 0117 AffineWarpType, 0118 SimilitudeWarpType 0119 }; 0120 Q_ENUMS(WarpType) 0121 0122 KisToolTransform(KoCanvasBase * canvas); 0123 ~KisToolTransform() override; 0124 0125 /** 0126 * @brief wantsAutoScroll 0127 * reimplemented from KoToolBase 0128 * there's an issue where autoscrolling with this tool never makes the 0129 * stroke end, so we return false here so that users don't get stuck with 0130 * the tool. See bug 362659 0131 * @return false 0132 */ 0133 bool wantsAutoScroll() const override { 0134 return false; 0135 } 0136 0137 QWidget* createOptionWidget() override; 0138 0139 void mousePressEvent(KoPointerEvent *e) override; 0140 void mouseMoveEvent(KoPointerEvent *e) override; 0141 void mouseReleaseEvent(KoPointerEvent *e) override; 0142 void beginActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action); 0143 void continueActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action); 0144 void endActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action); 0145 QMenu* popupActionsMenu() override; 0146 0147 void activatePrimaryAction() override; 0148 void deactivatePrimaryAction() override; 0149 void beginPrimaryAction(KoPointerEvent *event) override; 0150 void continuePrimaryAction(KoPointerEvent *event) override; 0151 void endPrimaryAction(KoPointerEvent *event) override; 0152 0153 void activateAlternateAction(AlternateAction action) override; 0154 void deactivateAlternateAction(AlternateAction action) override; 0155 void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override; 0156 void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override; 0157 void endAlternateAction(KoPointerEvent *event, AlternateAction action) override; 0158 0159 void paint(QPainter& gc, const KoViewConverter &converter) override; 0160 0161 void newActivationWithExternalSource(KisPaintDeviceSP externalSource) override; 0162 0163 TransformToolMode transformMode() const; 0164 0165 double translateX() const; 0166 double translateY() const; 0167 0168 double rotateX() const; 0169 double rotateY() const; 0170 double rotateZ() const; 0171 0172 double scaleX() const; 0173 double scaleY() const; 0174 0175 double shearX() const; 0176 double shearY() const; 0177 0178 WarpType warpType() const; 0179 double warpFlexibility() const; 0180 int warpPointDensity() const; 0181 0182 public Q_SLOTS: 0183 void activate(const QSet<KoShape*> &shapes) override; 0184 void deactivate() override; 0185 // Applies the current transformation to the original paint device and commits it to the undo stack 0186 void applyTransform(); 0187 0188 void requestImageRecalculation(); 0189 0190 void setTransformMode( KisToolTransform::TransformToolMode newMode ); 0191 0192 void setTranslateX(double translateX); 0193 void setTranslateY(double translateY); 0194 0195 void setRotateX(double rotation); 0196 void setRotateY(double rotation); 0197 void setRotateZ(double rotation); 0198 0199 void setScaleX(double scaleX); 0200 void setScaleY(double scaleY); 0201 0202 void setShearX(double shearX); 0203 void setShearY(double shearY); 0204 0205 void setWarpType(WarpType type); 0206 void setWarpFlexibility(double flexibility); 0207 void setWarpPointDensity(int density); 0208 0209 protected Q_SLOTS: 0210 void resetCursorStyle() override; 0211 void slotGlobalConfigChanged(); 0212 0213 Q_SIGNALS: 0214 void transformModeChanged(); 0215 void freeTransformChanged(); 0216 void warpTransformChanged(); 0217 0218 public Q_SLOTS: 0219 void requestUndoDuringStroke() override; 0220 void requestRedoDuringStroke() override; 0221 void requestStrokeEnd() override; 0222 void requestStrokeCancellation() override; 0223 void canvasUpdateRequested(); 0224 void cursorOutlineUpdateRequested(const QPointF &imagePos); 0225 0226 // Update the widget according to m_currentArgs 0227 void updateOptionWidget(); 0228 0229 void resetRotationCenterButtonsRequested(); 0230 void imageTooBigRequested(bool value); 0231 0232 private: 0233 QList<KisNodeSP> fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool isExternalSourcePresent); 0234 QScopedPointer<QMenu> m_contextMenu; 0235 0236 void startStroke(ToolTransformArgs::TransformMode mode, bool forceReset); 0237 void endStroke(); 0238 void cancelStroke(); 0239 0240 private: 0241 void outlineChanged(); 0242 // Sets the cursor according to mouse position (doesn't take shearing into account well yet) 0243 void setFunctionalCursor(); 0244 // Sets m_function according to mouse position and modifier 0245 void setTransformFunction(QPointF mousePos, Qt::KeyboardModifiers modifiers); 0246 0247 void commitChanges(); 0248 0249 void initTransformMode(ToolTransformArgs::TransformMode mode); 0250 void initGuiAfterTransformMode(); 0251 0252 void initThumbnailImage(KisPaintDeviceSP previewDevice); 0253 void updateApplyResetAvailability(); 0254 0255 private: 0256 ToolTransformArgs m_currentArgs; 0257 0258 // Set by newActivationWithExternalSource before starting a new stroke. 0259 // The source pixels for the next transform will be read from this device. 0260 KisPaintDeviceSP m_externalSourceForNextActivation; 0261 0262 bool m_actuallyMoveWhileSelected {false}; // true <=> selection has been moved while clicked 0263 0264 KisPaintDeviceSP m_selectedPortionCache; 0265 KisStrokeId m_strokeId; 0266 void *m_strokeStrategyCookie {0}; 0267 bool m_currentlyUsingOverlayPreviewStyle {false}; 0268 bool m_preferOverlayPreviewStyle {false}; 0269 bool m_forceLodMode {false}; 0270 0271 0272 QPainterPath m_selectionPath; // original (unscaled) selection outline, used for painting decorations 0273 0274 KisToolTransformConfigWidget *m_optionsWidget {0}; 0275 QPointer<KisCanvas2> m_canvas; 0276 0277 TransformTransactionProperties m_transaction; 0278 KisToolChangesTracker m_changesTracker; 0279 KisSignalAutoConnectionsStore m_actionConnections; 0280 0281 0282 /// actions for the context click menu 0283 KisAction* warpAction {0}; 0284 KisAction* meshAction {0}; 0285 KisAction* liquifyAction {0}; 0286 KisAction* cageAction {0}; 0287 KisAction* freeTransformAction {0}; 0288 KisAction* perspectiveAction {0}; 0289 KisAction* applyTransformation {0}; 0290 KisAction* resetTransformation {0}; 0291 0292 // a few extra context click options if free transform is active 0293 KisAction* mirrorHorizontalAction {0}; 0294 KisAction* mirrorVerticalAction {0}; 0295 KisAction* rotateNinetyCWAction {0}; 0296 KisAction* rotateNinetyCCWAction {0}; 0297 0298 0299 0300 0301 /** 0302 * This artificial rect is used to store the image to flake 0303 * transformation. We check against this rect to get to know 0304 * whether zoom has changed. 0305 */ 0306 QRectF m_refRect; 0307 0308 QScopedPointer<KisWarpTransformStrategy> m_warpStrategy; 0309 QScopedPointer<KisCageTransformStrategy> m_cageStrategy; 0310 QScopedPointer<KisLiquifyTransformStrategy> m_liquifyStrategy; 0311 QScopedPointer<KisMeshTransformStrategy> m_meshStrategy; 0312 QScopedPointer<KisFreeTransformStrategy> m_freeStrategy; 0313 QScopedPointer<KisPerspectiveTransformStrategy> m_perspectiveStrategy; 0314 KisTransformStrategyBase* currentStrategy() const; 0315 0316 QPainterPath m_cursorOutline; 0317 0318 KisAsynchronousStrokeUpdateHelper m_asyncUpdateHelper; 0319 0320 private Q_SLOTS: 0321 void slotTrackerChangedConfig(KisToolChangesTrackerDataSP status); 0322 void slotUiChangedConfig(bool needsPreviewRecalculation); 0323 void slotApplyTransform(); 0324 void slotResetTransform(ToolTransformArgs::TransformMode mode); 0325 void slotCancelTransform(); 0326 void slotRestartTransform(); 0327 void slotRestartAndContinueTransform(); 0328 void slotEditingFinished(); 0329 0330 void slotMoveDiscreteUp(); 0331 void slotMoveDiscreteUpMore(); 0332 void slotMoveDiscreteDown(); 0333 void slotMoveDiscreteDownMore(); 0334 void slotMoveDiscreteLeft(); 0335 void slotMoveDiscreteLeftMore(); 0336 void slotMoveDiscreteRight(); 0337 void slotMoveDiscreteRightMore(); 0338 0339 void slotTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *strokeStrategyCookie); 0340 void slotPreviewDeviceGenerated(KisPaintDeviceSP device); 0341 0342 // context menu options for updating the transform type 0343 // this is to help with discoverability since come people can't find the tool options 0344 void slotUpdateToWarpType(); 0345 void slotUpdateToPerspectiveType(); 0346 void slotUpdateToFreeTransformType(); 0347 void slotUpdateToLiquifyType(); 0348 void slotUpdateToMeshType(); 0349 void slotUpdateToCageType(); 0350 }; 0351 0352 class KisToolTransformFactory : public KisToolPaintFactoryBase 0353 { 0354 public: 0355 0356 KisToolTransformFactory() 0357 : KisToolPaintFactoryBase("KisToolTransform") { 0358 setToolTip(i18n("Transform a layer or a selection")); 0359 setSection(ToolBoxSection::Transform); 0360 setIconName(koIconNameCStr("krita_tool_transform")); 0361 setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); 0362 setPriority(2); 0363 setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); 0364 } 0365 0366 ~KisToolTransformFactory() override {} 0367 0368 KoToolBase * createTool(KoCanvasBase *canvas) override { 0369 return new KisToolTransform(canvas); 0370 } 0371 0372 QList<QAction *> createActionsImpl() override; 0373 }; 0374 0375 0376 0377 #endif // KIS_TOOL_TRANSFORM_H_ 0378