| 1 | // Aseprite |
| 2 | // Copyright (C) 2019-2021 Igara Studio S.A. |
| 3 | // Copyright (C) 2001-2018 David Capello |
| 4 | // |
| 5 | // This program is distributed under the terms of |
| 6 | // the End-User License Agreement for Aseprite. |
| 7 | |
| 8 | #ifndef APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED |
| 9 | #define APP_UI_EDITOR_PIXELS_MOVEMENT_H_INCLUDED |
| 10 | #pragma once |
| 11 | |
| 12 | #include "app/context_access.h" |
| 13 | #include "app/extra_cel.h" |
| 14 | #include "app/site.h" |
| 15 | #include "app/transformation.h" |
| 16 | #include "app/tx.h" |
| 17 | #include "app/ui/editor/handle_type.h" |
| 18 | #include "doc/algorithm/flip_type.h" |
| 19 | #include "doc/frame.h" |
| 20 | #include "doc/image_ref.h" |
| 21 | #include "gfx/size.h" |
| 22 | #include "obs/connection.h" |
| 23 | |
| 24 | #include <memory> |
| 25 | |
| 26 | namespace doc { |
| 27 | class Image; |
| 28 | class Mask; |
| 29 | class Sprite; |
| 30 | } |
| 31 | |
| 32 | namespace app { |
| 33 | class Doc; |
| 34 | |
| 35 | namespace cmd { |
| 36 | class SetMask; |
| 37 | } |
| 38 | |
| 39 | class PixelsMovementDelegate { |
| 40 | public: |
| 41 | virtual ~PixelsMovementDelegate() { } |
| 42 | virtual void onPivotChange() = 0; |
| 43 | }; |
| 44 | |
| 45 | // Helper class to move pixels interactively and control undo history |
| 46 | // correctly. The extra cel of the sprite is temporally used to show |
| 47 | // feedback, drag, and drop the specified image in the constructor |
| 48 | // (which generally would be the selected region or the clipboard |
| 49 | // content). |
| 50 | class PixelsMovement { |
| 51 | public: |
| 52 | enum MoveModifier { |
| 53 | NormalMovement = 1, |
| 54 | SnapToGridMovement = 2, |
| 55 | AngleSnapMovement = 4, |
| 56 | MaintainAspectRatioMovement = 8, |
| 57 | LockAxisMovement = 16, |
| 58 | ScaleFromPivot = 32, |
| 59 | FineControl = 64, |
| 60 | }; |
| 61 | |
| 62 | enum CommitChangesOption { |
| 63 | DontCommitChanges, |
| 64 | CommitChanges, |
| 65 | }; |
| 66 | |
| 67 | enum KeepMaskOption { |
| 68 | DontKeepMask, |
| 69 | KeepMask, |
| 70 | }; |
| 71 | |
| 72 | PixelsMovement(Context* context, |
| 73 | Site site, |
| 74 | const Image* moveThis, |
| 75 | const Mask* mask, |
| 76 | const char* operationName); |
| 77 | |
| 78 | HandleType handle() const { return m_handle; } |
| 79 | bool canHandleFrameChange() const { return m_canHandleFrameChange; } |
| 80 | |
| 81 | void setDelegate(PixelsMovementDelegate* delegate); |
| 82 | void setFastMode(const bool fastMode); |
| 83 | |
| 84 | void trim(); |
| 85 | void cutMask(); |
| 86 | void copyMask(); |
| 87 | void catchImage(const gfx::PointF& pos, HandleType handle); |
| 88 | void catchImageAgain(const gfx::PointF& pos, HandleType handle); |
| 89 | |
| 90 | // Moves the image to the new position (relative to the start |
| 91 | // position given in the ctor). |
| 92 | void moveImage(const gfx::PointF& pos, MoveModifier moveModifier); |
| 93 | |
| 94 | // Returns a copy of the current image being dragged with the |
| 95 | // current transformation. |
| 96 | void getDraggedImageCopy(std::unique_ptr<Image>& outputImage, |
| 97 | std::unique_ptr<Mask>& outputMask); |
| 98 | |
| 99 | // Copies the image being dragged in the current position. |
| 100 | void stampImage(); |
| 101 | |
| 102 | void dropImageTemporarily(); |
| 103 | void dropImage(); |
| 104 | void discardImage(const CommitChangesOption commit = CommitChanges, |
| 105 | const KeepMaskOption keepMask = DontKeepMask); |
| 106 | bool isDragging() const; |
| 107 | |
| 108 | gfx::Rect getImageBounds(); |
| 109 | gfx::Size getInitialImageSize() const; |
| 110 | |
| 111 | void setMaskColor(bool opaque, color_t mask_color); |
| 112 | |
| 113 | // Flips the image and mask in the given direction in "flipType". |
| 114 | // Flip Horizontally/Vertically commands are replaced calling this |
| 115 | // function, so they work more as the user would expect (flip the |
| 116 | // current selection instead of dropping and flipping it). |
| 117 | void flipImage(doc::algorithm::FlipType flipType); |
| 118 | |
| 119 | // Rotates the image and the mask the given angle. It's used to |
| 120 | // simulate RotateCommand when we're inside MovingPixelsState. |
| 121 | void rotate(double angle); |
| 122 | |
| 123 | void shift(int dx, int dy); |
| 124 | |
| 125 | // Navigate frames |
| 126 | bool gotoFrame(const doc::frame_t deltaFrame); |
| 127 | |
| 128 | const Transformation& getTransformation() const { return m_currentData; } |
| 129 | void setTransformation(const Transformation& t); |
| 130 | |
| 131 | private: |
| 132 | void setTransformationBase(const Transformation& t); |
| 133 | void adjustPivot(); |
| 134 | bool editMultipleCels() const; |
| 135 | void stampImage(bool finalStamp); |
| 136 | void (); |
| 137 | void onPivotChange(); |
| 138 | void onRotationAlgorithmChange(); |
| 139 | void (Transformation* transformation = nullptr); |
| 140 | void redrawCurrentMask(); |
| 141 | void drawImage( |
| 142 | const Transformation& transformation, |
| 143 | doc::Image* dst, const gfx::PointF& pt, |
| 144 | const bool renderOriginalLayer); |
| 145 | void drawMask(doc::Mask* dst, bool shrink); |
| 146 | void drawParallelogram( |
| 147 | const Transformation& transformation, |
| 148 | doc::Image* dst, const doc::Image* src, const doc::Mask* mask, |
| 149 | const Transformation::Corners& corners, |
| 150 | const gfx::PointF& leftTop); |
| 151 | void drawTransformedTilemap( |
| 152 | const Transformation& transformation, |
| 153 | doc::Image* dst, const doc::Image* src, const doc::Mask* mask); |
| 154 | void updateDocumentMask(); |
| 155 | void hideDocumentMask(); |
| 156 | |
| 157 | void flipOriginalImage(const doc::algorithm::FlipType flipType); |
| 158 | void shiftOriginalImage(const int dx, const int dy, |
| 159 | const double angle); |
| 160 | CelList getEditableCels(); |
| 161 | void reproduceAllTransformationsWithInnerCmds(); |
| 162 | #if _DEBUG |
| 163 | void dumpInnerCmds(); |
| 164 | #endif |
| 165 | |
| 166 | PixelsMovementDelegate* m_delegate = nullptr; |
| 167 | const ContextReader m_reader; |
| 168 | Site m_site; |
| 169 | Doc* m_document; |
| 170 | Tx m_tx; |
| 171 | bool m_isDragging; |
| 172 | bool m_adjustPivot; |
| 173 | HandleType m_handle; |
| 174 | doc::ImageRef m_originalImage; |
| 175 | gfx::PointF m_catchPos; |
| 176 | Transformation m_initialData; |
| 177 | Transformation m_currentData; |
| 178 | std::unique_ptr<Mask> m_initialMask, m_initialMask0; |
| 179 | std::unique_ptr<Mask> m_currentMask; |
| 180 | bool m_opaque; |
| 181 | color_t m_maskColor; |
| 182 | obs::scoped_connection m_pivotVisConn; |
| 183 | obs::scoped_connection m_pivotPosConn; |
| 184 | obs::scoped_connection m_rotAlgoConn; |
| 185 | ExtraCelRef ; |
| 186 | bool m_canHandleFrameChange; |
| 187 | |
| 188 | // Fast mode is used to give a faster feedback to the user |
| 189 | // avoiding RotSprite on each mouse movement. |
| 190 | bool m_fastMode; |
| 191 | bool m_needsRotSpriteRedraw; |
| 192 | |
| 193 | // Commands used in the interaction with the transformed pixels. |
| 194 | // This is used to re-create the whole interaction on each |
| 195 | // modified cel when we are modifying multiples cels at the same |
| 196 | // time, or also to re-create it when we switch to another frame. |
| 197 | struct InnerCmd { |
| 198 | enum Type { None, Clear, Flip, Shift, Stamp } type; |
| 199 | union { |
| 200 | struct { |
| 201 | doc::algorithm::FlipType type; |
| 202 | } flip; |
| 203 | struct { |
| 204 | int dx, dy; |
| 205 | double angle; |
| 206 | } shift; |
| 207 | struct { |
| 208 | Transformation* transformation; |
| 209 | } stamp; |
| 210 | } data; |
| 211 | InnerCmd() : type(None) { } |
| 212 | InnerCmd(InnerCmd&&); |
| 213 | ~InnerCmd(); |
| 214 | InnerCmd(const InnerCmd&) = delete; |
| 215 | InnerCmd& operator=(const InnerCmd&) = delete; |
| 216 | static InnerCmd MakeClear(); |
| 217 | static InnerCmd MakeFlip(const doc::algorithm::FlipType flipType); |
| 218 | static InnerCmd MakeShift(const int dx, const int dy, const double angle); |
| 219 | static InnerCmd MakeStamp(const Transformation& t); |
| 220 | }; |
| 221 | std::vector<InnerCmd> m_innerCmds; |
| 222 | }; |
| 223 | |
| 224 | inline PixelsMovement::MoveModifier& operator|=(PixelsMovement::MoveModifier& a, |
| 225 | const PixelsMovement::MoveModifier& b) { |
| 226 | a = static_cast<PixelsMovement::MoveModifier>(a | b); |
| 227 | return a; |
| 228 | } |
| 229 | |
| 230 | typedef std::shared_ptr<PixelsMovement> PixelsMovementPtr; |
| 231 | |
| 232 | } // namespace app |
| 233 | |
| 234 | #endif |
| 235 | |