Skip to content

Keep empty cels#5684

Draft
dacap wants to merge 3 commits into
aseprite:betafrom
dacap:keep-empty-cels
Draft

Keep empty cels#5684
dacap wants to merge 3 commits into
aseprite:betafrom
dacap:keep-empty-cels

Conversation

@dacap

@dacap dacap commented Mar 2, 2026

Copy link
Copy Markdown
Member

Should fix all issues from #1303.

@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/trim_cel.cpp
Cel* c = layer->cel(fr);
if (c && c->dataRef().get() == celData)
add(new cmd::RemoveCel(c));
if (c && c->dataRef().get() == celData) {

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
if (c && c->dataRef().get() == celData) {
if ((c != nullptr) && c->dataRef().get() == celData) {

{
auto& styles = skinTheme()->styles;
const ObjectId dataId = (*data->activeIt)->dataId();
const bool has_image = ((*data->activeIt)->image() ? 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 = ((*data->activeIt)->image() ? true : false);
const bool has_image = (static_cast<bool>((*data->activeIt)->image()));

@dacap dacap force-pushed the keep-empty-cels branch from 140cdf0 to edef945 Compare March 2, 2026 22:07

@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

else {
ASSERT(site.layer()->isTilemap());
if (auto cel = site.cel()) {
if (auto cel = site.cel(); cel && cel->image()) {

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 cel' can be declared as 'auto *cel' [readability-qualified-auto]

Suggested change
if (auto cel = site.cel(); cel && cel->image()) {
if (auto *cel = site.cel(); cel && cel->image()) {

@dacap dacap force-pushed the keep-empty-cels branch from edef945 to cde669c Compare March 4, 2026 13:05

@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

// Same size of cel image and background image, we can just
// replace pixels.
if (bg_image->width() == cel_image->width() && bg_image->height() == cel_image->height()) {
if (cel_image && bg_image->size() == cel_image->size()) {

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 'Image *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
if (cel_image && bg_image->size() == cel_image->size()) {
if ((cel_image != nullptr) && bg_image->size() == cel_image->size()) {

Cel* cel = layer->cel(frame);
if (!cel) {
ImageRef cel_image(Image::create(sprite->pixelFormat(), sprite->width(), sprite->height()));
ImageRef cel_image(Image::create(sprite->spec()));

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 'cel_image' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef cel_image(Image::create(sprite->spec()));
ImageRef const cel_image(Image::create(sprite->spec()));

if (!mask->bitmap())
return;

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();

Comment thread src/app/color_picker.cpp

doc::PixelFormat pixelFormat = (cel->layer()->isTilemap() ? sprite->pixelFormat() :
cel->image()->pixelFormat());
doc::PixelFormat pixelFormat = (!cel->image() || cel->layer()->isTilemap() ?

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 'pixelFormat' of type 'doc::PixelFormat' can be declared 'const' [misc-const-correctness]

Suggested change
doc::PixelFormat pixelFormat = (!cel->image() || cel->layer()->isTilemap() ?
doc::PixelFormat const pixelFormat = (!cel->image() || cel->layer()->isTilemap() ?

Comment thread src/app/site.cpp
else if (Cel* c = cel()) {
if (c->image() && m_layer && m_layer->canEditPixels())
return CelList{ c };
}

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
}
if (Cel* c = cel()) {
if (c->image() && m_layer && m_layer->canEditPixels())
return CelList{ c };
}

Comment thread src/app/util/cel_ops.cpp
if (algorithm::shrink_bounds(dstCel->image(), bg, dstLayer, bounds)) {
ImageRef trimmed(doc::crop_image(dstCel->image(), bounds, bg));
if (algorithm::shrink_bounds(dstImage.get(), bg, dstLayer, bounds)) {
ImageRef trimmed(doc::crop_image(dstImage.get(), bounds, bg));

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 'trimmed' of type 'ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
ImageRef trimmed(doc::crop_image(dstImage.get(), bounds, bg));
ImageRef const trimmed(doc::crop_image(dstImage.get(), bounds, bg));

ASSERT(!ctx->activeSite().cel() || ctx->activeSite().cel()->image());

Cel* cel = ctx->activeSite().cel();
const bool isAnImageOnDstCel = (cel && cel->image());

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
const bool isAnImageOnDstCel = (cel && cel->image());
const bool isAnImageOnDstCel = ((cel != nullptr) && cel->image());

ASSERT(!ctx->activeSite().cel() || ctx->activeSite().cel()->image());

Cel* cel = ctx->activeSite().cel();
const bool isAnImageOnDstCel = (cel && cel->image());

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 'Image *' -> 'bool' [readability-implicit-bool-conversion]

Suggested change
const bool isAnImageOnDstCel = (cel && cel->image());
const bool isAnImageOnDstCel = (cel && (cel->image() != nullptr));

// Resize the image
const int w = std::max(1, int(scale.w * image->width()));
const int h = std::max(1, int(scale.h * image->height()));
doc::ImageRef newImage(

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 'newImage' of type 'doc::ImageRef' (aka 'shared_ptr') can be declared 'const' [misc-const-correctness]

Suggested change
doc::ImageRef newImage(
doc::ImageRef const newImage(

@dacap dacap marked this pull request as ready for review March 4, 2026 13:24
@dacap

dacap commented Mar 4, 2026

Copy link
Copy Markdown
Member Author

@ckaiser would you mind to take a look to this? It's a quite large change, I'd like to add some tests and probably change some APIs as the code looks like a mess for all the given cases.

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

Some things I found while testing:

  • FlattenLayers crashed when merging down in some cases
  • Linked Tilemaps behave differently in the UI than normal layers, if you link two tilemap cels that have no image data, then draw something in one of them and then undo that, the timeline won't go back to showing the "hollow" cel indicator.
  • When merging a layer with a cel with user data into a layer with an empty cel, the user data is not preserved regardless of it had an image before, I think this happens without this patch, so it's default behavior but it feels strange to not merge user data.

@dacap

dacap commented Mar 4, 2026

Copy link
Copy Markdown
Member Author

Thanks for testing @ckaiser. As we talked I'll see how we can automate all these kind of tests before merging this PR.

@dacap dacap assigned dacap and unassigned ckaiser Mar 4, 2026
@dacap dacap marked this pull request as draft March 5, 2026 10:40
@dacap dacap linked an issue Jun 30, 2026 that may be closed by this pull request
5 tasks
@dacap dacap force-pushed the keep-empty-cels branch from cde669c to 716ddbd Compare June 30, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Associate user data to an empty cel

3 participants