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
26namespace doc {
27 class Image;
28 class Mask;
29 class Sprite;
30}
31
32namespace 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 stampExtraCelImage();
137 void onPivotChange();
138 void onRotationAlgorithmChange();
139 void redrawExtraImage(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 m_extraCel;
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