1 | // Aseprite |
2 | // Copyright (C) 2018-2020 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_DOC_H_INCLUDED |
9 | #define APP_DOC_H_INCLUDED |
10 | #pragma once |
11 | |
12 | #include "app/doc_observer.h" |
13 | #include "app/extra_cel.h" |
14 | #include "app/file/format_options.h" |
15 | #include "app/transformation.h" |
16 | #include "base/disable_copying.h" |
17 | #include "base/rw_lock.h" |
18 | #include "doc/blend_mode.h" |
19 | #include "doc/color.h" |
20 | #include "doc/document.h" |
21 | #include "doc/frame.h" |
22 | #include "doc/mask_boundaries.h" |
23 | #include "doc/pixel_format.h" |
24 | #include "gfx/rect.h" |
25 | #include "obs/observable.h" |
26 | #include "os/color_space.h" |
27 | |
28 | #include <atomic> |
29 | #include <memory> |
30 | #include <string> |
31 | |
32 | namespace doc { |
33 | class Cel; |
34 | class Layer; |
35 | class Mask; |
36 | class Sprite; |
37 | class Tileset; |
38 | } |
39 | |
40 | namespace gfx { |
41 | class Region; |
42 | } |
43 | |
44 | namespace app { |
45 | |
46 | class Context; |
47 | class DocApi; |
48 | class DocUndo; |
49 | class Transaction; |
50 | |
51 | using namespace doc; |
52 | |
53 | enum DuplicateType { |
54 | DuplicateExactCopy, |
55 | DuplicateWithFlattenLayers, |
56 | }; |
57 | |
58 | // An application document. It is the class used to contain one file |
59 | // opened and being edited by the user (a sprite). |
60 | class Doc : public doc::Document, |
61 | public obs::observable<DocObserver> { |
62 | enum Flags { |
63 | kAssociatedToFile = 1, // This sprite is associated to a file in the file-system |
64 | kMaskVisible = 2, // The mask wasn't hidden by the user |
65 | kInhibitBackup = 4, // Inhibit the backup process |
66 | kFullyBackedUp = 8, // Full backup was done |
67 | }; |
68 | public: |
69 | Doc(Sprite* sprite); |
70 | ~Doc(); |
71 | |
72 | Context* context() const { return m_ctx; } |
73 | void setContext(Context* ctx); |
74 | |
75 | // Lock/unlock API (RWLock wrapper) |
76 | bool canWriteLockFromRead() const; |
77 | bool readLock(int timeout); |
78 | bool writeLock(int timeout); |
79 | bool upgradeToWrite(int timeout); |
80 | void downgradeToRead(); |
81 | void unlock(); |
82 | |
83 | bool weakLock(std::atomic<base::RWLock::WeakLock>* weak_lock_flag); |
84 | void weakUnlock(); |
85 | |
86 | // Sets active/running transaction. |
87 | void setTransaction(Transaction* transaction); |
88 | Transaction* transaction() { return m_transaction; } |
89 | |
90 | // Returns a high-level API: observable and undoable methods. |
91 | DocApi getApi(Transaction& transaction); |
92 | |
93 | ////////////////////////////////////////////////////////////////////// |
94 | // Main properties |
95 | |
96 | const DocUndo* undoHistory() const { return m_undo.get(); } |
97 | DocUndo* undoHistory() { return m_undo.get(); } |
98 | |
99 | color_t bgColor() const; |
100 | color_t bgColor(Layer* layer) const; |
101 | |
102 | os::ColorSpaceRef osColorSpace() const { return m_osColorSpace; } |
103 | |
104 | ////////////////////////////////////////////////////////////////////// |
105 | // Notifications |
106 | |
107 | void notifyGeneralUpdate(); |
108 | void notifyColorSpaceChanged(); |
109 | void notifyPaletteChanged(); |
110 | void notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region, frame_t frame); |
111 | void notifyExposeSpritePixels(Sprite* sprite, const gfx::Region& region); |
112 | void notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer); |
113 | void notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame); |
114 | void notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame); |
115 | void notifySelectionChanged(); |
116 | void notifySelectionBoundariesChanged(); |
117 | void notifyTilesetChanged(Tileset* tileset); |
118 | |
119 | ////////////////////////////////////////////////////////////////////// |
120 | // File related properties |
121 | |
122 | bool isModified() const; |
123 | bool isAssociatedToFile() const; |
124 | void markAsSaved(); |
125 | |
126 | // You can use this to indicate that we've destroyed (or we cannot |
127 | // trust) the file associated with the document (e.g. when we |
128 | // cancel a Save operation in the middle). So it's impossible to |
129 | // back to the saved state using the UndoHistory. |
130 | void impossibleToBackToSavedState(); |
131 | |
132 | // Returns true if it does make sense to create a backup in this |
133 | // document. For example, it doesn't make sense to create a backup |
134 | // for an unmodified document. |
135 | bool needsBackup() const; |
136 | |
137 | // Can be used to avoid creating a backup when the file is in a |
138 | // unusual temporary state (e.g. when the file is resized to be |
139 | // exported with other size) |
140 | bool inhibitBackup() const; |
141 | void setInhibitBackup(const bool inhibitBackup); |
142 | |
143 | void markAsBackedUp(); |
144 | bool isFullyBackedUp() const; |
145 | |
146 | ////////////////////////////////////////////////////////////////////// |
147 | // Loaded options from file |
148 | |
149 | void setFormatOptions(const FormatOptionsPtr& format_options); |
150 | FormatOptionsPtr formatOptions() const { return m_format_options; } |
151 | |
152 | ////////////////////////////////////////////////////////////////////// |
153 | // Boundaries |
154 | |
155 | void destroyMaskBoundaries(); |
156 | void generateMaskBoundaries(const Mask* mask = nullptr); |
157 | |
158 | const MaskBoundaries& maskBoundaries() const { |
159 | return m_maskBoundaries; |
160 | } |
161 | |
162 | MaskBoundaries& maskBoundaries() { |
163 | return m_maskBoundaries; |
164 | } |
165 | |
166 | bool hasMaskBoundaries() const { |
167 | return !m_maskBoundaries.isEmpty(); |
168 | } |
169 | |
170 | ////////////////////////////////////////////////////////////////////// |
171 | // Extra Cel (it is used to draw pen preview, pixels in movement, etc.) |
172 | |
173 | ExtraCelRef () const { return m_extraCel; } |
174 | void (const ExtraCelRef& ) { m_extraCel = extraCel; } |
175 | |
176 | ////////////////////////////////////////////////////////////////////// |
177 | // Mask |
178 | |
179 | // Returns the current mask, it can be empty. The mask could be not |
180 | // empty but hidden to the user if the setMaskVisible(false) was |
181 | // used called before. |
182 | Mask* mask() const { return m_mask.get(); } |
183 | |
184 | // Sets the current mask. The new mask will be visible by default, |
185 | // so you don't need to call setMaskVisible(true). |
186 | void setMask(const Mask* mask); |
187 | |
188 | // Returns true only when the mask is not empty, and was not yet |
189 | // hidden using setMaskVisible (e.g. when the user "deselect the |
190 | // mask"). |
191 | bool isMaskVisible() const; |
192 | |
193 | // Changes the visibility state of the mask (it is useful only if |
194 | // the getMask() is not empty and the user can see that the mask is |
195 | // being hidden and shown to him). |
196 | void setMaskVisible(bool visible); |
197 | |
198 | ////////////////////////////////////////////////////////////////////// |
199 | // Transformation |
200 | |
201 | Transformation getTransformation() const; |
202 | void setTransformation(const Transformation& transform); |
203 | void resetTransformation(); |
204 | |
205 | ////////////////////////////////////////////////////////////////////// |
206 | // Last point used to draw straight lines using freehand tools + Shift key |
207 | // (EditorCustomizationDelegate::isStraightLineFromLastPoint() modifier) |
208 | |
209 | static gfx::Point NoLastDrawingPoint(); |
210 | gfx::Point lastDrawingPoint() const { return m_lastDrawingPoint; } |
211 | void setLastDrawingPoint(const gfx::Point& pos) { m_lastDrawingPoint = pos; } |
212 | |
213 | ////////////////////////////////////////////////////////////////////// |
214 | // Copying |
215 | |
216 | void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const; |
217 | Doc* duplicate(DuplicateType type) const; |
218 | |
219 | void close(); |
220 | |
221 | protected: |
222 | void onFileNameChange() override; |
223 | virtual void onContextChanged(); |
224 | |
225 | private: |
226 | void removeFromContext(); |
227 | void updateOSColorSpace(bool appWideSignal); |
228 | |
229 | // The document is in the collection of documents of this context. |
230 | Context* m_ctx; |
231 | |
232 | // Internal states of the document. |
233 | int m_flags; |
234 | |
235 | // Read-Write locks. |
236 | base::RWLock m_rwLock; |
237 | |
238 | // Undo and redo information about the document. |
239 | std::unique_ptr<DocUndo> m_undo; |
240 | |
241 | // Current transaction for this document (when this is commit(), a |
242 | // new undo command is added to m_undo). |
243 | Transaction* m_transaction; |
244 | |
245 | // Selected mask region boundaries |
246 | doc::MaskBoundaries m_maskBoundaries; |
247 | |
248 | // Data to save the file in the same format that it was loaded |
249 | FormatOptionsPtr m_format_options; |
250 | |
251 | // Extra cel used to draw extra stuff (e.g. editor's pen preview, pixels in movement, etc.) |
252 | ExtraCelRef ; |
253 | |
254 | // Current mask. |
255 | std::unique_ptr<doc::Mask> m_mask; |
256 | |
257 | // Current transformation. |
258 | Transformation m_transformation; |
259 | |
260 | gfx::Point m_lastDrawingPoint; |
261 | |
262 | // Last used color space to render a sprite. |
263 | os::ColorSpaceRef m_osColorSpace; |
264 | |
265 | DISABLE_COPYING(Doc); |
266 | }; |
267 | |
268 | } // namespace app |
269 | |
270 | #endif |
271 | |