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