Skip to content

Refactors needed for new layer types#5347

Merged
dacap merged 11 commits into
aseprite:betafrom
dacap:new-layers
Mar 2, 2026
Merged

Refactors needed for new layer types#5347
dacap merged 11 commits into
aseprite:betafrom
dacap:new-layers

Conversation

@dacap

@dacap dacap commented Aug 13, 2025

Copy link
Copy Markdown
Member

This is a draft for an initial refactor to start working in several new layer types:

With this PR we'll try to incorporate the required concept for these layers, not to implement all these kind of layers at once, but it will be a good starting point to continue working after this is merged.

@dacap dacap added the refactor complexity This issue needs some refactor / modifying several files / rethinking some part of the architecture label Aug 13, 2025

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/commands/cmd_new_layer.cpp Outdated
Comment thread src/doc/layer_tilemap.h Outdated
Comment thread src/doc/object_type.h

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/doc/layer.cpp Outdated
Comment thread src/doc/layer.cpp Outdated
Comment thread src/doc/layer.cpp Outdated
Comment thread src/doc/layer.cpp Outdated
@dacap dacap force-pushed the new-layers branch 2 times, most recently from cdc75c1 to 5211ba3 Compare August 14, 2025 17:35

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/cmd/add_cel.cpp Outdated
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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: redundant boolean literal in ternary expression result [readability-simplify-boolean-expr]

Suggested change
const bool has_data = (flags & 1 ? true : false);
const bool has_data = ((flags & 1) != 0);

Comment thread src/app/cmd/add_cel.cpp Outdated
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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: redundant boolean literal in ternary expression result [readability-simplify-boolean-expr]

Suggested change
const bool has_image = (flags & 2 ? true : false);
const bool has_image = ((flags & 2) != 0);

Comment thread src/app/cmd/add_cel.cpp Outdated
ImageRef image(read_image(m_stream));
io.addImageRef(image);
if (has_image) {
ImageRef image(read_image(m_stream));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'image' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef image(read_image(m_stream));
ImageRef const image(read_image(m_stream));

Comment thread src/app/cmd/set_cel_image.cpp Outdated
// 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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'oldImage' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef oldImage = sprite->getImageRef(m_oldImageId);
ImageRef const oldImage = sprite->getImageRef(m_oldImageId);

Comment thread src/app/ui/timeline/timeline.cpp Outdated
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());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'Cel *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
fromLeft = (left && left->image() && left->imageId() == cel->imageId());
fromLeft = ((left != nullptr) && left->image() && left->imageId() == cel->imageId());

Comment thread src/app/util/cel_ops.cpp

// From Tilemap -> Image
if (srcCel->layer()->isTilemap() && !dstLayer->isTilemap()) {
auto layerTilemap = static_cast<doc::LayerTilemap*>(srcCel->layer());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'auto layerTilemap' can be declared as 'auto *layerTilemap' [readability-qualified-auto]

Suggested change
auto layerTilemap = static_cast<doc::LayerTilemap*>(srcCel->layer());
auto *layerTilemap = static_cast<doc::LayerTilemap*>(srcCel->layer());

Comment thread src/app/util/cel_ops.cpp
}
// From Image or Tilemap -> Tilemap
else if (dstLayer->isTilemap()) {
auto dstLayerTilemap = static_cast<doc::LayerTilemap*>(dstLayer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'auto dstLayerTilemap' can be declared as 'auto *dstLayerTilemap' [readability-qualified-auto]

Suggested change
auto dstLayerTilemap = static_cast<doc::LayerTilemap*>(dstLayer);
auto *dstLayerTilemap = static_cast<doc::LayerTilemap*>(dstLayer);

// getTrimDstImageBounds()
m_grid.tileToCanvas(trimBounds.origin()) :
trimBounds.origin()));
gfx::Point newPosition = (m_cel->position() +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'newPosition' of type 'gfx::Point' (aka 'PointT') can be declared 'const' [misc-const-correctness]

Suggested change
gfx::Point newPosition = (m_cel->position() +
gfx::Point const newPosition = (m_cel->position() +

Comment thread src/doc/layer.cpp Outdated
cel->image()->pixelFormat() == IMAGE_TILEMAP);
#if _DEBUG
if (isTilemap()) {
ASSERT(!cel->image() || cel->image()->pixelFormat() == IMAGE_TILEMAP);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]

    ASSERT(!cel->image() || cel->image()->pixelFormat() == IMAGE_TILEMAP);
    ^
Additional context

laf/base/debug.h:67: expanded from macro 'ASSERT'

      if (!(condition)) {                                                                          \
          ^

Comment thread src/doc/layer.cpp Outdated
ASSERT(!cel->image() || cel->image()->pixelFormat() == IMAGE_TILEMAP);
}
else if (isImage()) {
ASSERT(!cel->image() || cel->image()->pixelFormat() == sprite()->pixelFormat());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: boolean expression can be simplified by DeMorgan's theorem [readability-simplify-boolean-expr]

    ASSERT(!cel->image() || cel->image()->pixelFormat() == sprite()->pixelFormat());
    ^
Additional context

laf/base/debug.h:67: expanded from macro 'ASSERT'

      if (!(condition)) {                                                                          \
          ^

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/doc.cpp Outdated
Comment thread src/doc/layer_io.cpp Outdated
@dacap dacap force-pushed the new-layers branch 2 times, most recently from de2632a to 0e7350d Compare August 22, 2025 12:47
@dacap dacap self-assigned this Sep 5, 2025

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/commands/cmd_new_layer.cpp Outdated
class NewLayerCommand : public CommandWithNewParams<NewLayerParams> {
public:
enum class Type { Layer, Group, ReferenceLayer, TilemapLayer };
enum class Type {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: enum 'Type' uses a larger base type ('int', size: 4 bytes) than necessary for its value set, consider using 'std::uint8_t' (1 byte) as the base type to reduce its size [performance-enum-size]

  enum class Type {
             ^

return nullptr;

// Add a new cel to load in the future after we load all layers
ObjectId celId = read32(s);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'celId' of type 'ObjectId' (aka 'unsigned int') can be declared 'const' [misc-const-correctness]

Suggested change
ObjectId celId = read32(s);
ObjectId const celId = read32(s);

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

}

void writeAllLayersID(std::ofstream& s, ObjectId parentId, const LayerGroup* group)
void writeAllLayersID(std::ofstream& s, ObjectId parentId, const Layer* group)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'writeAllLayersID' is within a recursive call chain [misc-no-recursion]

  void writeAllLayersID(std::ofstream& s, ObjectId parentId, const Layer* group)
       ^
Additional context

src/app/crash/write_document.cpp:232: example recursive call chain, starting from function 'writeAllLayersID'

  void writeAllLayersID(std::ofstream& s, ObjectId parentId, const Layer* group)
       ^

src/app/crash/write_document.cpp:239: Frame #1: function 'writeAllLayersID' calls function 'writeAllLayersID' here:

        writeAllLayersID(s, lay->id(), lay);
        ^

src/app/crash/write_document.cpp:239: ... which was the starting point of the recursive call chain; there may be other cycles

        writeAllLayersID(s, lay->id(), lay);
        ^

Comment thread src/app/doc.cpp
// Copying

void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destLayer0) const
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: method 'copyLayerContent' can be made static [readability-convert-member-functions-to-static]

Suggested change
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer)

src/app/doc.h:248:

-   void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const;
+   static void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) ;

Comment thread src/app/doc_range_ops.cpp
}

static bool has_child(LayerGroup* parent, Layer* child)
static bool has_child(Layer* parent, Layer* child)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'has_child' is within a recursive call chain [misc-no-recursion]

static bool has_child(Layer* parent, Layer* child)
            ^
Additional context

src/app/doc_range_ops.cpp:188: example recursive call chain, starting from function 'has_child'

static bool has_child(Layer* parent, Layer* child)
            ^

src/app/doc_range_ops.cpp:193: Frame #1: function 'has_child' calls function 'has_child' here:

    else if (c->isGroup() && has_child(c, child))
                             ^

src/app/doc_range_ops.cpp:193: ... which was the starting point of the recursive call chain; there may be other cycles

    else if (c->isGroup() && has_child(c, child))
                             ^

Comment thread src/app/doc_range_ops.cpp
return true;
else if (c->isGroup() && has_child(static_cast<LayerGroup*>(c), child))
else if (c->isGroup() && has_child(c, child))
return true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use 'else' after 'return' [readability-else-after-return]

Suggested change
return true;
if (c->isGroup() && has_child(c, child))
return true;

Comment thread src/app/doc_range_ops.cpp
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)))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 1st argument 'moveThis' (passed to 'parent') looks like it might be swapped with the 2nd, 'parent' (passed to 'child') [readability-suspicious-call-argument]

      if (moveThis == parent || (moveThis->isGroup() && has_child(moveThis, parent)))
                                                        ^
Additional context

src/app/doc_range_ops.cpp:188: in the call to 'has_child', declared here

static bool has_child(Layer* parent, Layer* child)
            ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allVisibleReferenceLayers(LayerList& list) const
void Layer::allVisibleReferenceLayers(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allVisibleReferenceLayers' is within a recursive call chain [misc-no-recursion]

void Layer::allVisibleReferenceLayers(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:407: example recursive call chain, starting from function 'allVisibleReferenceLayers'

void Layer::allVisibleReferenceLayers(LayerList& list) const
            ^

src/doc/layer.cpp:414: Frame #1: function 'allVisibleReferenceLayers' calls function 'allVisibleReferenceLayers' here:

      child->allVisibleReferenceLayers(list);
      ^

src/doc/layer.cpp:414: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allVisibleReferenceLayers(list);
      ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allBrowsableLayers(LayerList& list) const
void Layer::allBrowsableLayers(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allBrowsableLayers' is within a recursive call chain [misc-no-recursion]

void Layer::allBrowsableLayers(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:423: example recursive call chain, starting from function 'allBrowsableLayers'

void Layer::allBrowsableLayers(LayerList& list) const
            ^

src/doc/layer.cpp:427: Frame #1: function 'allBrowsableLayers' calls function 'allBrowsableLayers' here:

      child->allBrowsableLayers(list);
      ^

src/doc/layer.cpp:427: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allBrowsableLayers(list);
      ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allTilemaps(LayerList& list) const
void Layer::allTilemaps(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allTilemaps' is within a recursive call chain [misc-no-recursion]

void Layer::allTilemaps(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:433: example recursive call chain, starting from function 'allTilemaps'

void Layer::allTilemaps(LayerList& list) const
            ^

src/doc/layer.cpp:437: Frame #1: function 'allTilemaps' calls function 'allTilemaps' here:

      child->allTilemaps(list);
      ^

src/doc/layer.cpp:437: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allTilemaps(list);
      ^

Comment thread src/doc/layer.cpp
}

std::string LayerGroup::visibleLayerHierarchyAsString(const std::string& indent) const
std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'visibleLayerHierarchyAsString' is within a recursive call chain [misc-no-recursion]

std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const
                   ^
Additional context

src/doc/layer.cpp:444: example recursive call chain, starting from function 'visibleLayerHierarchyAsString'

std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const
                   ^

src/doc/layer.cpp:453: Frame #1: function 'visibleLayerHierarchyAsString' calls function 'visibleLayerHierarchyAsString' here:

      str += child->visibleLayerHierarchyAsString(indent + "  ");
             ^

src/doc/layer.cpp:453: ... which was the starting point of the recursive call chain; there may be other cycles

      str += child->visibleLayerHierarchyAsString(indent + "  ");
             ^

Comment thread src/doc/layer.cpp
}

layer_t LayerGroup::getLayerIndex(const Layer* layer, layer_t& index) const
layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'getLayerIndex' is within a recursive call chain [misc-no-recursion]

layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const
               ^
Additional context

src/doc/layer.cpp:516: example recursive call chain, starting from function 'getLayerIndex'

layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const
               ^

src/doc/layer.cpp:519: Frame #1: function 'getLayerIndex' calls function 'getLayerIndex' here:

    if ((child->hasSublayers() && child->getLayerIndex(layer, index) != -1) || (child == layer)) {
                                  ^

src/doc/layer.cpp:519: ... which was the starting point of the recursive call chain; there may be other cycles

    if ((child->hasSublayers() && child->getLayerIndex(layer, index) != -1) || (child == layer)) {
                                  ^

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/app/doc.cpp
// Copying

void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destLayer0) const
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: method 'copyLayerContent' can be made static [readability-convert-member-functions-to-static]

Suggested change
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const
void Doc::copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer)

src/app/doc.h:249:

-   void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) const;
+   static void copyLayerContent(const Layer* sourceLayer, Doc* destDoc, Layer* destLayer) ;

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread src/doc/layer.cpp
return size;
}

void Layer::suspendObject()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'suspendObject' is within a recursive call chain [misc-no-recursion]

void Layer::suspendObject()
            ^
Additional context

src/doc/layer.cpp:59: example recursive call chain, starting from function 'suspendObject'

void Layer::suspendObject()
            ^

src/doc/layer.cpp:62: Frame #1: function 'suspendObject' calls function 'suspendObject' here:

    child->suspendObject();
    ^

src/doc/layer.cpp:62: ... which was the starting point of the recursive call chain; there may be other cycles

    child->suspendObject();
    ^

Comment thread src/doc/layer.cpp
child->suspendObject();

CelIterator it = getCelBegin();
CelIterator end = getCelEnd();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'end' of type 'CelIterator' (aka '__normal_iterator<doc::Cel **, std::vector<doc::Cel *, std::allocator<doc::Cel *>>>') can be declared 'const' [misc-const-correctness]

Suggested change
CelIterator end = getCelEnd();
CelIterator const end = getCelEnd();

Comment thread src/doc/layer.cpp
WithUserData::suspendObject();
}

void Layer::restoreObject()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'restoreObject' is within a recursive call chain [misc-no-recursion]

void Layer::restoreObject()
            ^
Additional context

src/doc/layer.cpp:74: example recursive call chain, starting from function 'restoreObject'

void Layer::restoreObject()
            ^

src/doc/layer.cpp:86: Frame #1: function 'restoreObject' calls function 'restoreObject' here:

    child->restoreObject();
    ^

src/doc/layer.cpp:86: ... which was the starting point of the recursive call chain; there may be other cycles

    child->restoreObject();
    ^

Comment thread src/doc/layer.cpp
WithUserData::restoreObject();

CelIterator it = getCelBegin();
CelIterator end = getCelEnd();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'end' of type 'CelIterator' (aka '__normal_iterator<doc::Cel **, std::vector<doc::Cel *, std::allocator<doc::Cel *>>>') can be declared 'const' [misc-const-correctness]

Suggested change
CelIterator end = getCelEnd();
CelIterator const end = getCelEnd();

Comment thread src/doc/layer.cpp
}

void LayerImage::getCels(CelList& cels) const
void Layer::getCels(CelList& cels) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'getCels' is within a recursive call chain [misc-no-recursion]

void Layer::getCels(CelList& cels) const
            ^
Additional context

src/doc/layer.cpp:274: example recursive call chain, starting from function 'getCels'

void Layer::getCels(CelList& cels) const
            ^

src/doc/layer.cpp:280: Frame #1: function 'getCels' calls function 'getCels' here:

    layer->getCels(cels);
    ^

src/doc/layer.cpp:280: ... which was the starting point of the recursive call chain; there may be other cycles

    layer->getCels(cels);
    ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allVisibleReferenceLayers(LayerList& list) const
void Layer::allVisibleReferenceLayers(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allVisibleReferenceLayers' is within a recursive call chain [misc-no-recursion]

void Layer::allVisibleReferenceLayers(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:437: example recursive call chain, starting from function 'allVisibleReferenceLayers'

void Layer::allVisibleReferenceLayers(LayerList& list) const
            ^

src/doc/layer.cpp:444: Frame #1: function 'allVisibleReferenceLayers' calls function 'allVisibleReferenceLayers' here:

      child->allVisibleReferenceLayers(list);
      ^

src/doc/layer.cpp:444: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allVisibleReferenceLayers(list);
      ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allBrowsableLayers(LayerList& list) const
void Layer::allBrowsableLayers(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allBrowsableLayers' is within a recursive call chain [misc-no-recursion]

void Layer::allBrowsableLayers(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:453: example recursive call chain, starting from function 'allBrowsableLayers'

void Layer::allBrowsableLayers(LayerList& list) const
            ^

src/doc/layer.cpp:457: Frame #1: function 'allBrowsableLayers' calls function 'allBrowsableLayers' here:

      child->allBrowsableLayers(list);
      ^

src/doc/layer.cpp:457: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allBrowsableLayers(list);
      ^

Comment thread src/doc/layer.cpp
}

void LayerGroup::allTilemaps(LayerList& list) const
void Layer::allTilemaps(LayerList& list) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'allTilemaps' is within a recursive call chain [misc-no-recursion]

void Layer::allTilemaps(LayerList& list) const
            ^
Additional context

src/doc/layer.cpp:463: example recursive call chain, starting from function 'allTilemaps'

void Layer::allTilemaps(LayerList& list) const
            ^

src/doc/layer.cpp:467: Frame #1: function 'allTilemaps' calls function 'allTilemaps' here:

      child->allTilemaps(list);
      ^

src/doc/layer.cpp:467: ... which was the starting point of the recursive call chain; there may be other cycles

      child->allTilemaps(list);
      ^

Comment thread src/doc/layer.cpp
}

std::string LayerGroup::visibleLayerHierarchyAsString(const std::string& indent) const
std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'visibleLayerHierarchyAsString' is within a recursive call chain [misc-no-recursion]

std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const
                   ^
Additional context

src/doc/layer.cpp:474: example recursive call chain, starting from function 'visibleLayerHierarchyAsString'

std::string Layer::visibleLayerHierarchyAsString(const std::string& indent) const
                   ^

src/doc/layer.cpp:483: Frame #1: function 'visibleLayerHierarchyAsString' calls function 'visibleLayerHierarchyAsString' here:

      str += child->visibleLayerHierarchyAsString(indent + "  ");
             ^

src/doc/layer.cpp:483: ... which was the starting point of the recursive call chain; there may be other cycles

      str += child->visibleLayerHierarchyAsString(indent + "  ");
             ^

Comment thread src/doc/layer.cpp
}

layer_t LayerGroup::getLayerIndex(const Layer* layer, layer_t& index) const
layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function 'getLayerIndex' is within a recursive call chain [misc-no-recursion]

layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const
               ^
Additional context

src/doc/layer.cpp:546: example recursive call chain, starting from function 'getLayerIndex'

layer_t Layer::getLayerIndex(const Layer* layer, layer_t& index) const
               ^

src/doc/layer.cpp:549: Frame #1: function 'getLayerIndex' calls function 'getLayerIndex' here:

    if ((child->hasSublayers() && child->getLayerIndex(layer, index) != -1) || (child == layer)) {
                                  ^

src/doc/layer.cpp:549: ... which was the starting point of the recursive call chain; there may be other cycles

    if ((child->hasSublayers() && child->getLayerIndex(layer, index) != -1) || (child == layer)) {
                                  ^

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

void SetCelImage::onExecute()
{
Cel* cel = this->cel();
ImageRef oldImage = cel->imageRef();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'oldImage' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef oldImage = cel->imageRef();
ImageRef const oldImage = cel->imageRef();

void SetCelImage::onUndo()
{
Cel* cel = this->cel();
ImageRef currentImage = cel->imageRef();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'currentImage' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef currentImage = cel->imageRef();
ImageRef const currentImage = cel->imageRef();

{
Cel* cel = this->cel();
ImageRef currentImage = cel->imageRef();
gfx::Rect currentBounds = cel->bounds();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'currentBounds' of type 'gfx::Rect' (aka 'RectT') can be declared 'const' [misc-const-correctness]

Suggested change
gfx::Rect currentBounds = cel->bounds();
gfx::Rect const currentBounds = cel->bounds();

@ckaiser ckaiser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a look and I have some minor thoughts on the API design, only bug I found was with the layer properties window for groups: it does not adjust the height properly when opening (if you open it for a layer and then minimize the other properties though it works?):
Image

Comment thread src/doc/layer.cpp
, m_opacity(255)
{
ASSERT(type == ObjectType::LayerImage || type == ObjectType::LayerGroup ||
type == ObjectType::LayerTilemap);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new-layer-type.md mentions adding the type to this ASSERT, should probably either keep the assert or remove the mention in the documentation.

@dacap dacap Mar 2, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'll just remove the ASSERT from docs.

Comment thread src/doc/layer.h Outdated
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a more generic word that can encompass "mask or fx" that would allow for future expansion? It feels a little specific to have a check for something that is one of two things, and in future there may be another type of layer that requires the same kind of behavior. Overlay layer? Blanket layer? Modifier layer?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the name either, but at the moment might be isEffect() if in the future LayerMask is an effect too (both kind of layers have in common that don't make sense alone, and they need to be linked to another layer to apply the effect).

Comment thread src/doc/layer.h

public:
// Disable assigment
Layer& operator=(const Layer& other) = delete;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably more consistent to use the DISABLE_COPYING macro

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the DISABLE_COPYING is from a pre-C++11 era. Someday we could get rid of it (at the moment we have a mix of both options).

Comment thread src/doc/object_type.h Outdated
LayerText = 20,
LayerVector = 21,
LayerAudio = 22,
LayerHitbox = 23,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno about pre-defining all these layers types in this PR, I feel like it locks us in to keeping these as TODOs in the code for a long time before they're implemented, and maybe in the meantime we might change our mind about either their existence/necessity/naming, I'd rather we just leave things open for future expansion in the broadest, most generic way, and then in each implementation we can adjust to what we need.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now I'll split the PR in two big changes: One with the refactors, and another new PR with the "new layer types" to continue working. So the refactors can be integrated, but the new types are not yet added to the codebase.

@dacap dacap changed the title New layer types Refactors needed for new layer types Mar 2, 2026
dacap added 2 commits March 2, 2026 14:10
We're going to make the Cel concept as a container of any kind of data
related to a layer/frame (e.g. audio, vector, text, etc.).
dacap added 9 commits March 2, 2026 14:24
This will be a work-in-progress, as many operations might fail as they
still expect a cel with an image.
Now any kind of layer can be a parent layer (not only groups). This is
because we want to be able to associate FX/Mask layers as children of
a specific layer in the future.
We also save the bounds of the cel as it's required to restore the
bounds when we restore the image if we SetCelImage(cel, nullptr) and
then undo.

@aseprite-bot aseprite-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

ObjectId rightImg = (right ? right->image()->id() : 0);
fromLeft = (leftImg == cel->image()->id());
fromRight = (rightImg == cel->image()->id());
fromLeft = (left && left->dataId() == cel->dataId());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'Cel *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
fromLeft = (left && left->dataId() == cel->dataId());
fromLeft = ((left != nullptr) && left->dataId() == cel->dataId());

fromLeft = (leftImg == cel->image()->id());
fromRight = (rightImg == cel->image()->id());
fromLeft = (left && left->dataId() == cel->dataId());
fromRight = (right && right->dataId() == cel->dataId());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: implicit conversion 'Cel *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
fromRight = (right && right->dataId() == cel->dataId());
fromRight = ((right != nullptr) && right->dataId() == cel->dataId());

@dacap dacap added this to the v1.3.18-beta2 milestone Mar 2, 2026
@dacap dacap merged commit 2e55451 into aseprite:beta Mar 2, 2026
12 checks passed
@dacap dacap deleted the new-layers branch March 2, 2026 18:29
dacap added a commit to dacap/aseprite that referenced this pull request Mar 2, 2026
@dacap dacap mentioned this pull request Apr 1, 2026
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor complexity This issue needs some refactor / modifying several files / rethinking some part of the architecture

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants