mirror of https://github.com/aseprite/aseprite.git
Merge 147a2308c6
into 6c7544a132
This commit is contained in:
commit
5cbcb347b1
|
@ -906,6 +906,11 @@
|
|||
<param name="tilemap" value="true" />
|
||||
<param name="ask" value="true" />
|
||||
</item>
|
||||
<separator />
|
||||
<item command="NewLayer" text="@.layer_new_audio_layer">
|
||||
<param name="type" value="audio" />
|
||||
<param name="ask" value="true" />
|
||||
</item>
|
||||
</menu>
|
||||
<item command="RemoveLayer" text="@.layer_delete_layer" group="layer_remove" />
|
||||
<menu text="@.layer_convert_to">
|
||||
|
|
|
@ -365,6 +365,14 @@ NewLayer_Layer = Layer
|
|||
NewLayer_Group = Group
|
||||
NewLayer_ReferenceLayer = Reference Layer
|
||||
NewLayer_TilemapLayer = Tilemap
|
||||
NewLayer_FillLayer = Fill
|
||||
NewLayer_MaskLayer = Mask
|
||||
NewLayer_FxLayer = Fx
|
||||
NewLayer_TextLayer = Text
|
||||
NewLayer_VectorLayer = Vector
|
||||
NewLayer_AudioLayer = Audio
|
||||
NewLayer_SubspriteLayer = Subsprite
|
||||
NewLayer_HitboxLayer = Hitbox
|
||||
NewLayer_FromClipboard = {} from Clipboard
|
||||
NewLayer_ViaCopy = {} via Copy
|
||||
NewLayer_ViaCut = {} via Cut
|
||||
|
@ -1149,6 +1157,7 @@ layer_new_layer_via_copy = New Layer via &Copy
|
|||
layer_new_layer_via_cut = New Layer via Cu&t
|
||||
layer_new_reference_layer_from_file = New &Reference Layer from File
|
||||
layer_new_tilemap_layer = New Tilemap Layer
|
||||
layer_new_audio_layer = New Audio Layer
|
||||
layer_delete_layer = Delete Laye&r
|
||||
layer_convert_to = Conv&ert To...
|
||||
layer_convert_to_background = &Background
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!-- Aseprite -->
|
||||
<!-- Copyright (C) 2020 by Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2020-2025 by Igara Studio S.A. -->
|
||||
<!-- Copyright (C) 2001-2016 by David Capello -->
|
||||
<gui>
|
||||
<window id="layer_properties" text="@.title">
|
||||
|
@ -9,11 +9,11 @@
|
|||
<entry text="" id="name" magnet="true" maxsize="256" minwidth="64" cell_align="horizontal" />
|
||||
<button id="user_data" icon="icon_user_data" tooltip="@general.user_data" />
|
||||
|
||||
<label text="@.mode" for="mode" />
|
||||
<label id="mode_label" text="@.mode" for="mode" />
|
||||
<combobox id="mode" />
|
||||
<button id="tileset" icon="tiles" tooltip="@.tileset_tooltip" />
|
||||
|
||||
<label text="@.opacity" for="opacity" />
|
||||
<label id="opacity_label" text="@.opacity" for="opacity" />
|
||||
<opacityslider id="opacity" width="128" cell_align="horizontal" cell_hspan="2" />
|
||||
|
||||
<label id="uuid_label" text="@.uuid" visible="false" for="uuid" />
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# New Layer Type
|
||||
|
||||
This is a little hacking guide about how to add a new kind of layer to Aseprite:
|
||||
|
||||
1. The first step is to add a new [`doc::ObjectType` value](../src/doc/object_type.h)
|
||||
for the new kind of layer
|
||||
1. Create a new `LayerNAME` class derived from `Layer` or `LayerImage` (e.g. [`LayerAudio`](../src/doc/layer_audio.h))
|
||||
1. Add this new `LayerNAME` [in `Layer::Layer()`'s `ASSERT()`](../src/doc/layer.cpp)
|
||||
1. Expand [the `type` parameter in `NewLayerParams` for
|
||||
`NewLayerCommand`](../src/app/commands/cmd_new_layer.cpp) and
|
||||
`NewLayerCommand::Type` for `LayerNAME`
|
||||
1. Add the necessary code in `NewLayerCommand::onExecute()` to create
|
||||
this new kind of layer with a transaction
|
||||
1. Complete `NewLayerCommand::layerPrefix()` function (which is used
|
||||
in `NewLayerCommand::onGetFriendlyName()`) to get a proper text for
|
||||
this command when it creates this new kind of layer, you will need
|
||||
some [new `NewLayer_*Layer` string in
|
||||
`en.ini`](../data/strings/en.ini)
|
||||
1. Add a [new menu option in `gui.xml`](../data/gui.xml) in `<menu
|
||||
text="@.layer" id="layer_menu">` for a new *Layer > New* menu,
|
||||
you will need some [new `layer_new_*_layer` string in
|
||||
`en.ini`](../data/strings/en.ini) in the `[main_menu]` section
|
||||
1. Add the serialization of the layer specifics in
|
||||
[`write_layer`/`read_layer` functions](../src/doc/layer_io.cpp),
|
||||
this code is used for [undo/redo](../src/app/cmd/add_layer.cpp)
|
||||
1. Add the serialization for [data recovery](https://www.aseprite.org/docs/data-recovery/)
|
||||
purposes of the layer specifics in [Writer::writeLayerStructure()](../src/app/crash/write_document.cpp)
|
||||
and [Reader::readLayer()](../src/app/crash/read_document.cpp) functions
|
||||
1. To make `DuplicateLayerCommand` work, add the new `doc::ObjectType`
|
||||
in `app::DocApi::copyLayerWithSprite()` and `app::Doc::copyLayerContent()`
|
||||
|
||||
# Layer Properties
|
||||
|
||||
Customize the `LayerPropertiesCommand` dialog:
|
||||
|
||||
1. Hide widgets that don't make sense for this layer type
|
|
@ -315,6 +315,7 @@ target_sources(app-lib PRIVATE
|
|||
cmd/set_cel_bounds.cpp
|
||||
cmd/set_cel_data.cpp
|
||||
cmd/set_cel_frame.cpp
|
||||
cmd/set_cel_image.cpp
|
||||
cmd/set_cel_opacity.cpp
|
||||
cmd/set_cel_position.cpp
|
||||
cmd/set_cel_zindex.cpp
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -112,7 +112,7 @@ std::string convert_filter_to_layer_path_if_possible(const Sprite* sprite,
|
|||
}
|
||||
}
|
||||
if (layer->isGroup()) {
|
||||
for (auto child : static_cast<const LayerGroup*>(layer)->layers())
|
||||
for (Layer* child : layer->layers())
|
||||
layers.push(child);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -238,15 +238,14 @@ void PreviewCliDelegate::showLayersFilter(const CliOpenFile& cof)
|
|||
}
|
||||
}
|
||||
|
||||
void PreviewCliDelegate::showLayerVisibility(const doc::LayerGroup* group,
|
||||
const std::string& indent)
|
||||
void PreviewCliDelegate::showLayerVisibility(const doc::Layer* group, const std::string& indent)
|
||||
{
|
||||
for (auto layer : group->layers()) {
|
||||
if (!layer->isVisible())
|
||||
continue;
|
||||
std::cout << indent << "- " << layer->name() << "\n";
|
||||
if (layer->isGroup())
|
||||
showLayerVisibility(static_cast<const LayerGroup*>(layer), indent + " ");
|
||||
showLayerVisibility(layer, indent + " ");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -14,7 +14,7 @@
|
|||
#include <string>
|
||||
|
||||
namespace doc {
|
||||
class LayerGroup;
|
||||
class Layer;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
|
||||
private:
|
||||
void showLayersFilter(const CliOpenFile& cof);
|
||||
void showLayerVisibility(const doc::LayerGroup* group, const std::string& indent);
|
||||
void showLayerVisibility(const doc::Layer* group, const std::string& indent);
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2020-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -49,10 +49,12 @@ void AddCel::onUndo()
|
|||
ASSERT(cel);
|
||||
|
||||
// Save the CelData only if the cel isn't linked
|
||||
bool has_data = (cel->links() == 0);
|
||||
write8(m_stream, has_data ? 1 : 0);
|
||||
const bool has_data = (cel->links() == 0);
|
||||
const bool has_image = (cel->imageId() != NullId);
|
||||
write8(m_stream, (has_data ? 1 : 0) | (has_image ? 2 : 0));
|
||||
if (has_data) {
|
||||
write_image(m_stream, cel->image());
|
||||
if (has_image)
|
||||
write_image(m_stream, cel->image());
|
||||
write_celdata(m_stream, cel->data());
|
||||
}
|
||||
write_cel(m_stream, cel);
|
||||
|
@ -67,10 +69,14 @@ void AddCel::onRedo()
|
|||
ASSERT(layer);
|
||||
|
||||
SubObjectsFromSprite io(layer->sprite());
|
||||
bool has_data = (read8(m_stream) != 0);
|
||||
const uint8_t flags = read8(m_stream);
|
||||
const bool has_data = (flags & 1 ? true : false);
|
||||
const bool has_image = (flags & 2 ? true : false);
|
||||
if (has_data) {
|
||||
ImageRef image(read_image(m_stream));
|
||||
io.addImageRef(image);
|
||||
if (has_image) {
|
||||
ImageRef image(read_image(m_stream));
|
||||
io.addImageRef(image);
|
||||
}
|
||||
|
||||
CelDataRef celdata(read_celdata(m_stream, &io));
|
||||
io.addCelDataRef(celdata);
|
||||
|
@ -87,7 +93,7 @@ void AddCel::onRedo()
|
|||
|
||||
void AddCel::addCel(Layer* layer, Cel* cel)
|
||||
{
|
||||
static_cast<LayerImage*>(layer)->addCel(cel);
|
||||
layer->addCel(cel);
|
||||
layer->incrementVersion();
|
||||
|
||||
Doc* doc = static_cast<Doc*>(cel->document());
|
||||
|
@ -107,7 +113,7 @@ void AddCel::removeCel(Layer* layer, Cel* cel)
|
|||
ev.cel(cel);
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onBeforeRemoveCel, ev);
|
||||
|
||||
static_cast<LayerImage*>(layer)->removeCel(cel);
|
||||
layer->removeCel(cel);
|
||||
layer->incrementVersion();
|
||||
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onAfterRemoveCel, ev);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -64,7 +65,7 @@ void AddLayer::onRedo()
|
|||
|
||||
void AddLayer::addLayer(Layer* group, Layer* newLayer, Layer* afterThis)
|
||||
{
|
||||
static_cast<LayerGroup*>(group)->insertLayer(newLayer, afterThis);
|
||||
group->insertLayer(newLayer, afterThis);
|
||||
group->incrementVersion();
|
||||
group->sprite()->incrementVersion();
|
||||
|
||||
|
@ -83,7 +84,7 @@ void AddLayer::removeLayer(Layer* group, Layer* layer)
|
|||
ev.layer(layer);
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onBeforeRemoveLayer, ev);
|
||||
|
||||
static_cast<LayerGroup*>(group)->removeLayer(layer);
|
||||
group->removeLayer(layer);
|
||||
group->incrementVersion();
|
||||
group->sprite()->incrementVersion();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
@ -86,7 +86,7 @@ void convert_color_profile(doc::Sprite* sprite, const gfx::ColorSpaceRef& newCS)
|
|||
if (sprite->pixelFormat() != doc::IMAGE_INDEXED) {
|
||||
for (Cel* cel : sprite->uniqueCels()) {
|
||||
ImageRef old_image = cel->imageRef();
|
||||
if (old_image.get()->pixelFormat() != IMAGE_TILEMAP) {
|
||||
if (old_image && old_image->pixelFormat() != IMAGE_TILEMAP) {
|
||||
ImageRef new_image = convert_image_color_space(old_image.get(), newCS, conversion.get());
|
||||
|
||||
sprite->replaceImage(old_image->id(), new_image);
|
||||
|
@ -177,7 +177,7 @@ ConvertColorProfile::ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSp
|
|||
if (sprite->pixelFormat() != doc::IMAGE_INDEXED) {
|
||||
for (Cel* cel : sprite->uniqueCels()) {
|
||||
ImageRef old_image = cel->imageRef();
|
||||
if (old_image.get()->pixelFormat() != IMAGE_TILEMAP) {
|
||||
if (old_image && old_image->pixelFormat() != IMAGE_TILEMAP) {
|
||||
ImageRef new_image = convert_image_color_space(old_image.get(), newCS, conversion.get());
|
||||
|
||||
m_seq.add(new cmd::ReplaceImage(sprite, old_image, new_image));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -33,9 +33,9 @@ namespace app { namespace cmd {
|
|||
|
||||
using namespace doc;
|
||||
|
||||
MoveCel::MoveCel(LayerImage* srcLayer,
|
||||
MoveCel::MoveCel(Layer* srcLayer,
|
||||
frame_t srcFrame,
|
||||
LayerImage* dstLayer,
|
||||
Layer* dstLayer,
|
||||
frame_t dstFrame,
|
||||
bool continuous)
|
||||
: m_srcLayer(srcLayer)
|
||||
|
@ -48,9 +48,8 @@ MoveCel::MoveCel(LayerImage* srcLayer,
|
|||
|
||||
void MoveCel::onExecute()
|
||||
{
|
||||
LayerImage* srcLayer = static_cast<LayerImage*>(m_srcLayer.layer());
|
||||
LayerImage* dstLayer = static_cast<LayerImage*>(m_dstLayer.layer());
|
||||
|
||||
Layer* srcLayer = m_srcLayer.layer();
|
||||
Layer* dstLayer = m_dstLayer.layer();
|
||||
ASSERT(srcLayer);
|
||||
ASSERT(dstLayer);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -13,19 +14,14 @@
|
|||
#include "doc/color.h"
|
||||
#include "doc/frame.h"
|
||||
|
||||
namespace doc {
|
||||
class LayerImage;
|
||||
}
|
||||
|
||||
namespace app { namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class MoveCel : public CmdSequence {
|
||||
public:
|
||||
MoveCel(LayerImage* srcLayer,
|
||||
frame_t srcFrame,
|
||||
LayerImage* dstLayer,
|
||||
frame_t dstFrame,
|
||||
MoveCel(doc::Layer* srcLayer,
|
||||
doc::frame_t srcFrame,
|
||||
doc::Layer* dstLayer,
|
||||
doc::frame_t dstFrame,
|
||||
bool continuous);
|
||||
|
||||
protected:
|
||||
|
@ -34,7 +30,7 @@ protected:
|
|||
|
||||
private:
|
||||
WithLayer m_srcLayer, m_dstLayer;
|
||||
frame_t m_srcFrame, m_dstFrame;
|
||||
doc::frame_t m_srcFrame, m_dstFrame;
|
||||
bool m_continuous;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -32,8 +33,8 @@ void MoveLayer::onExecute()
|
|||
{
|
||||
Layer* layer = m_layer.layer();
|
||||
Layer* afterThis = m_newAfterThis.layer();
|
||||
LayerGroup* oldParent = static_cast<LayerGroup*>(m_oldParent.layer());
|
||||
LayerGroup* newParent = static_cast<LayerGroup*>(m_newParent.layer());
|
||||
Layer* oldParent = m_oldParent.layer();
|
||||
Layer* newParent = m_newParent.layer();
|
||||
ASSERT(layer);
|
||||
ASSERT(oldParent);
|
||||
ASSERT(newParent);
|
||||
|
@ -61,8 +62,8 @@ void MoveLayer::onUndo()
|
|||
{
|
||||
Layer* layer = m_layer.layer();
|
||||
Layer* afterThis = m_oldAfterThis.layer();
|
||||
LayerGroup* oldParent = static_cast<LayerGroup*>(m_oldParent.layer());
|
||||
LayerGroup* newParent = static_cast<LayerGroup*>(m_newParent.layer());
|
||||
Layer* oldParent = m_oldParent.layer();
|
||||
Layer* newParent = m_newParent.layer();
|
||||
ASSERT(layer);
|
||||
ASSERT(oldParent);
|
||||
ASSERT(newParent);
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/set_cel_image.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
namespace app { namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
SetCelImage::SetCelImage(Cel* cel, const ImageRef& newImage)
|
||||
: WithCel(cel)
|
||||
, m_oldImageId(cel->imageId())
|
||||
, m_newImageId(newImage ? newImage->id() : NullId)
|
||||
, m_newImage(newImage)
|
||||
{
|
||||
}
|
||||
|
||||
void SetCelImage::onExecute()
|
||||
{
|
||||
Cel* cel = this->cel();
|
||||
Sprite* sprite = cel->sprite();
|
||||
|
||||
// Save old image in m_copy. We cannot keep an ImageRef to this
|
||||
// image, because there are other undo branches that could try to
|
||||
// modify/re-add this same image ID
|
||||
if (m_oldImageId) {
|
||||
ImageRef oldImage = sprite->getImageRef(m_oldImageId);
|
||||
ASSERT(oldImage);
|
||||
m_copy.reset(Image::createCopy(oldImage.get()));
|
||||
}
|
||||
|
||||
cel->data()->setImage(m_newImage, cel->layer());
|
||||
cel->data()->incrementVersion();
|
||||
|
||||
m_newImage.reset();
|
||||
}
|
||||
|
||||
void SetCelImage::onUndo()
|
||||
{
|
||||
Cel* cel = this->cel();
|
||||
Sprite* sprite = cel->sprite();
|
||||
|
||||
ImageRef newImage;
|
||||
if (m_newImageId) {
|
||||
newImage = sprite->getImageRef(m_newImageId);
|
||||
ASSERT(newImage);
|
||||
}
|
||||
if (m_oldImageId) {
|
||||
ASSERT(!sprite->getImageRef(m_oldImageId));
|
||||
m_copy->setId(m_oldImageId);
|
||||
}
|
||||
|
||||
cel->data()->setImage(m_copy, cel->layer());
|
||||
m_copy.reset(newImage ? Image::createCopy(newImage.get()) : nullptr);
|
||||
}
|
||||
|
||||
void SetCelImage::onRedo()
|
||||
{
|
||||
Cel* cel = this->cel();
|
||||
Sprite* sprite = cel->sprite();
|
||||
|
||||
ImageRef oldImage;
|
||||
if (m_oldImageId)
|
||||
oldImage = sprite->getImageRef(m_oldImageId);
|
||||
|
||||
if (m_newImageId) {
|
||||
ASSERT(!sprite->getImageRef(m_newImageId));
|
||||
m_copy->setId(m_newImageId);
|
||||
cel->data()->setImage(m_copy, cel->layer());
|
||||
}
|
||||
else {
|
||||
cel->data()->setImage(nullptr, nullptr);
|
||||
}
|
||||
cel->data()->incrementVersion();
|
||||
|
||||
m_copy.reset(oldImage ? Image::createCopy(oldImage.get()) : nullptr);
|
||||
}
|
||||
|
||||
}} // namespace app::cmd
|
|
@ -0,0 +1,43 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_SET_CEL_IMAGE_H_INCLUDED
|
||||
#define APP_CMD_SET_CEL_IMAGE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd/with_cel.h"
|
||||
#include "doc/image_ref.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace app { namespace cmd {
|
||||
|
||||
class SetCelImage : public Cmd,
|
||||
public WithCel {
|
||||
public:
|
||||
SetCelImage(doc::Cel* cel, const doc::ImageRef& newImage);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onRedo() override;
|
||||
size_t onMemSize() const override { return sizeof(*this); }
|
||||
|
||||
private:
|
||||
doc::ObjectId m_oldImageId;
|
||||
doc::ObjectId m_newImageId;
|
||||
|
||||
// Reference used only to keep the copy of the new image from the
|
||||
// SetCelImage() ctor until the SetCelImage::onExecute() call. Then
|
||||
// the reference is not used anymore.
|
||||
doc::ImageRef m_newImage;
|
||||
doc::ImageRef m_copy;
|
||||
};
|
||||
|
||||
}} // namespace app::cmd
|
||||
|
||||
#endif
|
|
@ -390,8 +390,8 @@ CelPropertiesCommand::CelPropertiesCommand() : Command(CommandId::CelProperties(
|
|||
|
||||
bool CelPropertiesCommand::onEnabled(Context* context)
|
||||
{
|
||||
return context->isUIAvailable() && context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
|
||||
ContextFlags::ActiveLayerIsImage);
|
||||
return context->isUIAvailable() &&
|
||||
context->checkFlags(ContextFlags::ActiveDocumentIsWritable | ContextFlags::HasActiveCel);
|
||||
}
|
||||
|
||||
void CelPropertiesCommand::onExecute(Context* context)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -51,9 +51,6 @@ void ClearCelCommand::onExecute(Context* context)
|
|||
const Site& site = writer.site();
|
||||
if (site.inTimeline() && !site.selectedLayers().empty() && !site.selectedFrames().empty()) {
|
||||
for (Layer* layer : site.selectedLayers()) {
|
||||
if (!layer->isImage())
|
||||
continue;
|
||||
|
||||
if (!layer->isEditableHierarchy()) {
|
||||
nonEditableLayers = true;
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -146,8 +146,8 @@ void destroy_doc(Context* ctx, Doc* doc)
|
|||
void insert_layers_to_selected_layers(Layer* layer, SelectedLayers& selectedLayers)
|
||||
{
|
||||
if (layer->isGroup()) {
|
||||
auto children = static_cast<LayerGroup*>(layer)->layers();
|
||||
for (auto child : children)
|
||||
auto children = layer->layers();
|
||||
for (Layer* child : children)
|
||||
insert_layers_to_selected_layers(child, selectedLayers);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -377,6 +377,8 @@ private:
|
|||
return;
|
||||
|
||||
auto tilemap = static_cast<LayerTilemap*>(m_layer);
|
||||
expandWindow(gfx::Size(bounds().w, sizeHint().h));
|
||||
|
||||
auto tileset = tilemap->tileset();
|
||||
|
||||
// Information about the tileset to be used for new tilemaps
|
||||
|
@ -442,8 +444,11 @@ private:
|
|||
name()->setText(m_layer->name().c_str());
|
||||
name()->setEnabled(true);
|
||||
|
||||
if (m_layer->isImage() ||
|
||||
(m_layer->isGroup() && Preferences::instance().experimental.composeGroups())) {
|
||||
const bool imageProps =
|
||||
((m_layer->isImage() ||
|
||||
(m_layer->isGroup() && Preferences::instance().experimental.composeGroups())));
|
||||
|
||||
if (imageProps) {
|
||||
mode()->setSelectedItem(nullptr);
|
||||
for (auto item : *mode()) {
|
||||
if (auto blendModeItem = dynamic_cast<BlendModeItem*>(item)) {
|
||||
|
@ -457,10 +462,11 @@ private:
|
|||
opacity()->setValue(m_layer->opacity());
|
||||
opacity()->setEnabled(!m_layer->isBackground());
|
||||
}
|
||||
else {
|
||||
mode()->setEnabled(false);
|
||||
opacity()->setEnabled(false);
|
||||
}
|
||||
|
||||
modeLabel()->setVisible(imageProps);
|
||||
mode()->setVisible(imageProps);
|
||||
opacityLabel()->setVisible(imageProps);
|
||||
opacity()->setVisible(imageProps);
|
||||
|
||||
color_t c = m_layer->userData().color();
|
||||
m_userDataView.color()->setColor(
|
||||
|
@ -486,6 +492,8 @@ private:
|
|||
tileset()->setVisible(tilemapVisibility);
|
||||
tileset()->parent()->layout();
|
||||
}
|
||||
|
||||
expandWindow(gfx::Size(bounds().w, sizeHint().h));
|
||||
}
|
||||
|
||||
Timer m_timer;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -123,15 +123,9 @@ void NewFrameCommand::onExecute(Context* context)
|
|||
site.selectedFrames().firstFrame() + 1);
|
||||
|
||||
for (Layer* layer : selLayers) {
|
||||
if (layer->isImage()) {
|
||||
for (frame_t srcFrame : site.selectedFrames().reversed()) {
|
||||
frame_t dstFrame = srcFrame + frameRange;
|
||||
api.copyCel(static_cast<LayerImage*>(layer),
|
||||
srcFrame,
|
||||
static_cast<LayerImage*>(layer),
|
||||
dstFrame,
|
||||
continuous.get());
|
||||
}
|
||||
for (frame_t srcFrame : site.selectedFrames().reversed()) {
|
||||
frame_t dstFrame = srcFrame + frameRange;
|
||||
api.copyCel(layer, srcFrame, layer, dstFrame, continuous.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +133,7 @@ void NewFrameCommand::onExecute(Context* context)
|
|||
if (timeline)
|
||||
timeline->moveRange(range);
|
||||
}
|
||||
else if (auto layer = static_cast<LayerImage*>(writer.layer())) {
|
||||
else if (Layer* layer = writer.layer()) {
|
||||
api.copyCel(layer, writer.frame(), layer, writer.frame() + 1, continuous.get());
|
||||
|
||||
context->setActiveFrame(writer.frame() + 1);
|
||||
|
|
|
@ -33,7 +33,15 @@
|
|||
#include "app/util/clipboard.h"
|
||||
#include "app/util/new_image_from_mask.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_audio.h"
|
||||
#include "doc/layer_fill.h"
|
||||
#include "doc/layer_fx.h"
|
||||
#include "doc/layer_hitbox.h"
|
||||
#include "doc/layer_mask.h"
|
||||
#include "doc/layer_subsprite.h"
|
||||
#include "doc/layer_text.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/layer_vector.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "fmt/format.h"
|
||||
|
@ -58,6 +66,8 @@ struct NewLayerParams : public NewParams {
|
|||
Param<bool> group{ this, false, "group" };
|
||||
Param<bool> reference{ this, false, "reference" };
|
||||
Param<bool> tilemap{ this, false, "tilemap" };
|
||||
// Alternative for group/reference/tilemap params and used for future layer types
|
||||
Param<std::string> type{ this, {}, "type" };
|
||||
Param<gfx::Rect> gridBounds{ this, gfx::Rect(), "gridBounds" };
|
||||
Param<bool> ask{ this, false, "ask" };
|
||||
Param<bool> fromFile{
|
||||
|
@ -74,7 +84,20 @@ struct NewLayerParams : public NewParams {
|
|||
|
||||
class NewLayerCommand : public CommandWithNewParams<NewLayerParams> {
|
||||
public:
|
||||
enum class Type { Layer, Group, ReferenceLayer, TilemapLayer };
|
||||
enum class Type {
|
||||
Layer,
|
||||
Group,
|
||||
ReferenceLayer,
|
||||
TilemapLayer,
|
||||
FillLayer,
|
||||
MaskLayer,
|
||||
FxLayer,
|
||||
TextLayer,
|
||||
VectorLayer,
|
||||
AudioLayer,
|
||||
SubspriteLayer,
|
||||
HitboxLayer
|
||||
};
|
||||
enum class Place { AfterActiveLayer, BeforeActiveLayer, Top };
|
||||
|
||||
NewLayerCommand();
|
||||
|
@ -105,12 +128,30 @@ void NewLayerCommand::onLoadParams(const Params& commandParams)
|
|||
CommandWithNewParams<NewLayerParams>::onLoadParams(commandParams);
|
||||
|
||||
m_type = Type::Layer;
|
||||
if (params().group())
|
||||
if (params().group() || params().type() == "group")
|
||||
m_type = Type::Group;
|
||||
else if (params().reference())
|
||||
else if (params().reference() || params().type() == "reference")
|
||||
m_type = Type::ReferenceLayer;
|
||||
else if (params().tilemap())
|
||||
else if (params().tilemap() || params().type() == "tilemap")
|
||||
m_type = Type::TilemapLayer;
|
||||
#ifdef ENABLE_DEVMODE // TODO not yet production-ready
|
||||
else if (params().type() == "fill")
|
||||
m_type = Type::FillLayer;
|
||||
else if (params().type() == "mask")
|
||||
m_type = Type::MaskLayer;
|
||||
else if (params().type() == "fx")
|
||||
m_type = Type::FxLayer;
|
||||
else if (params().type() == "text")
|
||||
m_type = Type::TextLayer;
|
||||
else if (params().type() == "vector")
|
||||
m_type = Type::VectorLayer;
|
||||
else if (params().type() == "audio")
|
||||
m_type = Type::AudioLayer;
|
||||
else if (params().type() == "subsprite")
|
||||
m_type = Type::SubspriteLayer;
|
||||
else if (params().type() == "hitbox")
|
||||
m_type = Type::HitboxLayer;
|
||||
#endif // ENABLE_DEVMODE
|
||||
else
|
||||
m_type = Type::Layer;
|
||||
|
||||
|
@ -244,12 +285,12 @@ void NewLayerCommand::onExecute(Context* context)
|
|||
}
|
||||
}
|
||||
|
||||
LayerGroup* parent = sprite->root();
|
||||
Layer* parent = sprite->root();
|
||||
Layer* activeLayer = reader.layer();
|
||||
SelectedLayers selLayers = site.selectedLayers();
|
||||
if (activeLayer) {
|
||||
if (activeLayer->isGroup() && activeLayer->isExpanded() && m_type != Type::Group) {
|
||||
parent = static_cast<LayerGroup*>(activeLayer);
|
||||
parent = activeLayer;
|
||||
activeLayer = nullptr;
|
||||
}
|
||||
else {
|
||||
|
@ -293,6 +334,70 @@ void NewLayerCommand::onExecute(Context* context)
|
|||
layer = api.newTilemapAfter(parent, name, tsi, activeLayer);
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::FillLayer: {
|
||||
layer = new LayerFill(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::MaskLayer: {
|
||||
layer = new LayerMask(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::FxLayer: {
|
||||
layer = new LayerFx(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::TextLayer: {
|
||||
layer = new LayerText(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::VectorLayer: {
|
||||
layer = new LayerVector(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::AudioLayer: {
|
||||
layer = new LayerAudio(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::SubspriteLayer: {
|
||||
layer = new LayerSubsprite(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
|
||||
case Type::HitboxLayer: {
|
||||
layer = new LayerHitbox(parent->sprite());
|
||||
layer->setName(name);
|
||||
|
||||
api.addLayer(parent, layer, parent->lastLayer());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(layer);
|
||||
|
@ -324,7 +429,7 @@ void NewLayerCommand::onExecute(Context* context)
|
|||
|
||||
// Put all selected layers inside the group
|
||||
if (m_type == Type::Group && site.inTimeline()) {
|
||||
LayerGroup* commonParent = nullptr;
|
||||
Layer* commonParent = nullptr;
|
||||
layer_t sameParents = 0;
|
||||
for (Layer* l : selLayers) {
|
||||
if (!commonParent || commonParent == l->parent()) {
|
||||
|
@ -335,7 +440,7 @@ void NewLayerCommand::onExecute(Context* context)
|
|||
|
||||
if (sameParents == selLayers.size()) {
|
||||
for (Layer* newChild : selLayers.toBrowsableLayerList()) {
|
||||
tx(new cmd::MoveLayer(newChild, layer, static_cast<LayerGroup*>(layer)->lastLayer()));
|
||||
tx(new cmd::MoveLayer(newChild, layer, layer->lastLayer()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -518,7 +623,7 @@ int NewLayerCommand::getMaxLayerNum(const Layer* layer) const
|
|||
max = std::strtol(layer->name().c_str() + prefix.size(), NULL, 10);
|
||||
|
||||
if (layer->isGroup()) {
|
||||
for (const Layer* child : static_cast<const LayerGroup*>(layer)->layers()) {
|
||||
for (const Layer* child : layer->layers()) {
|
||||
int tmp = getMaxLayerNum(child);
|
||||
max = std::max(tmp, max);
|
||||
}
|
||||
|
@ -534,6 +639,14 @@ std::string NewLayerCommand::layerPrefix() const
|
|||
case Type::Group: return Strings::commands_NewLayer_Group();
|
||||
case Type::ReferenceLayer: return Strings::commands_NewLayer_ReferenceLayer();
|
||||
case Type::TilemapLayer: return Strings::commands_NewLayer_TilemapLayer();
|
||||
case Type::FillLayer: return Strings::commands_NewLayer_FillLayer();
|
||||
case Type::MaskLayer: return Strings::commands_NewLayer_MaskLayer();
|
||||
case Type::FxLayer: return Strings::commands_NewLayer_FxLayer();
|
||||
case Type::TextLayer: return Strings::commands_NewLayer_TextLayer();
|
||||
case Type::VectorLayer: return Strings::commands_NewLayer_VectorLayer();
|
||||
case Type::AudioLayer: return Strings::commands_NewLayer_AudioLayer();
|
||||
case Type::SubspriteLayer: return Strings::commands_NewLayer_SubspriteLayer();
|
||||
case Type::HitboxLayer: return Strings::commands_NewLayer_HitboxLayer();
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -96,16 +96,15 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
|
|||
if (layer->isTilemap())
|
||||
m_flags |= ActiveLayerIsTilemap;
|
||||
|
||||
if (layer->isImage()) {
|
||||
if (layer->isImage())
|
||||
m_flags |= ActiveLayerIsImage;
|
||||
|
||||
Cel* cel = layer->cel(frame);
|
||||
if (cel) {
|
||||
m_flags |= HasActiveCel;
|
||||
Cel* cel = layer->cel(frame);
|
||||
if (cel) {
|
||||
m_flags |= HasActiveCel;
|
||||
|
||||
if (cel->image())
|
||||
m_flags |= HasActiveImage;
|
||||
}
|
||||
if (cel->image())
|
||||
m_flags |= HasActiveImage;
|
||||
}
|
||||
|
||||
if (site.selectedColors().picks() > 0)
|
||||
|
|
|
@ -28,7 +28,15 @@
|
|||
#include "doc/frame.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_audio.h"
|
||||
#include "doc/layer_fill.h"
|
||||
#include "doc/layer_fx.h"
|
||||
#include "doc/layer_hitbox.h"
|
||||
#include "doc/layer_mask.h"
|
||||
#include "doc/layer_subsprite.h"
|
||||
#include "doc/layer_text.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/layer_vector.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/palette_io.h"
|
||||
#include "doc/serial_format.h"
|
||||
|
@ -289,7 +297,7 @@ private:
|
|||
// Read layers
|
||||
int nlayers = read32(s);
|
||||
if (nlayers >= 1 && nlayers < 0xfffff) {
|
||||
std::map<ObjectId, LayerGroup*> layersMap;
|
||||
std::map<ObjectId, Layer*> layersMap;
|
||||
layersMap[0] = spr->root(); // parentId = 0 is the root level
|
||||
|
||||
for (int i = 0; i < nlayers; ++i) {
|
||||
|
@ -308,7 +316,7 @@ private:
|
|||
Layer* lay = loadObject<Layer*>("lay", layId, &Reader::readLayer);
|
||||
if (lay) {
|
||||
if (lay->isGroup())
|
||||
layersMap[layId] = static_cast<LayerGroup*>(lay);
|
||||
layersMap[layId] = lay;
|
||||
|
||||
layersMap[parentId]->addLayer(lay);
|
||||
}
|
||||
|
@ -324,7 +332,7 @@ private:
|
|||
return nullptr;
|
||||
|
||||
const auto& pair = m_celsToLoad[i];
|
||||
LayerImage* lay = doc::get<LayerImage>(pair.first);
|
||||
Layer* lay = doc::get<Layer>(pair.first);
|
||||
if (!lay)
|
||||
continue;
|
||||
|
||||
|
@ -447,59 +455,68 @@ private:
|
|||
{
|
||||
LayerFlags flags = (LayerFlags)read32(s);
|
||||
ObjectType type = (ObjectType)read16(s);
|
||||
ASSERT(type == ObjectType::LayerImage || type == ObjectType::LayerGroup ||
|
||||
type == ObjectType::LayerTilemap);
|
||||
|
||||
std::string name = read_string(s);
|
||||
|
||||
std::unique_ptr<Layer> lay;
|
||||
|
||||
switch (type) {
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
switch (type) {
|
||||
case ObjectType::LayerImage: lay.reset(new LayerImage(m_sprite)); break;
|
||||
case ObjectType::LayerImage: lay = std::make_unique<LayerImage>(m_sprite); break;
|
||||
case ObjectType::LayerTilemap: {
|
||||
tileset_index tilesetIndex = read32(s);
|
||||
lay.reset(new LayerTilemap(m_sprite, tilesetIndex));
|
||||
lay = std::make_unique<LayerTilemap>(m_sprite, tilesetIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lay->setName(name);
|
||||
lay->setFlags(flags);
|
||||
|
||||
// Blend mode & opacity
|
||||
static_cast<LayerImage*>(lay.get())->setBlendMode((BlendMode)read16(s));
|
||||
static_cast<LayerImage*>(lay.get())->setOpacity(read8(s));
|
||||
|
||||
// Cels
|
||||
int ncels = read32(s);
|
||||
for (int i = 0; i < ncels; ++i) {
|
||||
if (canceled())
|
||||
return nullptr;
|
||||
|
||||
// Add a new cel to load in the future after we load all layers
|
||||
ObjectId celId = read32(s);
|
||||
m_celsToLoad.push_back(std::make_pair(lay->id(), celId));
|
||||
}
|
||||
lay->setBlendMode((BlendMode)read16(s));
|
||||
lay->setOpacity(read8(s));
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectType::LayerGroup:
|
||||
lay.reset(new LayerGroup(m_sprite));
|
||||
lay->setName(name);
|
||||
lay->setFlags(flags);
|
||||
break;
|
||||
case ObjectType::LayerGroup: lay = std::make_unique<LayerGroup>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerFill: lay = std::make_unique<LayerFill>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerMask: lay = std::make_unique<LayerMask>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerFx: lay = std::make_unique<LayerFx>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerText: lay = std::make_unique<LayerText>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerVector: lay = std::make_unique<LayerVector>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerAudio: lay = std::make_unique<LayerAudio>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerSubsprite: lay = std::make_unique<LayerSubsprite>(m_sprite); break;
|
||||
|
||||
case ObjectType::LayerHitbox: lay = std::make_unique<LayerHitbox>(m_sprite); break;
|
||||
|
||||
default:
|
||||
Console().printf("Unable to load layer named '%s', type #%d\n", name.c_str(), (int)type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lay)
|
||||
return nullptr;
|
||||
|
||||
lay->setName(name);
|
||||
lay->setFlags(flags);
|
||||
|
||||
// LayerGroup doesn't contain cels
|
||||
if (type != ObjectType::LayerGroup) {
|
||||
const int ncels = read32(s);
|
||||
for (int i = 0; i < ncels; ++i) {
|
||||
if (canceled())
|
||||
return nullptr;
|
||||
|
||||
// Add a new cel to load in the future after we load all layers
|
||||
ObjectId celId = read32(s);
|
||||
m_celsToLoad.push_back(std::make_pair(lay->id(), celId));
|
||||
}
|
||||
}
|
||||
|
||||
UserData userData = read_user_data(s, m_serial);
|
||||
lay->setUserData(userData);
|
||||
|
||||
|
|
|
@ -104,14 +104,12 @@ public:
|
|||
|
||||
// Save original cel data (skip links)
|
||||
for (Layer* lay : layers) {
|
||||
CelList cels;
|
||||
lay->getCels(cels);
|
||||
|
||||
for (Cel* cel : cels) {
|
||||
for (const Cel* cel : lay->cels()) {
|
||||
if (cel->link()) // Skip link
|
||||
continue;
|
||||
|
||||
if (!saveObject("img", cel->image(), &Writer::writeImage))
|
||||
// TODO backup other cel data, e.g. for audio layers
|
||||
if (cel->image() && !saveObject("img", cel->image(), &Writer::writeImage))
|
||||
return false;
|
||||
|
||||
if (!saveObject("celdata", cel->data(), &Writer::writeCelData))
|
||||
|
@ -121,10 +119,7 @@ public:
|
|||
|
||||
// Save all cels (original and links)
|
||||
for (Layer* lay : layers) {
|
||||
CelList cels;
|
||||
lay->getCels(cels);
|
||||
|
||||
for (Cel* cel : cels)
|
||||
for (Cel* cel : lay->cels())
|
||||
if (!saveObject("cel", cel, &Writer::writeCel))
|
||||
return false;
|
||||
}
|
||||
|
@ -235,14 +230,14 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
void writeAllLayersID(std::ofstream& s, ObjectId parentId, const LayerGroup* group)
|
||||
void writeAllLayersID(std::ofstream& s, ObjectId parentId, const Layer* group)
|
||||
{
|
||||
for (const Layer* lay : group->layers()) {
|
||||
write32(s, lay->id());
|
||||
write32(s, parentId);
|
||||
|
||||
if (lay->isGroup())
|
||||
writeAllLayersID(s, lay->id(), static_cast<const LayerGroup*>(lay));
|
||||
writeAllLayersID(s, lay->id(), lay);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,19 +254,9 @@ private:
|
|||
if (lay->type() == ObjectType::LayerTilemap)
|
||||
write32(s, static_cast<const LayerTilemap*>(lay)->tilesetIndex());
|
||||
|
||||
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
|
||||
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
|
||||
|
||||
// Blend mode & opacity
|
||||
write16(s, (int)static_cast<const LayerImage*>(lay)->blendMode());
|
||||
write8(s, static_cast<const LayerImage*>(lay)->opacity());
|
||||
|
||||
// Cels
|
||||
write32(s, static_cast<const LayerImage*>(lay)->getCelsCount());
|
||||
for (it = begin; it != end; ++it) {
|
||||
const Cel* cel = *it;
|
||||
write32(s, cel->id());
|
||||
}
|
||||
write16(s, (int)lay->blendMode());
|
||||
write8(s, lay->opacity());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -279,6 +264,26 @@ private:
|
|||
// Do nothing (the layer parent/children structure is saved in
|
||||
// writeSprite/writeAllLayersID() functions)
|
||||
break;
|
||||
|
||||
case ObjectType::LayerFill:
|
||||
case ObjectType::LayerMask:
|
||||
case ObjectType::LayerFx:
|
||||
case ObjectType::LayerText:
|
||||
case ObjectType::LayerVector:
|
||||
case ObjectType::LayerAudio:
|
||||
case ObjectType::LayerSubsprite:
|
||||
case ObjectType::LayerHitbox: {
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save cels
|
||||
if (lay->type() != ObjectType::LayerGroup) {
|
||||
const CelList& cels = lay->cels();
|
||||
write32(s, cels.size());
|
||||
for (const Cel* cel : cels)
|
||||
write32(s, cel->id());
|
||||
}
|
||||
|
||||
// Save user data
|
||||
|
|
|
@ -515,9 +515,9 @@ void Doc::resetTransformation()
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// Copying
|
||||
|
||||
void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destLayer0) const
|
||||
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const
|
||||
{
|
||||
LayerFlags dstFlags = sourceLayer0->flags();
|
||||
LayerFlags dstFlags = sourceLayer->flags();
|
||||
|
||||
// Remove the "background" flag if the destDoc already has a background layer.
|
||||
if (((int)dstFlags & (int)LayerFlags::Background) == (int)LayerFlags::Background &&
|
||||
|
@ -526,53 +526,15 @@ void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destL
|
|||
}
|
||||
|
||||
// Copy the layer name/flags/user data
|
||||
destLayer0->setName(sourceLayer0->name());
|
||||
destLayer0->setFlags(dstFlags);
|
||||
destLayer0->setUserData(sourceLayer0->userData());
|
||||
destLayer->setName(sourceLayer->name());
|
||||
destLayer->setFlags(dstFlags);
|
||||
destLayer->setUserData(sourceLayer->userData());
|
||||
|
||||
if (sourceLayer0->isImage() && destLayer0->isImage()) {
|
||||
const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0);
|
||||
LayerImage* destLayer = static_cast<LayerImage*>(destLayer0);
|
||||
|
||||
// Copy blend mode and opacity
|
||||
destLayer->setBlendMode(sourceLayer->blendMode());
|
||||
destLayer->setOpacity(sourceLayer->opacity());
|
||||
|
||||
// Copy cels
|
||||
CelConstIterator it = sourceLayer->getCelBegin();
|
||||
CelConstIterator end = sourceLayer->getCelEnd();
|
||||
|
||||
std::map<ObjectId, Cel*> linked;
|
||||
|
||||
for (; it != end; ++it) {
|
||||
const Cel* sourceCel = *it;
|
||||
if (sourceCel->frame() > destLayer->sprite()->lastFrame())
|
||||
break;
|
||||
|
||||
std::unique_ptr<Cel> newCel(nullptr);
|
||||
|
||||
auto it = linked.find(sourceCel->data()->id());
|
||||
if (it != linked.end()) {
|
||||
newCel.reset(Cel::MakeLink(sourceCel->frame(), it->second));
|
||||
newCel->copyNonsharedPropertiesFrom(sourceCel);
|
||||
}
|
||||
else {
|
||||
newCel.reset(create_cel_copy(nullptr, // TODO add undo information?
|
||||
sourceCel,
|
||||
destLayer->sprite(),
|
||||
destLayer,
|
||||
sourceCel->frame()));
|
||||
linked.insert(std::make_pair(sourceCel->data()->id(), newCel.get()));
|
||||
}
|
||||
|
||||
destLayer->addCel(newCel.get());
|
||||
newCel.release();
|
||||
}
|
||||
}
|
||||
else if (sourceLayer0->isGroup() && destLayer0->isGroup()) {
|
||||
const LayerGroup* sourceLayer = static_cast<const LayerGroup*>(sourceLayer0);
|
||||
LayerGroup* destLayer = static_cast<LayerGroup*>(destLayer0);
|
||||
// Copy blend mode and opacity
|
||||
destLayer->setBlendMode(sourceLayer->blendMode());
|
||||
destLayer->setOpacity(sourceLayer->opacity());
|
||||
|
||||
if (sourceLayer->isGroup() && destLayer->isGroup()) {
|
||||
for (Layer* sourceChild : sourceLayer->layers()) {
|
||||
std::unique_ptr<Layer> destChild(nullptr);
|
||||
|
||||
|
@ -608,6 +570,38 @@ void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destL
|
|||
destLayer->stackLayer(newLayer, afterThis);
|
||||
}
|
||||
}
|
||||
else if (sourceLayer->type() == destLayer->type()) {
|
||||
// Copy cels
|
||||
CelConstIterator it = sourceLayer->getCelBegin();
|
||||
const CelConstIterator end = sourceLayer->getCelEnd();
|
||||
|
||||
std::map<ObjectId, Cel*> linked;
|
||||
|
||||
for (; it != end; ++it) {
|
||||
const Cel* sourceCel = *it;
|
||||
if (sourceCel->frame() > destLayer->sprite()->lastFrame())
|
||||
break;
|
||||
|
||||
std::unique_ptr<Cel> newCel(nullptr);
|
||||
|
||||
auto it = linked.find(sourceCel->data()->id());
|
||||
if (it != linked.end()) {
|
||||
newCel.reset(Cel::MakeLink(sourceCel->frame(), it->second));
|
||||
newCel->copyNonsharedPropertiesFrom(sourceCel);
|
||||
}
|
||||
else {
|
||||
newCel.reset(create_cel_copy(nullptr, // TODO add undo information?
|
||||
sourceCel,
|
||||
destLayer->sprite(),
|
||||
destLayer,
|
||||
sourceCel->frame()));
|
||||
linked.insert(std::make_pair(sourceCel->data()->id(), newCel.get()));
|
||||
}
|
||||
|
||||
destLayer->addCel(newCel.get());
|
||||
newCel.release();
|
||||
}
|
||||
}
|
||||
else {
|
||||
ASSERT(false && "Trying to copy two incompatible layers");
|
||||
}
|
||||
|
|
|
@ -53,7 +53,15 @@
|
|||
#include "doc/algorithm/flip_image.h"
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/layer_audio.h"
|
||||
#include "doc/layer_fill.h"
|
||||
#include "doc/layer_fx.h"
|
||||
#include "doc/layer_hitbox.h"
|
||||
#include "doc/layer_mask.h"
|
||||
#include "doc/layer_subsprite.h"
|
||||
#include "doc/layer_text.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/layer_vector.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/slice.h"
|
||||
|
@ -74,9 +82,9 @@
|
|||
namespace app {
|
||||
|
||||
DocApi::HandleLinkedCels::HandleLinkedCels(DocApi& api,
|
||||
doc::LayerImage* srcLayer,
|
||||
doc::Layer* srcLayer,
|
||||
const doc::frame_t srcFrame,
|
||||
doc::LayerImage* dstLayer,
|
||||
doc::Layer* dstLayer,
|
||||
const doc::frame_t dstFrame)
|
||||
: m_api(api)
|
||||
, m_srcDataId(doc::NullId)
|
||||
|
@ -351,11 +359,8 @@ void DocApi::copyFrame(Sprite* sprite,
|
|||
if (fromFrame >= newFrame)
|
||||
++fromFrame;
|
||||
|
||||
for (Layer* layer : sprite->allLayers()) {
|
||||
if (layer->isImage()) {
|
||||
copyCel(static_cast<LayerImage*>(layer), fromFrame, static_cast<LayerImage*>(layer), newFrame);
|
||||
}
|
||||
}
|
||||
for (Layer* layer : sprite->allLayers())
|
||||
copyCel(layer, fromFrame, layer, newFrame);
|
||||
|
||||
adjustTags(sprite, newFrame0, +1, dropFramePlace, tagsHandling);
|
||||
}
|
||||
|
@ -474,14 +479,14 @@ void DocApi::moveFrameLayer(Layer* layer, frame_t frame, frame_t beforeFrame)
|
|||
}
|
||||
|
||||
case ObjectType::LayerGroup: {
|
||||
for (Layer* child : static_cast<LayerGroup*>(layer)->layers())
|
||||
for (Layer* child : layer->layers())
|
||||
moveFrameLayer(child, frame, beforeFrame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DocApi::addCel(LayerImage* layer, Cel* cel)
|
||||
void DocApi::addCel(Layer* layer, Cel* cel)
|
||||
{
|
||||
ASSERT(layer);
|
||||
ASSERT(cel);
|
||||
|
@ -540,7 +545,7 @@ void DocApi::clearCelAndAllLinks(Cel* cel)
|
|||
}
|
||||
}
|
||||
|
||||
void DocApi::moveCel(LayerImage* srcLayer, frame_t srcFrame, LayerImage* dstLayer, frame_t dstFrame)
|
||||
void DocApi::moveCel(Layer* srcLayer, frame_t srcFrame, Layer* dstLayer, frame_t dstFrame)
|
||||
{
|
||||
ASSERT(srcLayer != dstLayer || srcFrame != dstFrame);
|
||||
if (srcLayer == dstLayer && srcFrame == dstFrame)
|
||||
|
@ -557,9 +562,9 @@ void DocApi::moveCel(LayerImage* srcLayer, frame_t srcFrame, LayerImage* dstLaye
|
|||
new cmd::MoveCel(srcLayer, srcFrame, dstLayer, dstFrame, dstLayer->isContinuous()));
|
||||
}
|
||||
|
||||
void DocApi::copyCel(LayerImage* srcLayer,
|
||||
void DocApi::copyCel(Layer* srcLayer,
|
||||
frame_t srcFrame,
|
||||
LayerImage* dstLayer,
|
||||
Layer* dstLayer,
|
||||
frame_t dstFrame,
|
||||
const bool* forceContinuous)
|
||||
{
|
||||
|
@ -579,7 +584,7 @@ void DocApi::copyCel(LayerImage* srcLayer,
|
|||
(forceContinuous ? *forceContinuous : dstLayer->isContinuous())));
|
||||
}
|
||||
|
||||
void DocApi::swapCel(LayerImage* layer, frame_t frame1, frame_t frame2)
|
||||
void DocApi::swapCel(Layer* layer, frame_t frame1, frame_t frame2)
|
||||
{
|
||||
ASSERT(frame1 != frame2);
|
||||
|
||||
|
@ -598,7 +603,7 @@ void DocApi::swapCel(LayerImage* layer, frame_t frame1, frame_t frame2)
|
|||
setCelFramePosition(cel2, frame1);
|
||||
}
|
||||
|
||||
LayerImage* DocApi::newLayer(LayerGroup* parent, const std::string& name)
|
||||
LayerImage* DocApi::newLayer(Layer* parent, const std::string& name)
|
||||
{
|
||||
LayerImage* newLayer = new LayerImage(parent->sprite());
|
||||
newLayer->setName(name);
|
||||
|
@ -607,7 +612,7 @@ LayerImage* DocApi::newLayer(LayerGroup* parent, const std::string& name)
|
|||
return newLayer;
|
||||
}
|
||||
|
||||
LayerImage* DocApi::newLayerAfter(LayerGroup* parent, const std::string& name, Layer* afterThis)
|
||||
LayerImage* DocApi::newLayerAfter(Layer* parent, const std::string& name, Layer* afterThis)
|
||||
{
|
||||
LayerImage* newLayer = new LayerImage(parent->sprite());
|
||||
newLayer->setName(name);
|
||||
|
@ -619,7 +624,7 @@ LayerImage* DocApi::newLayerAfter(LayerGroup* parent, const std::string& name, L
|
|||
return newLayer;
|
||||
}
|
||||
|
||||
LayerGroup* DocApi::newGroup(LayerGroup* parent, const std::string& name)
|
||||
LayerGroup* DocApi::newGroup(Layer* parent, const std::string& name)
|
||||
{
|
||||
LayerGroup* newLayerGroup = new LayerGroup(parent->sprite());
|
||||
newLayerGroup->setName(name);
|
||||
|
@ -628,7 +633,7 @@ LayerGroup* DocApi::newGroup(LayerGroup* parent, const std::string& name)
|
|||
return newLayerGroup;
|
||||
}
|
||||
|
||||
LayerGroup* DocApi::newGroupAfter(LayerGroup* parent, const std::string& name, Layer* afterThis)
|
||||
LayerGroup* DocApi::newGroupAfter(Layer* parent, const std::string& name, Layer* afterThis)
|
||||
{
|
||||
LayerGroup* newLayerGroup = new LayerGroup(parent->sprite());
|
||||
newLayerGroup->setName(name);
|
||||
|
@ -640,7 +645,7 @@ LayerGroup* DocApi::newGroupAfter(LayerGroup* parent, const std::string& name, L
|
|||
return newLayerGroup;
|
||||
}
|
||||
|
||||
LayerTilemap* DocApi::newTilemapAfter(LayerGroup* parent,
|
||||
LayerTilemap* DocApi::newTilemapAfter(Layer* parent,
|
||||
const std::string& name,
|
||||
tileset_index tsi,
|
||||
Layer* afterThis)
|
||||
|
@ -655,7 +660,7 @@ LayerTilemap* DocApi::newTilemapAfter(LayerGroup* parent,
|
|||
return newTilemap;
|
||||
}
|
||||
|
||||
void DocApi::addLayer(LayerGroup* parent, Layer* newLayer, Layer* afterThis)
|
||||
void DocApi::addLayer(Layer* parent, Layer* newLayer, Layer* afterThis)
|
||||
{
|
||||
m_transaction.execute(new cmd::AddLayer(parent, newLayer, afterThis));
|
||||
}
|
||||
|
@ -667,7 +672,7 @@ void DocApi::removeLayer(Layer* layer)
|
|||
m_transaction.execute(new cmd::RemoveLayer(layer));
|
||||
}
|
||||
|
||||
void DocApi::restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThis)
|
||||
void DocApi::restackLayerAfter(Layer* layer, Layer* parent, Layer* afterThis)
|
||||
{
|
||||
ASSERT(parent);
|
||||
|
||||
|
@ -677,7 +682,7 @@ void DocApi::restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThi
|
|||
m_transaction.execute(new cmd::MoveLayer(layer, parent, afterThis));
|
||||
}
|
||||
|
||||
void DocApi::restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeThis)
|
||||
void DocApi::restackLayerBefore(Layer* layer, Layer* parent, Layer* beforeThis)
|
||||
{
|
||||
ASSERT(parent);
|
||||
|
||||
|
@ -696,27 +701,41 @@ void DocApi::restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeT
|
|||
Layer* DocApi::copyLayerWithSprite(doc::Layer* layer, doc::Sprite* sprite)
|
||||
{
|
||||
std::unique_ptr<doc::Layer> clone;
|
||||
if (layer->isTilemap()) {
|
||||
auto* srcTilemap = static_cast<LayerTilemap*>(layer);
|
||||
tileset_index tilesetIndex = srcTilemap->tilesetIndex();
|
||||
// If the caller is trying to make a copy of a tilemap layer specifying a
|
||||
// different sprite as its owner, then we must copy the tilesets of the
|
||||
// given tilemap layer into the new owner.
|
||||
if (sprite != srcTilemap->sprite()) {
|
||||
auto* srcTilesetCopy = Tileset::MakeCopyCopyingImagesForSprite(srcTilemap->tileset(), sprite);
|
||||
auto* addTileset = new cmd::AddTileset(sprite, srcTilesetCopy);
|
||||
m_transaction.execute(addTileset);
|
||||
tilesetIndex = addTileset->tilesetIndex();
|
||||
|
||||
switch (layer->type()) {
|
||||
case ObjectType::LayerImage: clone = std::make_unique<LayerImage>(sprite); break;
|
||||
|
||||
case ObjectType::LayerGroup: clone = std::make_unique<LayerGroup>(sprite); break;
|
||||
|
||||
case ObjectType::LayerTilemap: {
|
||||
auto* srcTilemap = static_cast<LayerTilemap*>(layer);
|
||||
tileset_index tilesetIndex = srcTilemap->tilesetIndex();
|
||||
// If the caller is trying to make a copy of a tilemap layer specifying a
|
||||
// different sprite as its owner, then we must copy the tilesets of the
|
||||
// given tilemap layer into the new owner.
|
||||
if (sprite != srcTilemap->sprite()) {
|
||||
auto* srcTilesetCopy = Tileset::MakeCopyCopyingImagesForSprite(srcTilemap->tileset(),
|
||||
sprite);
|
||||
auto* addTileset = new cmd::AddTileset(sprite, srcTilesetCopy);
|
||||
m_transaction.execute(addTileset);
|
||||
tilesetIndex = addTileset->tilesetIndex();
|
||||
}
|
||||
|
||||
clone = std::make_unique<LayerTilemap>(sprite, tilesetIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
clone = std::make_unique<LayerTilemap>(sprite, tilesetIndex);
|
||||
case ObjectType::LayerFill: clone = std::make_unique<LayerFill>(sprite); break;
|
||||
case ObjectType::LayerMask: clone = std::make_unique<LayerMask>(sprite); break;
|
||||
case ObjectType::LayerFx: clone = std::make_unique<LayerFx>(sprite); break;
|
||||
case ObjectType::LayerText: clone = std::make_unique<LayerText>(sprite); break;
|
||||
case ObjectType::LayerVector: clone = std::make_unique<LayerVector>(sprite); break;
|
||||
case ObjectType::LayerAudio: clone = std::make_unique<LayerAudio>(sprite); break;
|
||||
case ObjectType::LayerSubsprite: clone = std::make_unique<LayerSubsprite>(sprite); break;
|
||||
case ObjectType::LayerHitbox: clone = std::make_unique<LayerHitbox>(sprite); break;
|
||||
|
||||
default: throw std::runtime_error("Invalid layer type");
|
||||
}
|
||||
else if (layer->isImage())
|
||||
clone = std::make_unique<LayerImage>(sprite);
|
||||
else if (layer->isGroup())
|
||||
clone = std::make_unique<LayerGroup>(sprite);
|
||||
else
|
||||
throw std::runtime_error("Invalid layer type");
|
||||
|
||||
if (auto* doc = dynamic_cast<app::Doc*>(sprite->document())) {
|
||||
doc->copyLayerContent(layer, doc, clone.get());
|
||||
|
@ -726,7 +745,7 @@ Layer* DocApi::copyLayerWithSprite(doc::Layer* layer, doc::Sprite* sprite)
|
|||
}
|
||||
|
||||
Layer* DocApi::duplicateLayerAfter(Layer* sourceLayer,
|
||||
LayerGroup* parent,
|
||||
Layer* parent,
|
||||
Layer* afterLayer,
|
||||
const std::string& nameSuffix)
|
||||
{
|
||||
|
@ -741,7 +760,7 @@ Layer* DocApi::duplicateLayerAfter(Layer* sourceLayer,
|
|||
}
|
||||
|
||||
Layer* DocApi::duplicateLayerBefore(Layer* sourceLayer,
|
||||
LayerGroup* parent,
|
||||
Layer* parent,
|
||||
Layer* beforeLayer,
|
||||
const std::string& nameSuffix)
|
||||
{
|
||||
|
|
|
@ -91,40 +91,40 @@ public:
|
|||
const TagsHandling tagsHandling);
|
||||
|
||||
// Cels API
|
||||
void addCel(LayerImage* layer, Cel* cel);
|
||||
void addCel(Layer* layer, Cel* cel);
|
||||
Cel* addCel(LayerImage* layer, frame_t frameNumber, const ImageRef& image);
|
||||
void clearCel(Layer* layer, frame_t frame);
|
||||
void clearCel(Cel* cel);
|
||||
void clearCelAndAllLinks(Cel* cel);
|
||||
void setCelPosition(Sprite* sprite, Cel* cel, int x, int y);
|
||||
void setCelOpacity(Sprite* sprite, Cel* cel, int newOpacity);
|
||||
void moveCel(LayerImage* srcLayer, frame_t srcFrame, LayerImage* dstLayer, frame_t dstFrame);
|
||||
void copyCel(LayerImage* srcLayer,
|
||||
void moveCel(Layer* srcLayer, frame_t srcFrame, Layer* dstLayer, frame_t dstFrame);
|
||||
void copyCel(Layer* srcLayer,
|
||||
frame_t srcFrame,
|
||||
LayerImage* dstLayer,
|
||||
Layer* dstLayer,
|
||||
frame_t dstFrame,
|
||||
const bool* forceContinuous = nullptr);
|
||||
void swapCel(LayerImage* layer, frame_t frame1, frame_t frame2);
|
||||
void swapCel(Layer* layer, frame_t frame1, frame_t frame2);
|
||||
|
||||
// Layers API
|
||||
LayerImage* newLayer(LayerGroup* parent, const std::string& name);
|
||||
LayerImage* newLayerAfter(LayerGroup* parent, const std::string& name, Layer* afterThis);
|
||||
LayerGroup* newGroup(LayerGroup* parent, const std::string& name);
|
||||
LayerGroup* newGroupAfter(LayerGroup* parent, const std::string& name, Layer* afterThis);
|
||||
LayerTilemap* newTilemapAfter(LayerGroup* parent,
|
||||
LayerImage* newLayer(Layer* parent, const std::string& name);
|
||||
LayerImage* newLayerAfter(Layer* parent, const std::string& name, Layer* afterThis);
|
||||
LayerGroup* newGroup(Layer* parent, const std::string& name);
|
||||
LayerGroup* newGroupAfter(Layer* parent, const std::string& name, Layer* afterThis);
|
||||
LayerTilemap* newTilemapAfter(Layer* parent,
|
||||
const std::string& name,
|
||||
tileset_index tsi,
|
||||
Layer* afterThis);
|
||||
void addLayer(LayerGroup* parent, Layer* newLayer, Layer* afterThis);
|
||||
void addLayer(Layer* parent, Layer* newLayer, Layer* afterThis);
|
||||
void removeLayer(Layer* layer);
|
||||
void restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThis);
|
||||
void restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeThis);
|
||||
void restackLayerAfter(Layer* layer, Layer* parent, Layer* afterThis);
|
||||
void restackLayerBefore(Layer* layer, Layer* parent, Layer* beforeThis);
|
||||
Layer* duplicateLayerAfter(Layer* sourceLayer,
|
||||
LayerGroup* parent,
|
||||
Layer* parent,
|
||||
Layer* afterLayer,
|
||||
const std::string& nameSuffix = std::string());
|
||||
Layer* duplicateLayerBefore(Layer* sourceLayer,
|
||||
LayerGroup* parent,
|
||||
Layer* parent,
|
||||
Layer* beforeLayer,
|
||||
const std::string& nameSuffix = std::string());
|
||||
|
||||
|
@ -166,9 +166,9 @@ private:
|
|||
class HandleLinkedCels {
|
||||
public:
|
||||
HandleLinkedCels(DocApi& api,
|
||||
doc::LayerImage* srcLayer,
|
||||
doc::Layer* srcLayer,
|
||||
const doc::frame_t srcFrame,
|
||||
doc::LayerImage* dstLayer,
|
||||
doc::Layer* dstLayer,
|
||||
const doc::frame_t dstFrame);
|
||||
~HandleLinkedCels();
|
||||
bool linkWasCreated() { return m_created; }
|
||||
|
|
|
@ -43,12 +43,12 @@ void setup_insertion_layer(Doc* destDoc,
|
|||
doc::layer_t layerIndex,
|
||||
InsertionPoint& insert,
|
||||
Layer*& layer,
|
||||
LayerGroup*& group)
|
||||
Layer*& group)
|
||||
{
|
||||
const LayerList& allLayers = destDoc->sprite()->allLayers();
|
||||
layer = allLayers[layerIndex];
|
||||
if (insert == InsertionPoint::BeforeLayer && layer->isGroup()) {
|
||||
group = static_cast<LayerGroup*>(layer);
|
||||
group = layer;
|
||||
// The user is trying to drop layers into an empty group, so there is no after
|
||||
// nor before layer...
|
||||
if (group->layersCount() == 0) {
|
||||
|
@ -79,7 +79,7 @@ void DocApi::dropDocumentsOnTimeline(app::Doc* destDoc,
|
|||
// inserted after or before it.
|
||||
Layer* refLayer = nullptr;
|
||||
// Parent group of the reference layer layer.
|
||||
LayerGroup* group = nullptr;
|
||||
Layer* group = nullptr;
|
||||
// Keep track of the current insertion point.
|
||||
setup_insertion_layer(destDoc, layerIndex, insert, refLayer, group);
|
||||
|
||||
|
|
|
@ -49,11 +49,11 @@ static void move_or_copy_cels(DocApi& api,
|
|||
auto dstFrameEnd = dstFrames.end();
|
||||
|
||||
for (; srcFrame != srcFrameEnd && dstFrame != dstFrameEnd; ++srcFrame, ++dstFrame) {
|
||||
if (i >= 0 && i < srcLayers.size() && srcLayers[i]->isImage()) {
|
||||
LayerImage* srcLayer = static_cast<LayerImage*>(srcLayers[i]);
|
||||
if (i >= 0 && i < srcLayers.size()) {
|
||||
Layer* srcLayer = srcLayers[i];
|
||||
|
||||
if (i < dstLayers.size() && dstLayers[i]->isImage()) {
|
||||
LayerImage* dstLayer = static_cast<LayerImage*>(dstLayers[i]);
|
||||
if (i < dstLayers.size()) {
|
||||
Layer* dstLayer = dstLayers[i];
|
||||
|
||||
#ifdef TRACE_RANGE_OPS
|
||||
std::clog << (op == Move ? "Moving" : "Copying") << " cel " << srcLayer->name() << "["
|
||||
|
@ -186,12 +186,12 @@ static DocRange move_or_copy_frames(DocApi& api,
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool has_child(LayerGroup* parent, Layer* child)
|
||||
static bool has_child(Layer* parent, Layer* child)
|
||||
{
|
||||
for (auto c : parent->layers()) {
|
||||
if (c == child)
|
||||
return true;
|
||||
else if (c->isGroup() && has_child(static_cast<LayerGroup*>(c), child))
|
||||
else if (c->isGroup() && has_child(c, child))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -205,11 +205,11 @@ static DocRange drop_range_op(Doc* doc,
|
|||
DocRange to)
|
||||
{
|
||||
// Convert "first child" operation into a insert after last child.
|
||||
LayerGroup* parent = nullptr;
|
||||
Layer* parent = nullptr;
|
||||
if (to.type() == DocRange::kLayers && !to.selectedLayers().empty()) {
|
||||
if (place == kDocRangeFirstChild && (*to.selectedLayers().begin())->isGroup()) {
|
||||
if (place == kDocRangeFirstChild) {
|
||||
place = kDocRangeAfter;
|
||||
parent = static_cast<LayerGroup*>((*to.selectedLayers().begin()));
|
||||
parent = *to.selectedLayers().begin();
|
||||
|
||||
to.clearRange();
|
||||
to.startRange(parent->lastLayer(), -1, DocRange::kLayers);
|
||||
|
@ -221,8 +221,7 @@ static DocRange drop_range_op(Doc* doc,
|
|||
|
||||
// Check that we're not moving a group inside itself
|
||||
for (auto moveThis : from.selectedLayers()) {
|
||||
if (moveThis == parent ||
|
||||
(moveThis->isGroup() && has_child(static_cast<LayerGroup*>(moveThis), parent)))
|
||||
if (moveThis == parent || (moveThis->isGroup() && has_child(moveThis, parent)))
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
@ -498,17 +497,13 @@ void reverse_frames(Doc* doc, const DocRange& range)
|
|||
}
|
||||
else if (swapCels) {
|
||||
for (Layer* layer : layers) {
|
||||
if (!layer->isImage())
|
||||
continue;
|
||||
|
||||
for (frame_t frame = frameBegin, frameRev = frameEnd;
|
||||
frame != (frameBegin + frameEnd) / 2 + 1;
|
||||
++frame, --frameRev) {
|
||||
if (frame == frameRev)
|
||||
continue;
|
||||
|
||||
LayerImage* imageLayer = static_cast<LayerImage*>(layer);
|
||||
api.swapCel(imageLayer, frame, frameRev);
|
||||
api.swapCel(layer, frame, frameRev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -644,7 +644,7 @@ static void ase_file_write_layers(FILE* f,
|
|||
ase_file_write_user_data_chunk(f, fop, frame_header, ext_files, &layer->userData());
|
||||
|
||||
if (layer->isGroup()) {
|
||||
for (const Layer* child : static_cast<const LayerGroup*>(layer)->layers())
|
||||
for (const Layer* child : layer->layers())
|
||||
ase_file_write_layers(f, fop, header, frame_header, ext_files, child, child_index + 1);
|
||||
}
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ static layer_t ase_file_write_cels(FILE* f,
|
|||
++layer_index;
|
||||
|
||||
if (layer->isGroup()) {
|
||||
for (const Layer* child : static_cast<const LayerGroup*>(layer)->layers()) {
|
||||
for (const Layer* child : layer->layers()) {
|
||||
layer_index =
|
||||
ase_file_write_cels(f, fop, frame_header, ext_files, sprite, child, layer_index, frame);
|
||||
}
|
||||
|
@ -1449,7 +1449,7 @@ static void ase_file_write_external_files_chunk(FILE* f,
|
|||
|
||||
putExtentionIds(layer->userData().propertiesMaps(), ext_files);
|
||||
if (layer->isGroup()) {
|
||||
auto childLayers = static_cast<const LayerGroup*>(layer)->layers();
|
||||
auto childLayers = layer->layers();
|
||||
layers.insert(layers.end(), childLayers.begin(), childLayers.end());
|
||||
}
|
||||
else if (layer->isImage()) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -69,7 +69,7 @@ static bool has_cels(const Layer* layer, frame_t frame)
|
|||
case ObjectType::LayerImage: return (layer->cel(frame) ? true : false);
|
||||
|
||||
case ObjectType::LayerGroup: {
|
||||
for (const Layer* child : static_cast<const LayerGroup*>(layer)->layers()) {
|
||||
for (const Layer* child : layer->layers()) {
|
||||
if (has_cels(child, frame))
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -39,7 +40,7 @@ void RestoreVisibleLayers::showSelectedLayers(Sprite* sprite, const SelectedLaye
|
|||
setLayerVisiblity(sprite->root(), selLayers);
|
||||
}
|
||||
|
||||
void RestoreVisibleLayers::setLayerVisiblity(LayerGroup* group, const SelectedLayers& selLayers)
|
||||
void RestoreVisibleLayers::setLayerVisiblity(Layer* group, const SelectedLayers& selLayers)
|
||||
{
|
||||
for (Layer* layer : group->layers()) {
|
||||
bool selected = (selLayers.contains(layer));
|
||||
|
@ -48,7 +49,7 @@ void RestoreVisibleLayers::setLayerVisiblity(LayerGroup* group, const SelectedLa
|
|||
layer->setVisible(selected);
|
||||
}
|
||||
if (selected && layer->isGroup())
|
||||
setLayerVisiblity(static_cast<LayerGroup*>(layer), selLayers);
|
||||
setLayerVisiblity(layer, selLayers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
// Copyright (C) 2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -27,7 +28,7 @@ public:
|
|||
void showSelectedLayers(doc::Sprite* sprite, const doc::SelectedLayers& selLayers);
|
||||
|
||||
private:
|
||||
void setLayerVisiblity(doc::LayerGroup* group, const doc::SelectedLayers& selLayers);
|
||||
void setLayerVisiblity(doc::Layer* group, const doc::SelectedLayers& selLayers);
|
||||
|
||||
std::vector<std::pair<doc::Layer*, bool>> m_restore;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -44,7 +44,6 @@ namespace doc {
|
|||
class Cel;
|
||||
class Image;
|
||||
class Layer;
|
||||
class LayerGroup;
|
||||
class Mask;
|
||||
class Palette;
|
||||
class Sprite;
|
||||
|
@ -151,7 +150,7 @@ void push_cels(lua_State* L, doc::Sprite* sprite);
|
|||
void push_color_space(lua_State* L, const gfx::ColorSpace& cs);
|
||||
void push_doc_range(lua_State* L, Site& site);
|
||||
void push_editor(lua_State* L, Editor* editor);
|
||||
void push_group_layers(lua_State* L, doc::LayerGroup* group);
|
||||
void push_group_layers(lua_State* L, doc::Layer* group);
|
||||
void push_image(lua_State* L, doc::Image* image);
|
||||
void push_layers(lua_State* L, const doc::ObjectIds& layers);
|
||||
void push_palette(lua_State* L, doc::Palette* palette);
|
||||
|
|
|
@ -82,7 +82,7 @@ int Layer_get_layers(lua_State* L)
|
|||
{
|
||||
auto layer = get_docobj<Layer>(L, 1);
|
||||
if (layer->isGroup())
|
||||
push_group_layers(L, static_cast<LayerGroup*>(layer));
|
||||
push_group_layers(L, layer);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -31,7 +31,7 @@ struct LayersObj {
|
|||
for (const Layer* layer : sprite->root()->layers())
|
||||
layers.push_back(layer->id());
|
||||
}
|
||||
LayersObj(LayerGroup* group)
|
||||
LayersObj(Layer* group)
|
||||
{
|
||||
for (const Layer* layer : group->layers())
|
||||
layers.push_back(layer->id());
|
||||
|
@ -102,7 +102,7 @@ void push_sprite_layers(lua_State* L, Sprite* sprite)
|
|||
push_new<LayersObj>(L, sprite);
|
||||
}
|
||||
|
||||
void push_group_layers(lua_State* L, LayerGroup* group)
|
||||
void push_group_layers(lua_State* L, Layer* group)
|
||||
{
|
||||
push_new<LayersObj>(L, group);
|
||||
}
|
||||
|
|
|
@ -455,16 +455,13 @@ int Sprite_deleteFrame(lua_State* L)
|
|||
int Sprite_newCel(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
auto layerBase = get_docobj<Layer>(L, 2);
|
||||
if (!layerBase->isImage())
|
||||
return luaL_error(L, "unexpected kind of layer in Sprite:newCel()");
|
||||
auto layer = get_docobj<Layer>(L, 2);
|
||||
|
||||
frame_t frame = get_frame_number_from_arg(L, 3);
|
||||
if (frame < 0 || frame > sprite->lastFrame())
|
||||
return luaL_error(L, "frame index out of bounds %d", frame + 1);
|
||||
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
LayerImage* layer = static_cast<LayerImage*>(layerBase);
|
||||
ImageRef image(nullptr);
|
||||
|
||||
Image* srcImage = may_get_image_from_arg(L, 4);
|
||||
|
@ -488,8 +485,11 @@ int Sprite_newCel(lua_State* L)
|
|||
else {
|
||||
if (srcImage)
|
||||
image.reset(Image::createCopy(srcImage));
|
||||
else
|
||||
else if (layer->isImage())
|
||||
image.reset(Image::create(sprite->spec()));
|
||||
else {
|
||||
// TODO copy any other kind of cel data
|
||||
}
|
||||
|
||||
cel = new Cel(frame, image);
|
||||
cel->setPosition(pos);
|
||||
|
|
|
@ -118,7 +118,7 @@ struct Timeline::DrawCelData {
|
|||
namespace {
|
||||
|
||||
template<typename Pred>
|
||||
void for_each_expanded_layer(LayerGroup* group,
|
||||
void for_each_expanded_layer(Layer* group,
|
||||
Pred&& pred,
|
||||
int level = 0,
|
||||
LayerFlags flags = LayerFlags(int(LayerFlags::Visible) |
|
||||
|
@ -131,11 +131,8 @@ void for_each_expanded_layer(LayerGroup* group,
|
|||
flags = static_cast<LayerFlags>(int(flags) & ~int(LayerFlags::Editable));
|
||||
|
||||
for (Layer* child : group->layers()) {
|
||||
if (child->isGroup() && !child->isCollapsed())
|
||||
for_each_expanded_layer<Pred>(static_cast<LayerGroup*>(child),
|
||||
std::forward<Pred>(pred),
|
||||
level + 1,
|
||||
flags);
|
||||
if (child->isExpanded())
|
||||
for_each_expanded_layer<Pred>(child, std::forward<Pred>(pred), level + 1, flags);
|
||||
|
||||
pred(child, level, flags);
|
||||
}
|
||||
|
@ -453,7 +450,7 @@ void Timeline::setLayer(Layer* layer)
|
|||
|
||||
// Expand all parents
|
||||
if (m_layer) {
|
||||
LayerGroup* group = m_layer->parent();
|
||||
Layer* group = m_layer->parent();
|
||||
while (group != m_layer->sprite()->root()) {
|
||||
// Expand this group
|
||||
group->setCollapsed(false);
|
||||
|
@ -1728,7 +1725,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||
continue;
|
||||
|
||||
Layer* layerPtr = getLayer(layer);
|
||||
if (!layerPtr || !layerPtr->isImage()) {
|
||||
if (!layerPtr) {
|
||||
// Draw empty cels
|
||||
for (frame = firstFrame; frame <= lastFrame; frame = col_t(frame + 1)) {
|
||||
drawCel(g, layer, frame, nullptr, nullptr);
|
||||
|
@ -1741,13 +1738,12 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||
|
||||
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >=
|
||||
// first_frame)
|
||||
LayerImage* layerImagePtr = static_cast<LayerImage*>(layerPtr);
|
||||
data.begin = layerImagePtr->getCelBegin();
|
||||
data.end = layerImagePtr->getCelEnd();
|
||||
data.begin = layerPtr->getCelBegin();
|
||||
data.end = layerPtr->getCelEnd();
|
||||
|
||||
const frame_t firstRealFrame(m_adapter->toRealFrame(firstFrame));
|
||||
const frame_t lastRealFrame(m_adapter->toRealFrame(lastFrame));
|
||||
data.it = layerImagePtr->findFirstCelIteratorAfter(firstRealFrame - 1);
|
||||
data.it = layerPtr->findFirstCelIteratorAfter(firstRealFrame - 1);
|
||||
|
||||
if (firstRealFrame > 0 && data.it != data.begin)
|
||||
data.prevIt = data.it - 1;
|
||||
|
@ -1760,33 +1756,34 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||
data.lastLink = data.end;
|
||||
|
||||
if (layerPtr == m_layer) {
|
||||
data.activeIt = layerImagePtr->findCelIterator(frame_t(realActiveFrame));
|
||||
data.activeIt = layerPtr->findCelIterator(frame_t(realActiveFrame));
|
||||
if (data.activeIt != data.end) {
|
||||
data.firstLink = data.activeIt;
|
||||
data.lastLink = data.activeIt;
|
||||
|
||||
ObjectId imageId = (*data.activeIt)->image()->id();
|
||||
// TODO impl an alternative way to find links (not only by image ID)
|
||||
if (ObjectId imageId = (*data.activeIt)->imageId()) {
|
||||
auto it2 = data.activeIt;
|
||||
if (it2 != data.begin) {
|
||||
do {
|
||||
--it2;
|
||||
if ((*it2)->imageId() == imageId) {
|
||||
data.firstLink = it2;
|
||||
if ((*it2)->frame() < firstRealFrame)
|
||||
break;
|
||||
}
|
||||
} while (it2 != data.begin);
|
||||
}
|
||||
|
||||
auto it2 = data.activeIt;
|
||||
if (it2 != data.begin) {
|
||||
do {
|
||||
--it2;
|
||||
if ((*it2)->image()->id() == imageId) {
|
||||
data.firstLink = it2;
|
||||
if ((*it2)->frame() < firstRealFrame)
|
||||
it2 = data.activeIt;
|
||||
while (it2 != data.end) {
|
||||
if ((*it2)->imageId() == imageId) {
|
||||
data.lastLink = it2;
|
||||
if ((*it2)->frame() > lastRealFrame)
|
||||
break;
|
||||
}
|
||||
} while (it2 != data.begin);
|
||||
}
|
||||
|
||||
it2 = data.activeIt;
|
||||
while (it2 != data.end) {
|
||||
if ((*it2)->image()->id() == imageId) {
|
||||
data.lastLink = it2;
|
||||
if ((*it2)->frame() > lastRealFrame)
|
||||
break;
|
||||
++it2;
|
||||
}
|
||||
++it2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2350,6 +2347,16 @@ void Timeline::drawLayer(ui::Graphics* g, const int layerIdx)
|
|||
(hotlayer && m_hot.part == PART_ROW_CONTINUOUS_ICON),
|
||||
(clklayer && m_clk.part == PART_ROW_CONTINUOUS_ICON));
|
||||
}
|
||||
// Just an empty box for other kind of layers
|
||||
else {
|
||||
drawPart(g,
|
||||
bounds,
|
||||
nullptr,
|
||||
styles.timelineBox(),
|
||||
is_active || (clklayer && m_clk.part == PART_ROW_CONTINUOUS_ICON),
|
||||
(hotlayer && m_hot.part == PART_ROW_CONTINUOUS_ICON),
|
||||
(clklayer && m_clk.part == PART_ROW_CONTINUOUS_ICON));
|
||||
}
|
||||
|
||||
// Get the layer's name bounds.
|
||||
bounds = getPartBounds(Hit(PART_ROW_TEXT, layerIdx));
|
||||
|
@ -2448,7 +2455,7 @@ void Timeline::drawCel(ui::Graphics* g,
|
|||
bool is_hover = (m_hot.part == PART_CEL && m_hot.layer == layerIndex && m_hot.frame == col);
|
||||
const bool is_active = isCelActive(layerIndex, col);
|
||||
const bool is_loosely_active = isCelLooselyActive(layerIndex, col);
|
||||
const bool is_empty = (image == nullptr);
|
||||
const bool is_empty = (cel == nullptr);
|
||||
gfx::Rect bounds = getPartBounds(Hit(PART_CEL, layerIndex, col));
|
||||
gfx::Rect full_bounds = bounds;
|
||||
IntersectClip clip(g, bounds);
|
||||
|
@ -2503,10 +2510,8 @@ void Timeline::drawCel(ui::Graphics* g,
|
|||
if (right && right->frame() != frame + 1)
|
||||
right = nullptr;
|
||||
|
||||
ObjectId leftImg = (left ? left->image()->id() : 0);
|
||||
ObjectId rightImg = (right ? right->image()->id() : 0);
|
||||
fromLeft = (leftImg == cel->image()->id());
|
||||
fromRight = (rightImg == cel->image()->id());
|
||||
fromLeft = (left && left->image() && left->imageId() == cel->imageId());
|
||||
fromRight = (right && right->image() && right->imageId() == cel->imageId());
|
||||
|
||||
if (fromLeft && fromRight)
|
||||
style = styles.timelineFromBoth();
|
||||
|
@ -2638,25 +2643,27 @@ void Timeline::drawCelLinkDecorators(ui::Graphics* g,
|
|||
const DrawCelData* data)
|
||||
{
|
||||
auto& styles = skinTheme()->styles;
|
||||
ObjectId imageId = (*data->activeIt)->image()->id();
|
||||
ObjectId imageId = (*data->activeIt)->imageId();
|
||||
|
||||
ui::Style* style1 = nullptr;
|
||||
ui::Style* style2 = nullptr;
|
||||
|
||||
// Links at the left or right side
|
||||
fr_t frame = m_adapter->toRealFrame(col);
|
||||
bool left = (data->firstLink != data->end ? frame > (*data->firstLink)->frame() : false);
|
||||
bool right = (data->lastLink != data->end ? frame < (*data->lastLink)->frame() : false);
|
||||
bool left = (imageId && data->firstLink != data->end ? frame > (*data->firstLink)->frame() :
|
||||
false);
|
||||
bool right = (imageId && data->lastLink != data->end ? frame < (*data->lastLink)->frame() :
|
||||
false);
|
||||
|
||||
if (cel && cel->image()->id() == imageId) {
|
||||
if (cel && cel->imageId() == imageId) {
|
||||
if (left) {
|
||||
Cel* prevCel = m_layer->cel(cel->frame() - 1);
|
||||
if (!prevCel || prevCel->image()->id() != imageId)
|
||||
if (!prevCel || prevCel->imageId() != imageId)
|
||||
style1 = styles.timelineLeftLink();
|
||||
}
|
||||
if (right) {
|
||||
Cel* nextCel = m_layer->cel(cel->frame() + 1);
|
||||
if (!nextCel || nextCel->image()->id() != imageId)
|
||||
if (!nextCel || nextCel->imageId() != imageId)
|
||||
style2 = styles.timelineRightLink();
|
||||
}
|
||||
}
|
||||
|
@ -4264,10 +4271,13 @@ void Timeline::updateDropRange(const gfx::Point& pt)
|
|||
m_dropTarget.vhit = DropTarget::VeryBottom;
|
||||
else if (pt.y < bounds.y + bounds.h / 2)
|
||||
m_dropTarget.vhit = DropTarget::Top;
|
||||
// Special drop target for expanded groups
|
||||
else if (m_range.type() == Range::kLayers && m_hot.layer >= 0 &&
|
||||
m_hot.layer < int(m_rows.size()) && m_rows[m_hot.layer].layer()->isGroup() &&
|
||||
static_cast<LayerGroup*>(m_rows[m_hot.layer].layer())->isExpanded()) {
|
||||
m_hot.layer < int(m_rows.size()) &&
|
||||
// Special drop target for expanded groups
|
||||
((m_rows[m_hot.layer].layer()->isGroup() && m_rows[m_hot.layer].layer()->isExpanded()) ||
|
||||
// Special drop for mask/fx inside layers
|
||||
(m_rows[m_clk.layer].layer()->isMaskOrFx() &&
|
||||
m_rows[m_hot.layer].layer()->acceptMaskOrFx()))) {
|
||||
m_dropTarget.vhit = DropTarget::FirstChild;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -302,38 +302,49 @@ Cel* create_cel_copy(CmdSequence* cmds,
|
|||
Layer* dstLayer,
|
||||
const frame_t dstFrame)
|
||||
{
|
||||
gfx::Size dstSize(0, 0);
|
||||
doc::PixelFormat dstPixelFormat = dstSprite->pixelFormat();
|
||||
|
||||
const Image* srcImage = srcCel->image();
|
||||
doc::PixelFormat dstPixelFormat = (dstLayer->isTilemap() ? IMAGE_TILEMAP :
|
||||
dstSprite->pixelFormat());
|
||||
gfx::Size dstSize(srcImage->width(), srcImage->height());
|
||||
if (srcImage) {
|
||||
if (dstLayer->isTilemap())
|
||||
dstPixelFormat = IMAGE_TILEMAP;
|
||||
dstSize = gfx::Size(srcImage->width(), srcImage->height());
|
||||
|
||||
// From Tilemap -> Image
|
||||
if (srcCel->layer()->isTilemap() && !dstLayer->isTilemap()) {
|
||||
auto layerTilemap = static_cast<doc::LayerTilemap*>(srcCel->layer());
|
||||
dstSize = layerTilemap->tileset()->grid().tilemapSizeToCanvas(dstSize);
|
||||
}
|
||||
// From Image or Tilemap -> Tilemap
|
||||
else if (dstLayer->isTilemap()) {
|
||||
auto dstLayerTilemap = static_cast<doc::LayerTilemap*>(dstLayer);
|
||||
// From Tilemap -> Image
|
||||
if (srcCel->layer()->isTilemap() && !dstLayer->isTilemap()) {
|
||||
auto layerTilemap = static_cast<doc::LayerTilemap*>(srcCel->layer());
|
||||
dstSize = layerTilemap->tileset()->grid().tilemapSizeToCanvas(dstSize);
|
||||
}
|
||||
// From Image or Tilemap -> Tilemap
|
||||
else if (dstLayer->isTilemap()) {
|
||||
auto dstLayerTilemap = static_cast<doc::LayerTilemap*>(dstLayer);
|
||||
|
||||
Grid grid = dstLayerTilemap->tileset()->grid();
|
||||
Grid grid = dstLayerTilemap->tileset()->grid();
|
||||
|
||||
// Tilemap -> Tilemap
|
||||
if (srcCel->layer()->isTilemap())
|
||||
grid.origin(srcCel->position());
|
||||
// Tilemap -> Tilemap
|
||||
if (srcCel->layer()->isTilemap())
|
||||
grid.origin(srcCel->position());
|
||||
|
||||
const gfx::Rect tilemapBounds = grid.canvasToTile(srcCel->bounds());
|
||||
dstSize = tilemapBounds.size();
|
||||
const gfx::Rect tilemapBounds = grid.canvasToTile(srcCel->bounds());
|
||||
dstSize = tilemapBounds.size();
|
||||
}
|
||||
}
|
||||
|
||||
// New cel
|
||||
auto dstCel =
|
||||
std::make_unique<Cel>(dstFrame, ImageRef(Image::create(dstPixelFormat, dstSize.w, dstSize.h)));
|
||||
ImageRef dstImage;
|
||||
if (dstSize.w > 0 && dstSize.h > 0)
|
||||
dstImage.reset(Image::create(dstPixelFormat, dstSize.w, dstSize.h));
|
||||
|
||||
auto dstCel = std::make_unique<Cel>(dstFrame, dstImage);
|
||||
dstCel->setOpacity(srcCel->opacity());
|
||||
dstCel->copyNonsharedPropertiesFrom(srcCel);
|
||||
dstCel->data()->setUserData(srcCel->data()->userData());
|
||||
|
||||
if (!srcImage)
|
||||
return dstCel.release();
|
||||
// The following code is to copy the image between cels...
|
||||
|
||||
// Special case were we copy from a tilemap...
|
||||
if (srcCel->layer()->isTilemap()) {
|
||||
if (dstLayer->isTilemap()) {
|
||||
|
@ -341,7 +352,7 @@ Cel* create_cel_copy(CmdSequence* cmds,
|
|||
// Best case, copy a cel in the same layer (we have the same
|
||||
// tileset available, so we just copy the tilemap as it is).
|
||||
if (srcCel->layer() == dstLayer) {
|
||||
dstCel->image()->copy(srcImage, gfx::Clip(0, 0, srcImage->bounds()));
|
||||
dstImage->copy(srcImage, gfx::Clip(0, 0, srcImage->bounds()));
|
||||
}
|
||||
// Tilemap -> Tilemap (with different tilesets)
|
||||
else {
|
||||
|
|
|
@ -688,10 +688,7 @@ void Clipboard::paste(Context* ctx, const bool interactive, const gfx::Point* po
|
|||
Cel* srcCel = srcLayer->cel(srcFrame);
|
||||
|
||||
if (srcCel && srcCel->image()) {
|
||||
api.copyCel(static_cast<LayerImage*>(srcLayer),
|
||||
srcFrame,
|
||||
static_cast<LayerImage*>(dstLayer),
|
||||
dstFrame);
|
||||
api.copyCel(srcLayer, srcFrame, dstLayer, dstFrame);
|
||||
}
|
||||
else {
|
||||
if (Cel* dstCel = dstLayer->cel(dstFrame))
|
||||
|
@ -741,15 +738,8 @@ void Clipboard::paste(Context* ctx, const bool interactive, const gfx::Point* po
|
|||
auto srcLayer = *srcIt;
|
||||
auto dstLayer = *dstIt;
|
||||
|
||||
if (!srcLayer->isImage() || !dstLayer->isImage())
|
||||
continue;
|
||||
|
||||
Cel* cel = static_cast<LayerImage*>(srcLayer)->cel(srcFrame);
|
||||
if (cel && cel->image()) {
|
||||
api.copyCel(static_cast<LayerImage*>(srcLayer),
|
||||
srcFrame,
|
||||
static_cast<LayerImage*>(dstLayer),
|
||||
dstFrame);
|
||||
if (Cel* cel = srcLayer->cel(srcFrame)) {
|
||||
api.copyCel(srcLayer, srcFrame, dstLayer, dstFrame);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -18,6 +18,8 @@
|
|||
#include "app/cmd/copy_rect.h"
|
||||
#include "app/cmd/copy_region.h"
|
||||
#include "app/cmd/patch_cel.h"
|
||||
#include "app/cmd/set_cel_image.h"
|
||||
#include "app/cmd/set_cel_position.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "app/context.h"
|
||||
#include "app/doc.h"
|
||||
|
@ -220,7 +222,9 @@ void ExpandCelCanvas::commit()
|
|||
}
|
||||
|
||||
// Was the cel created in the start of the tool-loop?
|
||||
if (m_celCreated) {
|
||||
if ((m_celCreated) ||
|
||||
// Was the cel without an image when the tool-loop started?
|
||||
(m_dstImage && !m_celImage)) {
|
||||
ASSERT(m_cel);
|
||||
ASSERT(!m_celImage);
|
||||
|
||||
|
@ -230,7 +234,8 @@ void ExpandCelCanvas::commit()
|
|||
|
||||
if (previewSpecificLayerChanges()) {
|
||||
// We can temporary remove the cel.
|
||||
static_cast<LayerImage*>(m_layer)->removeCel(m_cel);
|
||||
if (m_celCreated)
|
||||
m_layer->removeCel(m_cel);
|
||||
|
||||
gfx::Rect trimBounds = getTrimDstImageBounds();
|
||||
if (!trimBounds.isEmpty()) {
|
||||
|
@ -251,16 +256,26 @@ void ExpandCelCanvas::commit()
|
|||
ImageRef newImage(trimDstImage(trimBounds));
|
||||
ASSERT(newImage);
|
||||
|
||||
m_cel->data()->setImage(newImage, m_layer);
|
||||
m_cel->setPosition(m_cel->position() + (m_layer->isTilemap() ?
|
||||
// TODO we should get the exact coordinate from
|
||||
// getTrimDstImageBounds()
|
||||
m_grid.tileToCanvas(trimBounds.origin()) :
|
||||
trimBounds.origin()));
|
||||
gfx::Point newPosition = (m_cel->position() +
|
||||
// TODO we should get the exact coordinate from
|
||||
// getTrimDstImageBounds()
|
||||
(m_layer->isTilemap() ?
|
||||
m_grid.tileToCanvas(trimBounds.origin()) :
|
||||
trimBounds.origin()));
|
||||
|
||||
if (m_celCreated) {
|
||||
m_cel->data()->setImage(newImage, m_layer);
|
||||
m_cel->setPosition(newPosition);
|
||||
}
|
||||
else {
|
||||
m_cmds->executeAndAdd(new cmd::SetCelImage(m_cel, newImage));
|
||||
m_cmds->executeAndAdd(new cmd::SetCelPosition(m_cel, newPosition.x, newPosition.y));
|
||||
}
|
||||
}
|
||||
|
||||
// And add the cel again in the layer.
|
||||
m_cmds->executeAndAdd(new cmd::AddCel(m_layer, m_cel));
|
||||
if (m_celCreated)
|
||||
m_cmds->executeAndAdd(new cmd::AddCel(m_layer, m_cel));
|
||||
}
|
||||
else {
|
||||
// Delete unused cel
|
||||
|
|
|
@ -610,7 +610,7 @@ doc::Layer* AsepriteDecoder::readLayerChunk(AsepriteHeader* header,
|
|||
else if (child_level > *current_level)
|
||||
static_cast<doc::LayerGroup*>(*previous_layer)->addLayer(layer);
|
||||
else if (child_level < *current_level) {
|
||||
doc::LayerGroup* parent = (*previous_layer)->parent();
|
||||
doc::Layer* parent = (*previous_layer)->parent();
|
||||
ASSERT(parent);
|
||||
if (parent) {
|
||||
int levels = (*current_level - child_level);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Aseprite Document Library
|
||||
# Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
# Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
# Copyright (C) 2001-2018 David Capello
|
||||
|
||||
if(WIN32)
|
||||
|
@ -47,9 +47,17 @@ add_library(doc-lib
|
|||
image_io.cpp
|
||||
image_iterators2.cpp
|
||||
layer.cpp
|
||||
layer_audio.cpp
|
||||
layer_fill.cpp
|
||||
layer_fx.cpp
|
||||
layer_hitbox.cpp
|
||||
layer_io.cpp
|
||||
layer_list.cpp
|
||||
layer_mask.cpp
|
||||
layer_subsprite.cpp
|
||||
layer_text.cpp
|
||||
layer_tilemap.cpp
|
||||
layer_vector.cpp
|
||||
mask.cpp
|
||||
mask_boundaries.cpp
|
||||
mask_io.cpp
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -147,7 +147,7 @@ std::size_t Cel::links() const
|
|||
return links;
|
||||
}
|
||||
|
||||
void Cel::setParentLayer(LayerImage* layer)
|
||||
void Cel::setParentLayer(Layer* layer)
|
||||
{
|
||||
m_layer = layer;
|
||||
fixupImage();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -21,9 +21,16 @@ namespace doc {
|
|||
|
||||
class Document;
|
||||
class Grid;
|
||||
class LayerImage;
|
||||
class Layer;
|
||||
class Sprite;
|
||||
|
||||
// Short for celluloid. Initially used to represent an image in a
|
||||
// specific layer/frame location, but it could refer any kind of data
|
||||
// for other layers in a specific frame (e.g. a block of audio).
|
||||
//
|
||||
// This means that the image of a cel could be nullptr for layers that
|
||||
// don't require it (e.g. an audio layer) or could be a rendered cache
|
||||
// of the layer content (e.g. a text layer).
|
||||
class Cel : public Object {
|
||||
public:
|
||||
Cel(frame_t frame, const ImageRef& image);
|
||||
|
@ -43,8 +50,9 @@ public:
|
|||
|
||||
gfx::Rect imageBounds() const { return m_data->imageBounds(); }
|
||||
|
||||
LayerImage* layer() const { return m_layer; }
|
||||
Layer* layer() const { return m_layer; }
|
||||
Image* image() const { return m_data->image(); }
|
||||
ObjectId imageId() const { return m_data->image() ? m_data->image()->id() : NullId; }
|
||||
ImageRef imageRef() const { return m_data->imageRef(); }
|
||||
CelData* data() const { return const_cast<CelData*>(m_data.get()); }
|
||||
CelDataRef dataRef() const { return m_data; }
|
||||
|
@ -55,7 +63,7 @@ public:
|
|||
|
||||
// You should change the frame only if the cel isn't member of a
|
||||
// layer. If the cel is already in a layer, you should use
|
||||
// LayerImage::moveCel() member function.
|
||||
// Layer::moveCel() member function.
|
||||
void setFrame(frame_t frame);
|
||||
void setDataRef(const CelDataRef& celData);
|
||||
void setPosition(int x, int y);
|
||||
|
@ -67,7 +75,7 @@ public:
|
|||
|
||||
virtual int getMemSize() const override { return sizeof(Cel) + m_data->getMemSize(); }
|
||||
|
||||
void setParentLayer(LayerImage* layer);
|
||||
void setParentLayer(Layer* layer);
|
||||
Grid grid() const;
|
||||
|
||||
// Copies properties that are not shared between linked cels
|
||||
|
@ -77,7 +85,7 @@ public:
|
|||
private:
|
||||
void fixupImage();
|
||||
|
||||
LayerImage* m_layer;
|
||||
Layer* m_layer;
|
||||
frame_t m_frame; // Frame position
|
||||
CelDataRef m_data;
|
||||
int m_zIndex = 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -44,8 +44,6 @@ CelData::~CelData()
|
|||
|
||||
void CelData::setImage(const ImageRef& image, Layer* layer)
|
||||
{
|
||||
ASSERT(image.get());
|
||||
|
||||
m_image = image;
|
||||
adjustBounds(layer);
|
||||
}
|
||||
|
@ -57,9 +55,40 @@ void CelData::setPosition(const gfx::Point& pos)
|
|||
m_boundsF->setOrigin(gfx::PointF(pos));
|
||||
}
|
||||
|
||||
void CelData::setBounds(const gfx::Rect& bounds)
|
||||
{
|
||||
#if _DEBUG
|
||||
if (m_image) {
|
||||
ASSERT(bounds.w > 0);
|
||||
ASSERT(bounds.h > 0);
|
||||
}
|
||||
#endif
|
||||
m_bounds = bounds;
|
||||
if (m_boundsF)
|
||||
*m_boundsF = gfx::RectF(bounds);
|
||||
}
|
||||
|
||||
void CelData::setBoundsF(const gfx::RectF& boundsF)
|
||||
{
|
||||
if (m_boundsF)
|
||||
*m_boundsF = boundsF;
|
||||
else
|
||||
m_boundsF = std::make_unique<gfx::RectF>(boundsF);
|
||||
|
||||
m_bounds = gfx::Rect(boundsF);
|
||||
if (m_bounds.w <= 0)
|
||||
m_bounds.w = 1;
|
||||
if (m_bounds.h <= 0)
|
||||
m_bounds.h = 1;
|
||||
}
|
||||
|
||||
void CelData::adjustBounds(Layer* layer)
|
||||
{
|
||||
ASSERT(m_image);
|
||||
if (!m_image) {
|
||||
m_bounds = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_image->pixelFormat() == IMAGE_TILEMAP) {
|
||||
Tileset* tileset = nullptr;
|
||||
if (layer && layer->isTilemap())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -31,6 +31,7 @@ public:
|
|||
const gfx::Rect& bounds() const { return m_bounds; }
|
||||
int opacity() const { return m_opacity; }
|
||||
Image* image() const { return const_cast<Image*>(m_image.get()); };
|
||||
ObjectId imageId() const { return m_image ? m_image->id() : NullId; };
|
||||
ImageRef imageRef() const { return m_image; }
|
||||
|
||||
// Returns a rectangle with the bounds of the image (width/height
|
||||
|
@ -39,7 +40,9 @@ public:
|
|||
// bounds).
|
||||
gfx::Rect imageBounds() const
|
||||
{
|
||||
return gfx::Rect(m_bounds.x, m_bounds.y, m_image->width(), m_image->height());
|
||||
if (m_image)
|
||||
return gfx::Rect(m_bounds.x, m_bounds.y, m_image->width(), m_image->height());
|
||||
return {};
|
||||
}
|
||||
|
||||
void setImage(const ImageRef& image, Layer* layer);
|
||||
|
@ -47,28 +50,8 @@ public:
|
|||
|
||||
void setOpacity(int opacity) { m_opacity = opacity; }
|
||||
|
||||
void setBounds(const gfx::Rect& bounds)
|
||||
{
|
||||
ASSERT(bounds.w > 0);
|
||||
ASSERT(bounds.h > 0);
|
||||
m_bounds = bounds;
|
||||
if (m_boundsF)
|
||||
*m_boundsF = gfx::RectF(bounds);
|
||||
}
|
||||
|
||||
void setBoundsF(const gfx::RectF& boundsF)
|
||||
{
|
||||
if (m_boundsF)
|
||||
*m_boundsF = boundsF;
|
||||
else
|
||||
m_boundsF = std::make_unique<gfx::RectF>(boundsF);
|
||||
|
||||
m_bounds = gfx::Rect(boundsF);
|
||||
if (m_bounds.w <= 0)
|
||||
m_bounds.w = 1;
|
||||
if (m_bounds.h <= 0)
|
||||
m_bounds.h = 1;
|
||||
}
|
||||
void setBounds(const gfx::Rect& bounds);
|
||||
void setBoundsF(const gfx::RectF& boundsF);
|
||||
|
||||
const gfx::RectF& boundsF() const
|
||||
{
|
||||
|
@ -81,8 +64,7 @@ public:
|
|||
|
||||
virtual int getMemSize() const override
|
||||
{
|
||||
ASSERT(m_image);
|
||||
return sizeof(CelData) + m_image->getMemSize();
|
||||
return sizeof(CelData) + (m_image ? m_image->getMemSize() : 0);
|
||||
}
|
||||
|
||||
void adjustBounds(Layer* layer);
|
||||
|
@ -97,7 +79,7 @@ private:
|
|||
mutable std::unique_ptr<gfx::RectF> m_boundsF;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<CelData> CelDataRef;
|
||||
using CelDataRef = std::shared_ptr<CelData>;
|
||||
|
||||
} // namespace doc
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ void write_celdata(std::ostream& os, const CelData* celdata)
|
|||
write32(os, celdata->bounds().w);
|
||||
write32(os, celdata->bounds().h);
|
||||
write8(os, celdata->opacity());
|
||||
write32(os, celdata->image()->id());
|
||||
write32(os, celdata->imageId());
|
||||
write_user_data(os, celdata->userData());
|
||||
|
||||
if (celdata->hasBoundsF()) { // Reference layer
|
||||
|
@ -78,9 +78,12 @@ CelData* read_celdata(std::istream& is,
|
|||
}
|
||||
}
|
||||
|
||||
ImageRef image(subObjects->getImageRef(imageId));
|
||||
if (!image)
|
||||
return nullptr;
|
||||
ImageRef image;
|
||||
if (imageId) {
|
||||
image = subObjects->getImageRef(imageId);
|
||||
if (!image)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<CelData> celdata(new CelData(image));
|
||||
celdata->setBounds(gfx::Rect(x, y, w, h));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2023 Igara Studio S.A.
|
||||
// Copyright (c) 2023-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -27,7 +27,7 @@ void write_cel(std::ostream& os, const Cel* cel)
|
|||
{
|
||||
write32(os, cel->id());
|
||||
write16(os, cel->frame());
|
||||
write32(os, cel->dataRef()->id());
|
||||
write32(os, cel->data() ? cel->data()->id() : 0);
|
||||
write16(os, uint16_t(int16_t(cel->zIndex())));
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,12 @@ Cel* read_cel(std::istream& is, SubObjectsIO* subObjects, bool setId)
|
|||
if (is.eof())
|
||||
zIndex = 0;
|
||||
|
||||
CelDataRef celData(subObjects->getCelDataRef(celDataId));
|
||||
if (!celData)
|
||||
return nullptr;
|
||||
CelDataRef celData;
|
||||
if (celDataId) {
|
||||
celData = subObjects->getCelDataRef(celDataId);
|
||||
if (!celData)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto cel = std::make_unique<Cel>(frame, celData);
|
||||
cel->setZIndex(zIndex);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -42,14 +42,12 @@ CelsRange::iterator::iterator(const Sprite* sprite,
|
|||
// Get first cel
|
||||
Layer* layer = sprite->root()->firstLayerInWholeHierarchy();
|
||||
while (layer && !m_cel) {
|
||||
if (layer->isImage()) {
|
||||
m_frameIterator = m_selFrames.begin();
|
||||
auto endFrame = m_selFrames.end();
|
||||
for (; m_frameIterator != endFrame; ++m_frameIterator) {
|
||||
m_cel = layer->cel(*m_frameIterator);
|
||||
if (m_cel)
|
||||
break;
|
||||
}
|
||||
m_frameIterator = m_selFrames.begin();
|
||||
auto endFrame = m_selFrames.end();
|
||||
for (; m_frameIterator != endFrame; ++m_frameIterator) {
|
||||
m_cel = layer->cel(*m_frameIterator);
|
||||
if (m_cel)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_cel)
|
||||
|
@ -72,21 +70,19 @@ CelsRange::iterator& CelsRange::iterator::operator++()
|
|||
Layer* layer = m_cel->layer();
|
||||
m_cel = nullptr;
|
||||
while (layer && !m_cel) {
|
||||
if (layer->isImage()) {
|
||||
for (; m_frameIterator != endFrame; ++m_frameIterator) {
|
||||
m_cel = layer->cel(*m_frameIterator);
|
||||
if (m_cel) {
|
||||
if (m_flags == CelsRange::UNIQUE) {
|
||||
if (m_visited.find(m_cel->data()->id()) == m_visited.end()) {
|
||||
m_visited.insert(m_cel->data()->id());
|
||||
break;
|
||||
}
|
||||
else
|
||||
m_cel = nullptr;
|
||||
for (; m_frameIterator != endFrame; ++m_frameIterator) {
|
||||
m_cel = layer->cel(*m_frameIterator);
|
||||
if (m_cel) {
|
||||
if (m_flags == CelsRange::UNIQUE) {
|
||||
if (m_visited.find(m_cel->data()->id()) == m_visited.end()) {
|
||||
m_visited.insert(m_cel->data()->id());
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
m_cel = nullptr;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,19 +32,29 @@ Layer::Layer(ObjectType type, Sprite* sprite)
|
|||
, m_blendmode(BlendMode::NORMAL)
|
||||
, m_opacity(255)
|
||||
{
|
||||
ASSERT(type == ObjectType::LayerImage || type == ObjectType::LayerGroup ||
|
||||
type == ObjectType::LayerTilemap);
|
||||
|
||||
setName("Layer");
|
||||
}
|
||||
|
||||
Layer::~Layer()
|
||||
{
|
||||
destroyAllCels();
|
||||
destroyAllLayers();
|
||||
}
|
||||
|
||||
int Layer::getMemSize() const
|
||||
{
|
||||
return sizeof(Layer);
|
||||
int size = sizeof(Layer);
|
||||
|
||||
for (const Cel* cel : m_cels) {
|
||||
if (cel->link()) // Skip link
|
||||
continue;
|
||||
size += cel->getMemSize();
|
||||
}
|
||||
|
||||
for (const Layer* layer : m_layers)
|
||||
size += layer->getMemSize();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Layer* Layer::getPrevious() const
|
||||
|
@ -78,14 +88,14 @@ Layer* Layer::getPreviousBrowsable() const
|
|||
{
|
||||
// Go to children
|
||||
if (isBrowsable())
|
||||
return static_cast<const LayerGroup*>(this)->lastLayer();
|
||||
return lastLayer();
|
||||
|
||||
// Go to previous layer
|
||||
if (Layer* prev = getPrevious())
|
||||
return prev;
|
||||
|
||||
// Go to previous layer in the parent
|
||||
LayerGroup* parent = this->parent();
|
||||
Layer* parent = this->parent();
|
||||
while (parent != sprite()->root() && !parent->getPrevious()) {
|
||||
parent = parent->parent();
|
||||
}
|
||||
|
@ -98,7 +108,7 @@ Layer* Layer::getNextBrowsable() const
|
|||
if (Layer* next = getNext()) {
|
||||
// Go to children
|
||||
while (next->isBrowsable()) {
|
||||
Layer* firstChild = static_cast<const LayerGroup*>(next)->firstLayer();
|
||||
Layer* firstChild = next->firstLayer();
|
||||
if (!firstChild)
|
||||
break;
|
||||
next = firstChild;
|
||||
|
@ -116,15 +126,15 @@ Layer* Layer::getNextBrowsable() const
|
|||
Layer* Layer::getPreviousInWholeHierarchy() const
|
||||
{
|
||||
// Go to children
|
||||
if (isGroup() && static_cast<const LayerGroup*>(this)->layersCount() > 0)
|
||||
return static_cast<const LayerGroup*>(this)->lastLayer();
|
||||
if (layersCount() > 0)
|
||||
return lastLayer();
|
||||
|
||||
// Go to previous layer
|
||||
if (Layer* prev = getPrevious())
|
||||
return prev;
|
||||
|
||||
// Go to previous layer in the parent
|
||||
LayerGroup* parent = this->parent();
|
||||
Layer* parent = this->parent();
|
||||
while (parent != sprite()->root() && !parent->getPrevious()) {
|
||||
parent = parent->parent();
|
||||
}
|
||||
|
@ -136,8 +146,8 @@ Layer* Layer::getNextInWholeHierarchy() const
|
|||
// Go to next layer
|
||||
if (Layer* next = getNext()) {
|
||||
// Go to children
|
||||
while (next->isGroup() && static_cast<const LayerGroup*>(next)->layersCount() > 0) {
|
||||
Layer* firstChild = static_cast<const LayerGroup*>(next)->firstLayer();
|
||||
while (next->hasSublayers() && next->layersCount() > 0) {
|
||||
Layer* firstChild = next->firstLayer();
|
||||
if (!firstChild)
|
||||
break;
|
||||
next = firstChild;
|
||||
|
@ -212,88 +222,49 @@ Grid Layer::grid() const
|
|||
|
||||
Cel* Layer::cel(frame_t frame) const
|
||||
{
|
||||
const CelConstIterator it = findCelIterator(frame);
|
||||
if (it != getCelEnd())
|
||||
return *it;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerImage class
|
||||
|
||||
LayerImage::LayerImage(ObjectType type, Sprite* sprite) : Layer(type, sprite)
|
||||
void Layer::destroyAllCels()
|
||||
{
|
||||
}
|
||||
|
||||
LayerImage::LayerImage(Sprite* sprite) : LayerImage(ObjectType::LayerImage, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerImage::~LayerImage()
|
||||
{
|
||||
destroyAllCels();
|
||||
}
|
||||
|
||||
int LayerImage::getMemSize() const
|
||||
{
|
||||
int size = sizeof(LayerImage);
|
||||
CelConstIterator it = getCelBegin();
|
||||
CelConstIterator end = getCelEnd();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
const Cel* cel = *it;
|
||||
|
||||
if (cel->link()) // Skip link
|
||||
continue;
|
||||
|
||||
size += cel->getMemSize();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void LayerImage::destroyAllCels()
|
||||
{
|
||||
CelIterator it = getCelBegin();
|
||||
CelIterator end = getCelEnd();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
Cel* cel = *it;
|
||||
for (Cel* cel : m_cels)
|
||||
delete cel;
|
||||
}
|
||||
m_cels.clear();
|
||||
}
|
||||
|
||||
Cel* LayerImage::cel(frame_t frame) const
|
||||
void Layer::destroyAllLayers()
|
||||
{
|
||||
CelConstIterator it = findCelIterator(frame);
|
||||
if (it != getCelEnd())
|
||||
return *it;
|
||||
else
|
||||
return nullptr;
|
||||
for (Layer* layer : m_layers)
|
||||
delete layer;
|
||||
m_layers.clear();
|
||||
}
|
||||
|
||||
void LayerImage::getCels(CelList& cels) const
|
||||
void Layer::getCels(CelList& cels) const
|
||||
{
|
||||
CelConstIterator it = getCelBegin();
|
||||
CelConstIterator end = getCelEnd();
|
||||
for (Cel* cel : m_cels)
|
||||
cels.push_back(cel);
|
||||
|
||||
for (; it != end; ++it)
|
||||
cels.push_back(*it);
|
||||
for (const Layer* layer : m_layers)
|
||||
layer->getCels(cels);
|
||||
}
|
||||
|
||||
Cel* LayerImage::getLastCel() const
|
||||
Cel* Layer::getLastCel() const
|
||||
{
|
||||
if (!m_cels.empty())
|
||||
return m_cels.back();
|
||||
else
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CelConstIterator LayerImage::findCelIterator(frame_t frame) const
|
||||
CelConstIterator Layer::findCelIterator(frame_t frame) const
|
||||
{
|
||||
CelIterator it = const_cast<LayerImage*>(this)->findCelIterator(frame);
|
||||
const CelIterator it = const_cast<Layer*>(this)->findCelIterator(frame);
|
||||
return CelConstIterator(it);
|
||||
}
|
||||
|
||||
CelIterator LayerImage::findCelIterator(frame_t frame)
|
||||
CelIterator Layer::findCelIterator(frame_t frame)
|
||||
{
|
||||
auto first = getCelBegin();
|
||||
auto end = getCelEnd();
|
||||
|
@ -310,7 +281,7 @@ CelIterator LayerImage::findCelIterator(frame_t frame)
|
|||
return end;
|
||||
}
|
||||
|
||||
CelIterator LayerImage::findFirstCelIteratorAfter(frame_t firstAfterFrame)
|
||||
CelIterator Layer::findFirstCelIteratorAfter(frame_t firstAfterFrame)
|
||||
{
|
||||
auto first = getCelBegin();
|
||||
auto end = getCelEnd();
|
||||
|
@ -323,14 +294,11 @@ CelIterator LayerImage::findFirstCelIteratorAfter(frame_t firstAfterFrame)
|
|||
return first;
|
||||
}
|
||||
|
||||
void LayerImage::addCel(Cel* cel)
|
||||
void Layer::addCel(Cel* cel)
|
||||
{
|
||||
ASSERT(cel);
|
||||
ASSERT(cel->data() && "The cel doesn't contain CelData");
|
||||
ASSERT(cel->image());
|
||||
ASSERT(sprite());
|
||||
ASSERT(cel->image()->pixelFormat() == sprite()->pixelFormat() ||
|
||||
cel->image()->pixelFormat() == IMAGE_TILEMAP);
|
||||
|
||||
CelIterator it = findFirstCelIteratorAfter(cel->frame());
|
||||
m_cels.insert(it, cel);
|
||||
|
@ -344,7 +312,7 @@ void LayerImage::addCel(Cel* cel)
|
|||
* It doesn't destroy the cel, you have to delete it after calling
|
||||
* this routine.
|
||||
*/
|
||||
void LayerImage::removeCel(Cel* cel)
|
||||
void Layer::removeCel(Cel* cel)
|
||||
{
|
||||
ASSERT(cel);
|
||||
CelIterator it = findCelIterator(cel->frame());
|
||||
|
@ -355,7 +323,7 @@ void LayerImage::removeCel(Cel* cel)
|
|||
cel->setParentLayer(NULL);
|
||||
}
|
||||
|
||||
void LayerImage::moveCel(Cel* cel, frame_t frame)
|
||||
void Layer::moveCel(Cel* cel, frame_t frame)
|
||||
{
|
||||
removeCel(cel);
|
||||
cel->setFrame(frame);
|
||||
|
@ -363,6 +331,221 @@ void LayerImage::moveCel(Cel* cel, frame_t frame)
|
|||
addCel(cel);
|
||||
}
|
||||
|
||||
void Layer::displaceFrames(frame_t fromThis, frame_t delta)
|
||||
{
|
||||
Sprite* sprite = this->sprite();
|
||||
|
||||
if (delta > 0) {
|
||||
for (frame_t c = sprite->lastFrame(); c >= fromThis; --c) {
|
||||
if (Cel* cel = this->cel(c))
|
||||
moveCel(cel, c + delta);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (frame_t c = fromThis; c <= sprite->lastFrame(); ++c) {
|
||||
if (Cel* cel = this->cel(c))
|
||||
moveCel(cel, c + delta);
|
||||
}
|
||||
}
|
||||
|
||||
for (Layer* layer : m_layers)
|
||||
layer->displaceFrames(fromThis, delta);
|
||||
}
|
||||
|
||||
Layer* Layer::firstLayerInWholeHierarchy() const
|
||||
{
|
||||
Layer* layer = firstLayer();
|
||||
if (layer) {
|
||||
while (layer->layersCount() > 0)
|
||||
layer = layer->firstLayer();
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
void Layer::allLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->hasSublayers())
|
||||
child->allLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
layer_t Layer::allLayersCount() const
|
||||
{
|
||||
layer_t count = 0;
|
||||
for (Layer* child : m_layers) {
|
||||
count += child->allLayersCount();
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool Layer::hasVisibleReferenceLayers() const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if ((child->isReference() && child->isVisible()) ||
|
||||
(child->hasSublayers() && child->hasVisibleReferenceLayers()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Layer::allVisibleLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
if (child->hasSublayers())
|
||||
child->allVisibleLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::allVisibleReferenceLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
if (child->hasSublayers())
|
||||
child->allVisibleReferenceLayers(list);
|
||||
|
||||
if (!child->isReference())
|
||||
continue;
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::allBrowsableLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->isBrowsable())
|
||||
child->allBrowsableLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Layer::allTilemaps(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->hasSublayers())
|
||||
child->allTilemaps(list);
|
||||
|
||||
if (child->isTilemap())
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const
|
||||
{
|
||||
std::string str;
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
str += indent + child->name() + (child->isGroup() ? "/" : "") + "\n";
|
||||
if (child->hasSublayers())
|
||||
str += child->visibleLayerHierarchyAsString(indent + " ");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void Layer::addLayer(Layer* layer)
|
||||
{
|
||||
m_layers.push_back(layer);
|
||||
|
||||
layer->setParent(this);
|
||||
if (!isGroup())
|
||||
setCollapsed(m_layers.empty());
|
||||
}
|
||||
|
||||
void Layer::removeLayer(Layer* layer)
|
||||
{
|
||||
auto it = std::find(m_layers.begin(), m_layers.end(), layer);
|
||||
ASSERT(it != m_layers.end());
|
||||
m_layers.erase(it);
|
||||
|
||||
layer->setParent(nullptr);
|
||||
if (!isGroup())
|
||||
setCollapsed(m_layers.empty());
|
||||
}
|
||||
|
||||
void Layer::insertLayer(Layer* layer, Layer* after)
|
||||
{
|
||||
auto after_it = m_layers.begin();
|
||||
if (after) {
|
||||
after_it = std::find(m_layers.begin(), m_layers.end(), after);
|
||||
if (after_it != m_layers.end())
|
||||
++after_it;
|
||||
}
|
||||
m_layers.insert(after_it, layer);
|
||||
|
||||
layer->setParent(this);
|
||||
if (!isGroup())
|
||||
setCollapsed(m_layers.empty());
|
||||
}
|
||||
|
||||
void Layer::insertLayerBefore(Layer* layer, Layer* before)
|
||||
{
|
||||
auto before_it = m_layers.end();
|
||||
if (before) {
|
||||
before_it = std::find(m_layers.begin(), m_layers.end(), before);
|
||||
}
|
||||
m_layers.insert(before_it, layer);
|
||||
|
||||
layer->setParent(this);
|
||||
if (!isGroup())
|
||||
setCollapsed(m_layers.empty());
|
||||
}
|
||||
|
||||
void Layer::stackLayer(Layer* layer, Layer* after)
|
||||
{
|
||||
ASSERT(layer != after);
|
||||
if (layer == after)
|
||||
return;
|
||||
|
||||
removeLayer(layer);
|
||||
insertLayer(layer, after);
|
||||
}
|
||||
|
||||
layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const
|
||||
{
|
||||
for (Layer* child : this->layers()) {
|
||||
if ((child->hasSublayers() && child->getLayerIndex(layer, index) != -1) || (child == layer)) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
layer_t Layer::getLayerIndex(const Layer* layer) const
|
||||
{
|
||||
layer_t index = 0;
|
||||
return this->getLayerIndex(layer, index);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerImage class
|
||||
|
||||
LayerImage::LayerImage(ObjectType type, Sprite* sprite) : Layer(type, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerImage::LayerImage(Sprite* sprite) : LayerImage(ObjectType::LayerImage, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerImage::~LayerImage()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures some properties of the specified layer to make it as the
|
||||
* "Background" of the sprite.
|
||||
|
@ -381,24 +564,6 @@ void LayerImage::configureAsBackground()
|
|||
sprite()->root()->stackLayer(this, NULL);
|
||||
}
|
||||
|
||||
void LayerImage::displaceFrames(frame_t fromThis, frame_t delta)
|
||||
{
|
||||
Sprite* sprite = this->sprite();
|
||||
|
||||
if (delta > 0) {
|
||||
for (frame_t c = sprite->lastFrame(); c >= fromThis; --c) {
|
||||
if (Cel* cel = this->cel(c))
|
||||
moveCel(cel, c + delta);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (frame_t c = fromThis; c <= sprite->lastFrame(); ++c) {
|
||||
if (Cel* cel = this->cel(c))
|
||||
moveCel(cel, c + delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerGroup class
|
||||
|
||||
|
@ -409,210 +574,6 @@ LayerGroup::LayerGroup(Sprite* sprite) : Layer(ObjectType::LayerGroup, sprite)
|
|||
|
||||
LayerGroup::~LayerGroup()
|
||||
{
|
||||
destroyAllLayers();
|
||||
}
|
||||
|
||||
void LayerGroup::destroyAllLayers()
|
||||
{
|
||||
for (Layer* layer : m_layers)
|
||||
delete layer;
|
||||
m_layers.clear();
|
||||
}
|
||||
|
||||
int LayerGroup::getMemSize() const
|
||||
{
|
||||
int size = sizeof(LayerGroup);
|
||||
|
||||
for (const Layer* layer : m_layers) {
|
||||
size += layer->getMemSize();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Layer* LayerGroup::firstLayerInWholeHierarchy() const
|
||||
{
|
||||
Layer* layer = firstLayer();
|
||||
if (layer) {
|
||||
while (layer->isGroup() && static_cast<LayerGroup*>(layer)->layersCount() > 0) {
|
||||
layer = static_cast<LayerGroup*>(layer)->firstLayer();
|
||||
}
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
void LayerGroup::allLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->isGroup())
|
||||
static_cast<LayerGroup*>(child)->allLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
layer_t LayerGroup::allLayersCount() const
|
||||
{
|
||||
layer_t count = 0;
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->isGroup())
|
||||
count += static_cast<LayerGroup*>(child)->allLayersCount();
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool LayerGroup::hasVisibleReferenceLayers() const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if ((child->isReference() && child->isVisible()) ||
|
||||
(child->isGroup() && static_cast<LayerGroup*>(child)->hasVisibleReferenceLayers()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LayerGroup::allVisibleLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
if (child->isGroup())
|
||||
static_cast<LayerGroup*>(child)->allVisibleLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void LayerGroup::allVisibleReferenceLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
if (child->isGroup())
|
||||
static_cast<LayerGroup*>(child)->allVisibleReferenceLayers(list);
|
||||
|
||||
if (!child->isReference())
|
||||
continue;
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void LayerGroup::allBrowsableLayers(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->isBrowsable())
|
||||
static_cast<LayerGroup*>(child)->allBrowsableLayers(list);
|
||||
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
void LayerGroup::allTilemaps(LayerList& list) const
|
||||
{
|
||||
for (Layer* child : m_layers) {
|
||||
if (child->isGroup())
|
||||
static_cast<LayerGroup*>(child)->allTilemaps(list);
|
||||
|
||||
if (child->isTilemap())
|
||||
list.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
std::string LayerGroup::visibleLayerHierarchyAsString(const std::string& indent) const
|
||||
{
|
||||
std::string str;
|
||||
for (Layer* child : m_layers) {
|
||||
if (!child->isVisible())
|
||||
continue;
|
||||
|
||||
str += indent + child->name() + (child->isGroup() ? "/" : "") + "\n";
|
||||
if (child->isGroup())
|
||||
str += static_cast<LayerGroup*>(child)->visibleLayerHierarchyAsString(indent + " ");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void LayerGroup::getCels(CelList& cels) const
|
||||
{
|
||||
for (const Layer* layer : m_layers)
|
||||
layer->getCels(cels);
|
||||
}
|
||||
|
||||
void LayerGroup::addLayer(Layer* layer)
|
||||
{
|
||||
m_layers.push_back(layer);
|
||||
layer->setParent(this);
|
||||
}
|
||||
|
||||
void LayerGroup::removeLayer(Layer* layer)
|
||||
{
|
||||
auto it = std::find(m_layers.begin(), m_layers.end(), layer);
|
||||
ASSERT(it != m_layers.end());
|
||||
m_layers.erase(it);
|
||||
|
||||
layer->setParent(nullptr);
|
||||
}
|
||||
|
||||
void LayerGroup::insertLayer(Layer* layer, Layer* after)
|
||||
{
|
||||
auto after_it = m_layers.begin();
|
||||
if (after) {
|
||||
after_it = std::find(m_layers.begin(), m_layers.end(), after);
|
||||
if (after_it != m_layers.end())
|
||||
++after_it;
|
||||
}
|
||||
m_layers.insert(after_it, layer);
|
||||
|
||||
layer->setParent(this);
|
||||
}
|
||||
|
||||
void LayerGroup::insertLayerBefore(Layer* layer, Layer* before)
|
||||
{
|
||||
auto before_it = m_layers.end();
|
||||
if (before) {
|
||||
before_it = std::find(m_layers.begin(), m_layers.end(), before);
|
||||
}
|
||||
m_layers.insert(before_it, layer);
|
||||
|
||||
layer->setParent(this);
|
||||
}
|
||||
|
||||
void LayerGroup::stackLayer(Layer* layer, Layer* after)
|
||||
{
|
||||
ASSERT(layer != after);
|
||||
if (layer == after)
|
||||
return;
|
||||
|
||||
removeLayer(layer);
|
||||
insertLayer(layer, after);
|
||||
}
|
||||
|
||||
void LayerGroup::displaceFrames(frame_t fromThis, frame_t delta)
|
||||
{
|
||||
for (Layer* layer : m_layers)
|
||||
layer->displaceFrames(fromThis, delta);
|
||||
}
|
||||
|
||||
layer_t LayerGroup::getLayerIndex(const Layer* layer, layer_t& index) const
|
||||
{
|
||||
for (Layer* child : this->layers()) {
|
||||
if ((child->isGroup() && static_cast<LayerGroup*>(child)->getLayerIndex(layer, index) != -1) ||
|
||||
(child == layer)) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
layer_t LayerGroup::getLayerIndex(const Layer* layer) const
|
||||
{
|
||||
layer_t index = 0;
|
||||
return this->getLayerIndex(layer, index);
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
|
123
src/doc/layer.h
123
src/doc/layer.h
|
@ -55,9 +55,13 @@ enum class LayerFlags {
|
|||
|
||||
class Layer : public WithUserData {
|
||||
protected:
|
||||
// Only constructured by derived classes
|
||||
Layer(ObjectType type, Sprite* sprite);
|
||||
|
||||
public:
|
||||
// Disable assigment
|
||||
Layer& operator=(const Layer& other) = delete;
|
||||
|
||||
virtual ~Layer();
|
||||
|
||||
virtual int getMemSize() const override;
|
||||
|
@ -66,8 +70,8 @@ public:
|
|||
void setName(const std::string& name) { m_name = name; }
|
||||
|
||||
Sprite* sprite() const { return m_sprite; }
|
||||
LayerGroup* parent() const { return m_parent; }
|
||||
void setParent(LayerGroup* group) { m_parent = group; }
|
||||
Layer* parent() const { return m_parent; }
|
||||
void setParent(Layer* parent) { m_parent = parent; }
|
||||
|
||||
// Gets the previous sibling of this layer.
|
||||
Layer* getPrevious() const;
|
||||
|
@ -83,9 +87,22 @@ public:
|
|||
{
|
||||
return (type() == ObjectType::LayerImage || type() == ObjectType::LayerTilemap);
|
||||
}
|
||||
bool hasSublayers() const { return !m_layers.empty(); }
|
||||
bool isGroup() const { return type() == ObjectType::LayerGroup; }
|
||||
bool isTilemap() const { return type() == ObjectType::LayerTilemap; }
|
||||
virtual bool isBrowsable() const { return false; }
|
||||
bool isBrowsable() const { return isExpanded() && !m_layers.empty(); }
|
||||
// If this layer is a Mask or FX
|
||||
bool isMaskOrFx() const
|
||||
{
|
||||
return type() == ObjectType::LayerMask || type() == ObjectType::LayerFx;
|
||||
}
|
||||
// In case we can drop a Mask or FX inside this kind of layer
|
||||
bool acceptMaskOrFx() const
|
||||
{
|
||||
return type() == ObjectType::LayerImage || type() == ObjectType::LayerTilemap ||
|
||||
type() == ObjectType::LayerText || type() == ObjectType::LayerVector ||
|
||||
type() == ObjectType::LayerSubsprite;
|
||||
}
|
||||
|
||||
bool isBackground() const { return hasFlags(LayerFlags::Background); }
|
||||
bool isTransparent() const { return !hasFlags(LayerFlags::Background); }
|
||||
|
@ -143,71 +160,31 @@ public:
|
|||
}
|
||||
|
||||
virtual Grid grid() const;
|
||||
virtual Cel* cel(frame_t frame) const;
|
||||
virtual void getCels(CelList& cels) const = 0;
|
||||
virtual void displaceFrames(frame_t fromThis, frame_t delta) = 0;
|
||||
|
||||
private:
|
||||
std::string m_name; // layer name
|
||||
Sprite* m_sprite; // owner of the layer
|
||||
LayerGroup* m_parent; // parent layer
|
||||
LayerFlags m_flags; // stack order cannot be changed
|
||||
mutable base::Uuid m_uuid; // lazily generated layer's UUID
|
||||
|
||||
BlendMode m_blendmode;
|
||||
int m_opacity;
|
||||
|
||||
// Disable assigment
|
||||
Layer& operator=(const Layer& other);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerImage class
|
||||
|
||||
class LayerImage : public Layer {
|
||||
public:
|
||||
LayerImage(ObjectType type, Sprite* sprite);
|
||||
explicit LayerImage(Sprite* sprite);
|
||||
virtual ~LayerImage();
|
||||
|
||||
virtual int getMemSize() const override;
|
||||
// Cels management
|
||||
|
||||
void addCel(Cel* cel);
|
||||
void removeCel(Cel* cel);
|
||||
void moveCel(Cel* cel, frame_t frame);
|
||||
|
||||
Cel* cel(frame_t frame) const override;
|
||||
void getCels(CelList& cels) const override;
|
||||
void displaceFrames(frame_t fromThis, frame_t delta) override;
|
||||
Cel* cel(frame_t frame) const;
|
||||
const CelList& cels() const { return m_cels; }
|
||||
virtual void getCels(CelList& cels) const;
|
||||
virtual void displaceFrames(frame_t fromThis, frame_t delta);
|
||||
|
||||
Cel* getLastCel() const;
|
||||
CelConstIterator findCelIterator(frame_t frame) const;
|
||||
CelIterator findCelIterator(frame_t frame);
|
||||
CelIterator findFirstCelIteratorAfter(frame_t firstAfterFrame);
|
||||
|
||||
void configureAsBackground();
|
||||
|
||||
CelIterator getCelBegin() { return m_cels.begin(); }
|
||||
CelIterator getCelEnd() { return m_cels.end(); }
|
||||
CelConstIterator getCelBegin() const { return m_cels.begin(); }
|
||||
CelConstIterator getCelEnd() const { return m_cels.end(); }
|
||||
int getCelsCount() const { return (int)m_cels.size(); }
|
||||
|
||||
private:
|
||||
void destroyAllCels();
|
||||
|
||||
CelList m_cels; // List of all cels inside this layer used by frames.
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerGroup class
|
||||
|
||||
class LayerGroup : public Layer {
|
||||
public:
|
||||
explicit LayerGroup(Sprite* sprite);
|
||||
virtual ~LayerGroup();
|
||||
|
||||
virtual int getMemSize() const override;
|
||||
// Sublayers management. Groups accept any kind of
|
||||
// children. Image/rendered layers accept Mask or FXs.
|
||||
|
||||
const LayerList& layers() const { return m_layers; }
|
||||
int layersCount() const { return (int)m_layers.size(); }
|
||||
|
@ -231,21 +208,55 @@ public:
|
|||
void allTilemaps(LayerList& list) const;
|
||||
std::string visibleLayerHierarchyAsString(const std::string& indent) const;
|
||||
|
||||
void getCels(CelList& cels) const override;
|
||||
void displaceFrames(frame_t fromThis, frame_t delta) override;
|
||||
|
||||
bool isBrowsable() const override { return isGroup() && isExpanded() && !m_layers.empty(); }
|
||||
|
||||
layer_t getLayerIndex(const Layer* layer) const;
|
||||
|
||||
private:
|
||||
layer_t getLayerIndex(const Layer* layer, layer_t& index) const;
|
||||
void destroyAllCels();
|
||||
void destroyAllLayers();
|
||||
|
||||
layer_t getLayerIndex(const Layer* layer, layer_t& index) const;
|
||||
std::string m_name; // layer name
|
||||
Sprite* m_sprite; // owner of the layer
|
||||
Layer* m_parent; // parent layer
|
||||
LayerFlags m_flags; // stack order cannot be changed
|
||||
mutable base::Uuid m_uuid; // lazily generated layer's UUID
|
||||
|
||||
// Some of these fields might not be used depending on the layer
|
||||
// kind (e.g. the blend mode and opacity don't make sense for audio
|
||||
// layers).
|
||||
BlendMode m_blendmode;
|
||||
int m_opacity;
|
||||
|
||||
// List of all cels inside this layer used by frames.
|
||||
CelList m_cels;
|
||||
|
||||
// List of all layers inside this layer. For a group, this can be a
|
||||
// list of any kind. For other layers, they might be limited to
|
||||
// Masks and FXs.
|
||||
LayerList m_layers;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerImage class
|
||||
|
||||
class LayerImage : public Layer {
|
||||
public:
|
||||
LayerImage(ObjectType type, Sprite* sprite);
|
||||
explicit LayerImage(Sprite* sprite);
|
||||
virtual ~LayerImage();
|
||||
|
||||
void configureAsBackground();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LayerGroup class
|
||||
|
||||
class LayerGroup final : public Layer {
|
||||
public:
|
||||
explicit LayerGroup(Sprite* sprite);
|
||||
virtual ~LayerGroup();
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_audio.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerAudio::LayerAudio(Sprite* sprite) : Layer(ObjectType::LayerAudio, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerAudio::~LayerAudio()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,26 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_AUDIO_H_INCLUDED
|
||||
#define DOC_LAYER_AUDIO_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Starts playing audio at specific times.
|
||||
class LayerAudio final : public Layer {
|
||||
public:
|
||||
explicit LayerAudio(Sprite* sprite);
|
||||
virtual ~LayerAudio();
|
||||
|
||||
// TODO associated audio data from/to frame
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_fill.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerFill::LayerFill(Sprite* sprite) : Layer(ObjectType::LayerFill, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerFill::~LayerFill()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,27 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_FILL_H_INCLUDED
|
||||
#define DOC_LAYER_FILL_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Fills the whole canvas with a solid color or a gradient.
|
||||
// TODO to implement
|
||||
class LayerFill final : public Layer {
|
||||
public:
|
||||
LayerFill(Sprite* sprite);
|
||||
virtual ~LayerFill();
|
||||
|
||||
// TODO associated fill data per frame/keyframe
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_fx.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerFx::LayerFx(Sprite* sprite) : Layer(ObjectType::LayerFx, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerFx::~LayerFx()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,27 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_FX_H_INCLUDED
|
||||
#define DOC_LAYER_FX_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Applies an FX to the next non-FX Layer
|
||||
// TODO to implement
|
||||
class LayerFx final : public Layer {
|
||||
public:
|
||||
LayerFx(Sprite* sprite);
|
||||
virtual ~LayerFx();
|
||||
|
||||
// TODO associated FX data per frame/keyframe
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_hitbox.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerHitbox::LayerHitbox(Sprite* sprite) : Layer(ObjectType::LayerHitbox, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerHitbox::~LayerHitbox()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,28 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_HITBOX_H_INCLUDED
|
||||
#define DOC_LAYER_HITBOX_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Abstract vectorial shapes, data, and events that can be associated
|
||||
// to a specific frame. Generally with the purpose to define hitboxes
|
||||
// to be used in a game engine.
|
||||
class LayerHitbox final : public Layer {
|
||||
public:
|
||||
explicit LayerHitbox(Sprite* sprite);
|
||||
virtual ~LayerHitbox();
|
||||
|
||||
// TODO associated hitbox data
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -18,8 +18,16 @@
|
|||
#include "doc/cel_io.h"
|
||||
#include "doc/image_io.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_audio.h"
|
||||
#include "doc/layer_fill.h"
|
||||
#include "doc/layer_fx.h"
|
||||
#include "doc/layer_hitbox.h"
|
||||
#include "doc/layer_io.h"
|
||||
#include "doc/layer_mask.h"
|
||||
#include "doc/layer_subsprite.h"
|
||||
#include "doc/layer_text.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/layer_vector.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/string_io.h"
|
||||
#include "doc/subobjects_io.h"
|
||||
|
@ -46,14 +54,18 @@ void write_layer(std::ostream& os, const Layer* layer)
|
|||
|
||||
switch (layer->type()) {
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
const LayerImage* imgLayer = static_cast<const LayerImage*>(layer);
|
||||
CelConstIterator it, begin = imgLayer->getCelBegin();
|
||||
CelConstIterator end = imgLayer->getCelEnd();
|
||||
case ObjectType::LayerTilemap:
|
||||
case ObjectType::LayerText:
|
||||
case ObjectType::LayerVector:
|
||||
case ObjectType::LayerAudio:
|
||||
case ObjectType::LayerHitbox: {
|
||||
CelConstIterator it;
|
||||
const CelConstIterator begin = layer->getCelBegin();
|
||||
const CelConstIterator end = layer->getCelEnd();
|
||||
|
||||
// Blend mode & opacity
|
||||
write16(os, (int)imgLayer->blendMode());
|
||||
write8(os, imgLayer->opacity());
|
||||
write16(os, (int)layer->blendMode());
|
||||
write8(os, layer->opacity());
|
||||
|
||||
// Images
|
||||
int images = 0;
|
||||
|
@ -61,7 +73,8 @@ void write_layer(std::ostream& os, const Layer* layer)
|
|||
for (it = begin; it != end; ++it) {
|
||||
const Cel* cel = *it;
|
||||
if (!cel->link()) {
|
||||
++images;
|
||||
if (cel->image())
|
||||
++images;
|
||||
++celdatas;
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +82,7 @@ void write_layer(std::ostream& os, const Layer* layer)
|
|||
write16(os, images);
|
||||
for (it = begin; it != end; ++it) {
|
||||
const Cel* cel = *it;
|
||||
if (!cel->link())
|
||||
if (!cel->link() && cel->image())
|
||||
write_image(os, cel->image());
|
||||
}
|
||||
|
||||
|
@ -81,7 +94,7 @@ void write_layer(std::ostream& os, const Layer* layer)
|
|||
}
|
||||
|
||||
// Cels
|
||||
write16(os, imgLayer->getCelsCount());
|
||||
write16(os, layer->getCelsCount());
|
||||
for (it = begin; it != end; ++it) {
|
||||
const Cel* cel = *it;
|
||||
write_cel(os, cel);
|
||||
|
@ -119,60 +132,90 @@ Layer* read_layer(std::istream& is, SubObjectsFromSprite* subObjects, const Seri
|
|||
|
||||
switch (static_cast<ObjectType>(layer_type)) {
|
||||
case ObjectType::LayerImage:
|
||||
case ObjectType::LayerTilemap: {
|
||||
LayerImage* imgLayer;
|
||||
if ((static_cast<ObjectType>(layer_type)) == ObjectType::LayerTilemap) {
|
||||
imgLayer = new LayerTilemap(subObjects->sprite(), 0);
|
||||
}
|
||||
else {
|
||||
imgLayer = new LayerImage(subObjects->sprite());
|
||||
}
|
||||
|
||||
case ObjectType::LayerTilemap:
|
||||
case ObjectType::LayerFill:
|
||||
case ObjectType::LayerMask:
|
||||
case ObjectType::LayerFx:
|
||||
case ObjectType::LayerText:
|
||||
case ObjectType::LayerVector:
|
||||
case ObjectType::LayerAudio:
|
||||
case ObjectType::LayerSubsprite:
|
||||
case ObjectType::LayerHitbox: {
|
||||
// Create layer
|
||||
layer.reset(imgLayer);
|
||||
switch ((static_cast<ObjectType>(layer_type))) {
|
||||
case ObjectType::LayerImage:
|
||||
layer = std::make_unique<LayerImage>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerTilemap:
|
||||
layer = std::make_unique<LayerTilemap>(subObjects->sprite(), 0);
|
||||
break;
|
||||
case ObjectType::LayerFill:
|
||||
layer = std::make_unique<LayerFill>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerMask:
|
||||
layer = std::make_unique<LayerMask>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerFx: layer = std::make_unique<LayerFx>(subObjects->sprite()); break;
|
||||
case ObjectType::LayerText:
|
||||
layer = std::make_unique<LayerText>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerVector:
|
||||
layer = std::make_unique<LayerVector>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerAudio:
|
||||
layer = std::make_unique<LayerAudio>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerSubsprite:
|
||||
layer = std::make_unique<LayerSubsprite>(subObjects->sprite());
|
||||
break;
|
||||
case ObjectType::LayerHitbox:
|
||||
layer = std::make_unique<LayerHitbox>(subObjects->sprite());
|
||||
break;
|
||||
}
|
||||
|
||||
// Blend mode & opacity
|
||||
imgLayer->setBlendMode((BlendMode)read16(is));
|
||||
imgLayer->setOpacity(read8(is));
|
||||
layer->setBlendMode((BlendMode)read16(is));
|
||||
layer->setOpacity(read8(is));
|
||||
|
||||
// Read images
|
||||
int images = read16(is); // Number of images
|
||||
const int images = read16(is); // Number of images
|
||||
for (int c = 0; c < images; ++c) {
|
||||
ImageRef image(read_image(is));
|
||||
subObjects->addImageRef(image);
|
||||
}
|
||||
|
||||
// Read celdatas
|
||||
int celdatas = read16(is);
|
||||
const int celdatas = read16(is);
|
||||
for (int c = 0; c < celdatas; ++c) {
|
||||
CelDataRef celdata(read_celdata(is, subObjects, true, serial));
|
||||
subObjects->addCelDataRef(celdata);
|
||||
}
|
||||
|
||||
// Read cels
|
||||
int cels = read16(is); // Number of cels
|
||||
const int cels = read16(is); // Number of cels
|
||||
for (int c = 0; c < cels; ++c) {
|
||||
// Read the cel
|
||||
Cel* cel = read_cel(is, subObjects);
|
||||
ASSERT(cel);
|
||||
|
||||
// Add the cel in the layer
|
||||
imgLayer->addCel(cel);
|
||||
layer->addCel(cel);
|
||||
}
|
||||
|
||||
// Create the layer tilemap
|
||||
if (imgLayer->isTilemap()) {
|
||||
doc::tileset_index tsi = read32(is); // Tileset index
|
||||
static_cast<LayerTilemap*>(imgLayer)->setTilesetIndex(tsi);
|
||||
if (layer->isTilemap()) {
|
||||
const doc::tileset_index tsi = read32(is); // Tileset index
|
||||
static_cast<LayerTilemap*>(layer.get())->setTilesetIndex(tsi);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ObjectType::LayerGroup: {
|
||||
// Create the layer group
|
||||
layer.reset(new LayerGroup(subObjects->sprite()));
|
||||
layer = std::make_unique<LayerGroup>(subObjects->sprite());
|
||||
|
||||
// Number of sub-layers
|
||||
int layers = read16(is);
|
||||
const int layers = read16(is);
|
||||
for (int c = 0; c < layers; c++) {
|
||||
Layer* child = read_layer(is, subObjects, serial);
|
||||
if (child)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_mask.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerMask::LayerMask(Sprite* sprite) : LayerImage(ObjectType::LayerMask, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerMask::~LayerMask()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,25 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_MASK_H_INCLUDED
|
||||
#define DOC_LAYER_MASK_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Applies Cel images as a mask for the next layer.
|
||||
// TODO to implement
|
||||
class LayerMask final : public LayerImage {
|
||||
public:
|
||||
LayerMask(Sprite* sprite);
|
||||
virtual ~LayerMask();
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_subsprite.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerSubsprite::LayerSubsprite(Sprite* sprite) : Layer(ObjectType::LayerSubsprite, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerSubsprite::~LayerSubsprite()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,27 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_SUBSPRITE_H_INCLUDED
|
||||
#define DOC_LAYER_SUBSPRITE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Includes an subsprite (external or embedded)
|
||||
// TODO to implement
|
||||
class LayerSubsprite final : public Layer {
|
||||
public:
|
||||
LayerSubsprite(Sprite* sprite);
|
||||
virtual ~LayerSubsprite();
|
||||
|
||||
// TODO associated subsprite and bounds/rotation/transformation per frame/keyframe
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_text.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerText::LayerText(Sprite* sprite) : Layer(ObjectType::LayerText, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerText::~LayerText()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,27 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_TEXT_H_INCLUDED
|
||||
#define DOC_LAYER_TEXT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Dynamically renders a text for each cel.
|
||||
// TODO to implement
|
||||
class LayerText final : public Layer {
|
||||
public:
|
||||
LayerText(Sprite* sprite);
|
||||
virtual ~LayerText();
|
||||
|
||||
// TODO associated text/font/location per frame/keyframe
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
@ -14,9 +14,10 @@
|
|||
|
||||
namespace doc {
|
||||
|
||||
class LayerTilemap : public LayerImage {
|
||||
// Renders a tilemap on each cel using a tileset.
|
||||
class LayerTilemap final : public LayerImage {
|
||||
public:
|
||||
explicit LayerTilemap(Sprite* sprite, const tileset_index tsi);
|
||||
LayerTilemap(Sprite* sprite, tileset_index tsi);
|
||||
~LayerTilemap();
|
||||
|
||||
Grid grid() const override;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/layer_vector.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
LayerVector::LayerVector(Sprite* sprite) : Layer(ObjectType::LayerVector, sprite)
|
||||
{
|
||||
}
|
||||
|
||||
LayerVector::~LayerVector()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace doc
|
|
@ -0,0 +1,27 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_LAYER_VECTOR_H_INCLUDED
|
||||
#define DOC_LAYER_VECTOR_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/layer.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// Dynamically renders a path/vector for each cel.
|
||||
// TODO to implement
|
||||
class LayerVector final : public Layer {
|
||||
public:
|
||||
LayerVector(Sprite* sprite);
|
||||
virtual ~LayerVector();
|
||||
|
||||
// TODO associated path/vector data per frame/keyframe
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -9,9 +9,11 @@
|
|||
#define DOC_OBJECT_TYPE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/ints.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
enum class ObjectType {
|
||||
enum class ObjectType : uint16_t {
|
||||
Unknown = 0,
|
||||
Image = 1,
|
||||
Palette = 2,
|
||||
|
@ -35,6 +37,16 @@ enum class ObjectType {
|
|||
LayerTilemap = 14,
|
||||
Tileset = 15,
|
||||
Tilesets = 16,
|
||||
|
||||
LayerFill = 17,
|
||||
LayerMask = 18,
|
||||
LayerFx = 19,
|
||||
LayerText = 20,
|
||||
LayerVector = 21,
|
||||
LayerAudio = 22,
|
||||
LayerHitbox = 23,
|
||||
|
||||
LayerSubsprite = 24,
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite Document Library
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
|
@ -213,8 +213,10 @@ void Sprite::setSize(int width, int height)
|
|||
void Sprite::setColorSpace(const gfx::ColorSpaceRef& colorSpace)
|
||||
{
|
||||
m_spec.setColorSpace(colorSpace);
|
||||
for (auto cel : uniqueCels())
|
||||
cel->image()->setColorSpace(colorSpace);
|
||||
for (Cel* cel : uniqueCels()) {
|
||||
if (Image* img = cel->image())
|
||||
img->setColorSpace(colorSpace);
|
||||
}
|
||||
}
|
||||
|
||||
bool Sprite::isOpaque() const
|
||||
|
@ -301,7 +303,7 @@ Layer* Sprite::firstLayer() const
|
|||
{
|
||||
Layer* layer = root()->firstLayer();
|
||||
while (layer && layer->isGroup())
|
||||
layer = static_cast<LayerGroup*>(layer)->firstLayer();
|
||||
layer = layer->firstLayer();
|
||||
return layer;
|
||||
}
|
||||
|
||||
|
@ -309,7 +311,7 @@ Layer* Sprite::firstBrowsableLayer() const
|
|||
{
|
||||
Layer* layer = root()->firstLayer();
|
||||
while (layer->isBrowsable())
|
||||
layer = static_cast<LayerGroup*>(layer)->firstLayer();
|
||||
layer = layer->firstLayer();
|
||||
return layer;
|
||||
}
|
||||
|
||||
|
@ -532,7 +534,7 @@ void Sprite::setDurationForAllFrames(int msecs)
|
|||
ImageRef Sprite::getImageRef(ObjectId imageId)
|
||||
{
|
||||
for (Cel* cel : cels()) {
|
||||
if (cel->image()->id() == imageId)
|
||||
if (cel->imageId() == imageId)
|
||||
return cel->imageRef();
|
||||
}
|
||||
if (hasTilesets()) {
|
||||
|
@ -565,7 +567,7 @@ CelDataRef Sprite::getCelDataRef(ObjectId celDataId)
|
|||
void Sprite::replaceImage(ObjectId curImageId, const ImageRef& newImage)
|
||||
{
|
||||
for (Cel* cel : cels()) {
|
||||
if (cel->image()->id() == curImageId)
|
||||
if (cel->imageId() == curImageId)
|
||||
cel->data()->setImage(newImage, cel->layer());
|
||||
}
|
||||
|
||||
|
@ -604,7 +606,7 @@ void Sprite::replaceTileset(tileset_index tsi, Tileset* newTileset)
|
|||
void Sprite::getImages(std::vector<ImageRef>& images) const
|
||||
{
|
||||
for (Cel* cel : uniqueCels())
|
||||
if (cel->image()->pixelFormat() != IMAGE_TILEMAP)
|
||||
if (cel->image() && cel->image()->pixelFormat() != IMAGE_TILEMAP)
|
||||
images.push_back(cel->imageRef());
|
||||
|
||||
if (hasTilesets()) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite View Library
|
||||
// Copyright (c) 2020-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2020-2025 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
@ -24,7 +24,7 @@ Layer* candidate_if_layer_is_deleted(const Layer* selectedLayer, const Layer* la
|
|||
if ((selectedLayer == layerToDelete) ||
|
||||
(selectedLayer && selectedLayer->hasAncestor(layerToDelete))) {
|
||||
Sprite* sprite = selectedLayer->sprite();
|
||||
LayerGroup* parent = layerToDelete->parent();
|
||||
Layer* parent = layerToDelete->parent();
|
||||
|
||||
// Select previous layer, or next layer, or the parent (if it is
|
||||
// not the main layer of sprite set).
|
||||
|
|
Loading…
Reference in New Issue