mirror of
https://github.com/aseprite/aseprite.git
synced 2024-12-29 00:23:48 +00:00
e949a5401d
This fixes several problems in MovingPixelsState where hidden layers were transformed anyway when we switched the visibility of a layer in this state. Other fix was tried before in #3254 but we needed the onBefore/After layer visibility change notifications to make this work properly (i.e. drop pixels when the visiblity of a layer is changed). The only drawback at this moment is that changing the visibility of the non-active layer when we are transforming multiple cels/timeline range can be confused because we don't have #2144/#2865 implemented yet. This bug was originally reported here: https://community.aseprite.org/t/20621
300 lines
9.6 KiB
C++
300 lines
9.6 KiB
C++
// 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 <atomic>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
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<DocObserver> {
|
|
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<base::RWLock::WeakLock>* 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<DocUndo> 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<doc::Mask> 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
|