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 | |