// Aseprite // Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. #ifndef APP_DOC_H_INCLUDED #define APP_DOC_H_INCLUDED #pragma once #include "app/doc_observer.h" #include "app/extra_cel.h" #include "app/file/format_options.h" #include "app/transformation.h" #include "base/disable_copying.h" #include "base/rw_lock.h" #include "doc/blend_mode.h" #include "doc/color.h" #include "doc/document.h" #include "doc/frame.h" #include "doc/mask_boundaries.h" #include "doc/pixel_format.h" #include "gfx/rect.h" #include "obs/observable.h" #include "os/color_space.h" #include #include #include namespace doc { class Cel; class Layer; class LayerTilemap; class Mask; class Sprite; class Tileset; } namespace gfx { class Region; } namespace app { class Context; class DocApi; class DocUndo; class Transaction; using namespace doc; enum DuplicateType { DuplicateExactCopy, DuplicateWithFlattenLayers, }; // An application document. It is the class used to contain one file // opened and being edited by the user (a sprite). class Doc : public doc::Document, public obs::observable { enum Flags { kAssociatedToFile = 1, // This sprite is associated to a file in the file-system kMaskVisible = 2, // The mask wasn't hidden by the user kInhibitBackup = 4, // Inhibit the backup process kFullyBackedUp = 8, // Full backup was done kReadOnly = 16,// This document is read-only }; public: using LockResult = base::RWLock::LockResult; Doc(Sprite* sprite); ~Doc(); Context* context() const { return m_ctx; } void setContext(Context* ctx); // Lock/unlock API (RWLock wrapper) bool canWriteLockFromRead() const; LockResult readLock(int timeout); LockResult writeLock(int timeout); LockResult upgradeToWrite(int timeout); void downgradeToRead(LockResult lockResult); void unlock(LockResult lockResult); bool weakLock(std::atomic* weak_lock_flag); void weakUnlock(); // Sets active/running transaction. void setTransaction(Transaction* transaction); Transaction* transaction() { return m_transaction; } // Returns a high-level API: observable and undoable methods. DocApi getApi(Transaction& transaction); ////////////////////////////////////////////////////////////////////// // Main properties const DocUndo* undoHistory() const { return m_undo.get(); } DocUndo* undoHistory() { return m_undo.get(); } bool isUndoing() const; color_t bgColor() const; color_t bgColor(Layer* layer) const; os::ColorSpaceRef osColorSpace() const { return m_osColorSpace; } ////////////////////////////////////////////////////////////////////// // Modifications with notifications // Use this function to change the layer visibility and notify all // DocObservers about this change (e.g. so the Editor can be // invalidated/redrawn, MovingPixelsState can drop pixels, etc.) void setLayerVisibilityWithNotifications(Layer* layer, const bool visible); ////////////////////////////////////////////////////////////////////// // Notifications void notifyGeneralUpdate(); void notifyColorSpaceChanged(); void notifyPaletteChanged(); void notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region, frame_t frame); void notifyExposeSpritePixels(Sprite* sprite, const gfx::Region& region); void notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer); void notifyBeforeLayerVisibilityChange(Layer* layer, bool newState); void notifyAfterLayerVisibilityChange(Layer* layer); void notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame); void notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame); void notifySelectionChanged(); void notifySelectionBoundariesChanged(); void notifyTilesetChanged(Tileset* tileset); void notifyLayerGroupCollapseChange(Layer* layer); void notifyAfterAddTile(LayerTilemap* layer, frame_t frame, tile_index ti); ////////////////////////////////////////////////////////////////////// // File related properties bool isModified() const; bool isAssociatedToFile() const; void markAsSaved(); // You can use this to indicate that we've destroyed (or we cannot // trust) the file associated with the document (e.g. when we // cancel a Save operation in the middle). So it's impossible to // back to the saved state using the UndoHistory. void impossibleToBackToSavedState(); // Returns true if it does make sense to create a backup in this // document. For example, it doesn't make sense to create a backup // for an unmodified document. bool needsBackup() const; // Can be used to avoid creating a backup when the file is in a // unusual temporary state (e.g. when the file is resized to be // exported with other size) bool inhibitBackup() const; void setInhibitBackup(const bool inhibitBackup); void markAsBackedUp(); bool isFullyBackedUp() const; // TODO This read-only flag might be confusing because it // indicates that the file was loaded from an incompatible // version (future unknown feature) and it's preferable to // mark the sprite as read-only to avoid overwriting unknown // data. If in the future we want to add the possibility to // mark a regular file as read-only, this flag'll need a new // name. void markAsReadOnly(); bool isReadOnly() const; void removeReadOnlyMark(); ////////////////////////////////////////////////////////////////////// // Loaded options from file void setFormatOptions(const FormatOptionsPtr& format_options); FormatOptionsPtr formatOptions() const { return m_format_options; } ////////////////////////////////////////////////////////////////////// // Boundaries void destroyMaskBoundaries(); void generateMaskBoundaries(const Mask* mask = nullptr); const MaskBoundaries& maskBoundaries() const { return m_maskBoundaries; } MaskBoundaries& maskBoundaries() { return m_maskBoundaries; } bool hasMaskBoundaries() const { return !m_maskBoundaries.isEmpty(); } ////////////////////////////////////////////////////////////////////// // Extra Cel (it is used to draw pen preview, pixels in movement, etc.) ExtraCelRef extraCel() const { return m_extraCel; } void setExtraCel(const ExtraCelRef& extraCel) { m_extraCel = extraCel; } ////////////////////////////////////////////////////////////////////// // Mask // Returns the current mask, it can be empty. The mask could be not // empty but hidden to the user if the setMaskVisible(false) was // used called before. Mask* mask() const { return m_mask.get(); } // Sets the current mask. The new mask will be visible by default, // so you don't need to call setMaskVisible(true). void setMask(const Mask* mask); // Returns true only when the mask is not empty, and was not yet // hidden using setMaskVisible (e.g. when the user "deselect the // mask"). bool isMaskVisible() const; // Changes the visibility state of the mask (it is useful only if // the getMask() is not empty and the user can see that the mask is // being hidden and shown to him). void setMaskVisible(bool visible); ////////////////////////////////////////////////////////////////////// // Transformation Transformation getTransformation() const; void setTransformation(const Transformation& transform); void resetTransformation(); ////////////////////////////////////////////////////////////////////// // Last point used to draw straight lines using freehand tools + Shift key // (EditorCustomizationDelegate::isStraightLineFromLastPoint() modifier) static gfx::Point NoLastDrawingPoint(); gfx::Point lastDrawingPoint() const { return m_lastDrawingPoint; } void setLastDrawingPoint(const gfx::Point& pos) { m_lastDrawingPoint = pos; } ////////////////////////////////////////////////////////////////////// // Copying void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const; Doc* duplicate(DuplicateType type) const; void close(); protected: void onFileNameChange() override; virtual void onContextChanged(); private: void removeFromContext(); void updateOSColorSpace(bool appWideSignal); // The document is in the collection of documents of this context. Context* m_ctx; // Internal states of the document. int m_flags; // Read-Write locks. base::RWLock m_rwLock; // Undo and redo information about the document. std::unique_ptr m_undo; // Current transaction for this document (when this is commit(), a // new undo command is added to m_undo). Transaction* m_transaction; // Selected mask region boundaries doc::MaskBoundaries m_maskBoundaries; // Data to save the file in the same format that it was loaded FormatOptionsPtr m_format_options; // Extra cel used to draw extra stuff (e.g. editor's pen preview, pixels in movement, etc.) ExtraCelRef m_extraCel; // Current mask. std::unique_ptr m_mask; // Current transformation. Transformation m_transformation; gfx::Point m_lastDrawingPoint; // Last used color space to render a sprite. os::ColorSpaceRef m_osColorSpace; DISABLE_COPYING(Doc); }; } // namespace app #endif