Skip to content

Move .aseprite encoder to dio library#5677

Merged
dacap merged 3 commits into
aseprite:betafrom
dacap:dio-refactor
Feb 26, 2026
Merged

Move .aseprite encoder to dio library#5677
dacap merged 3 commits into
aseprite:betafrom
dacap:dio-refactor

Conversation

@dacap

@dacap dacap commented Feb 26, 2026

Copy link
Copy Markdown
Member

This is a long-standing TODO of ase_format.cpp to move the C-like code to encode .aseprite files to the dio library just like the decoder was moved in the past.

@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

There were too many comments to post at once. Showing the first 25 out of 135. Check the log or trigger a new build to see more.

header.ncolors));
std::unique_ptr<Sprite> sprite(
std::make_unique<Sprite>(ImageSpec(header.depth == 32 ? ColorMode::RGB :
header.depth == 16 ? ColorMode::GRAYSCALE :

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: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators [readability-avoid-nested-conditional-operator]

                                       header.depth == 16 ? ColorMode::GRAYSCALE :
                                       ^
Additional context

src/dio/aseprite_decoder.cpp:58: parent conditional operator here

    std::make_unique<Sprite>(ImageSpec(header.depth == 32 ? ColorMode::RGB :
                                       ^

Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_decoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
@dacap dacap force-pushed the dio-refactor branch 2 times, most recently from 21b4c92 to b6d2b0e Compare February 26, 2026 12:30

@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

There were too many comments to post at once. Showing the first 25 out of 112. Check the log or trigger a new build to see more.

Comment thread src/dio/aseprite_decoder.cpp
Comment thread src/dio/aseprite_decoder.cpp
Comment thread src/dio/aseprite_encoder.cpp Outdated
header->width = sprite->width();
header->height = sprite->height();
header->depth = (sprite->pixelFormat() == IMAGE_RGB ? 32 :
sprite->pixelFormat() == IMAGE_GRAYSCALE ? 16 :

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: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators [readability-avoid-nested-conditional-operator]

                   sprite->pixelFormat() == IMAGE_GRAYSCALE ? 16 :
                   ^
Additional context

src/dio/aseprite_encoder.cpp:162: parent conditional operator here

  header->depth = (sprite->pixelFormat() == IMAGE_RGB       ? 32 :
                   ^

header->height = sprite->height();
header->depth = (sprite->pixelFormat() == IMAGE_RGB ? 32 :
sprite->pixelFormat() == IMAGE_GRAYSCALE ? 16 :
sprite->pixelFormat() == IMAGE_INDEXED ? 8 :

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: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators [readability-avoid-nested-conditional-operator]

                   sprite->pixelFormat() == IMAGE_INDEXED   ? 8 :
                   ^
Additional context

src/dio/aseprite_encoder.cpp:163: parent conditional operator here

                   sprite->pixelFormat() == IMAGE_GRAYSCALE ? 16 :
                   ^

Comment thread src/dio/aseprite_encoder.cpp Outdated
//////////////////////////////////////////////////////////////////////

template<typename ImageTraits>
static void write_raw_image_templ(FileInterface* f, const ScanlinesGen* gen)

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: 'write_raw_image_templdoc::GrayscaleTraits' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace]

static void write_raw_image_templ(FileInterface* f, const ScanlinesGen* gen)
            ^

this fix will not be applied because it overlaps with another fix

Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated

@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

There were too many comments to post at once. Showing the first 25 out of 87. Check the log or trigger a new build to see more.

Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated
Comment thread src/dio/aseprite_encoder.cpp Outdated

@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

There were too many comments to post at once. Showing the first 25 out of 41. Check the log or trigger a new build to see more.

}
}

layer_t AsepriteEncoder::writeCels(AsepriteFrameHeader* frame_header,

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 'writeCels' is within a recursive call chain [misc-no-recursion]

layer_t AsepriteEncoder::writeCels(AsepriteFrameHeader* frame_header,
                         ^
Additional context

src/dio/aseprite_encoder.cpp:271: example recursive call chain, starting from function 'writeCels'

layer_t AsepriteEncoder::writeCels(AsepriteFrameHeader* frame_header,
                         ^

src/dio/aseprite_encoder.cpp:297: Frame #1: function 'writeCels' calls function 'writeCels' here:

      layer_index = writeCels(frame_header, ext_files, sprite, child, layer_index, frame);
                    ^

src/dio/aseprite_encoder.cpp:297: ... which was the starting point of the recursive call chain; there may be other cycles

      layer_index = writeCels(frame_header, ext_files, sprite, child, layer_index, frame);
                    ^

}

const int cel_type = (link ? ASE_FILE_LINK_CEL :
cel->layer()->isTilemap() ? delegate()->preferredTilemapCelType() :

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: conditional operator is used as sub-expression of parent conditional operator, refrain from using nested conditional operators [readability-avoid-nested-conditional-operator]

                        cel->layer()->isTilemap() ? delegate()->preferredTilemapCelType() :
                        ^
Additional context

src/dio/aseprite_encoder.cpp:661: parent conditional operator here

  const int cel_type = (link                      ? ASE_FILE_LINK_CEL :
                        ^


case ASE_FILE_LINK_CEL:
// Linked cel to another frame
write16(link->frame() - firstFrame);

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: Called C++ object pointer is null [clang-analyzer-core.CallAndMessage]

      write16(link->frame() - firstFrame);
              ^
Additional context

src/dio/aseprite_encoder.cpp:58: 'outputFrame' is equal to 0

    if (outputFrame == 0) {
        ^

src/dio/aseprite_encoder.cpp:58: Taking true branch

    if (outputFrame == 0) {
    ^

src/dio/aseprite_encoder.cpp:63: Assuming the condition is false

      if (delegate()->preserveColorProfile())
          ^

src/dio/aseprite_encoder.cpp:63: Taking false branch

      if (delegate()->preserveColorProfile())
      ^

src/dio/aseprite_encoder.cpp:71: Assuming the condition is false

      (frame == delegate()->fromFrame() ||
       ^

src/dio/aseprite_encoder.cpp:71: Left side of '||' is false

      (frame == delegate()->fromFrame() ||
       ^

src/dio/aseprite_encoder.cpp:73: Assuming the condition is false

       sprite->palette(frame - 1)->countDiff(pal, &palFrom, &palTo) > 0)) {
       ^

src/dio/aseprite_encoder.cpp:70: Taking false branch

    if ( // First frame or..
    ^

src/dio/aseprite_encoder.cpp:87: Assuming the condition is false

    if (frame == delegate()->fromFrame()) {
        ^

src/dio/aseprite_encoder.cpp:87: Taking false branch

    if (frame == delegate()->fromFrame()) {
    ^

src/dio/aseprite_encoder.cpp:123: Calling 'AsepriteEncoder::writeCels'

    writeCels(&frame_header, ext_files, sprite, sprite->root(), 0, frame);
    ^

src/dio/aseprite_encoder.cpp:278: Taking true branch

  if (layer->isImage()) {
  ^

src/dio/aseprite_encoder.cpp:280: Assuming 'cel' is non-null

    if (cel) {
        ^

src/dio/aseprite_encoder.cpp:280: Taking true branch

    if (cel) {
    ^

src/dio/aseprite_encoder.cpp:281: Calling 'AsepriteEncoder::writeCelChunk'

      writeCelChunk(frame_header, cel, static_cast<const LayerImage*>(layer), layer_index);
      ^

src/dio/aseprite_encoder.cpp:646: 'link' initialized here

  const Cel* link = cel->link();
  ^

src/dio/aseprite_encoder.cpp:650: Assuming 'link' is null

  if (link && link->frame() < firstFrame) {
      ^

src/dio/aseprite_encoder.cpp:650: Left side of '&&' is false

  if (link && link->frame() < firstFrame) {
           ^

src/dio/aseprite_encoder.cpp:661: 'link' is null

  const int cel_type = (link                      ? ASE_FILE_LINK_CEL :
                        ^

src/dio/aseprite_encoder.cpp:661: '?' condition is false

  const int cel_type = (link                      ? ASE_FILE_LINK_CEL :
                        ^

src/dio/aseprite_encoder.cpp:662: '?' condition is false

                        cel->layer()->isTilemap() ? delegate()->preferredTilemapCelType() :
                        ^

src/dio/aseprite_encoder.cpp:673: Control jumps to 'case 1:' at line 695

  switch (cel_type) {
  ^

src/dio/aseprite_encoder.cpp:696: Called C++ object pointer is null

      write16(link->frame() - firstFrame);
              ^

}
}

void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)

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 'writePropertyValue' is within a recursive call chain [misc-no-recursion]

void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)
                      ^
Additional context

src/dio/aseprite_encoder.cpp:1172: example recursive call chain, starting from function 'writePropertyValue'

void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)
                      ^

src/dio/aseprite_encoder.cpp:1222: Frame #1: function 'writePropertyValue' calls function 'writePropertyValue' here:

        writePropertyValue(v);
        ^

src/dio/aseprite_encoder.cpp:1222: ... which was the starting point of the recursive call chain; there may be other cycles

        writePropertyValue(v);
        ^


void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)
{
switch (value.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: switching on non-enum value without default case may not cover all cases [bugprone-switch-missing-default-case]

  switch (value.type()) {
  ^

Comment thread src/dio/decoder.h Outdated
return ((b2 << 8) | b1); // Little endian
}
else
return 0;

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 0;
return 0;

Comment thread src/dio/decoder.h Outdated

uint32_t read32()
{
int b1 = read8();

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 'b1' of type 'int' can be declared 'const' [misc-const-correctness]

Suggested change
int b1 = read8();
int const b1 = read8();

Comment thread src/dio/decoder.h Outdated
uint32_t read32()
{
int b1 = read8();
int b2 = read8();

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 'b2' of type 'int' can be declared 'const' [misc-const-correctness]

Suggested change
int b2 = read8();
int const b2 = read8();

Comment thread src/dio/decoder.h Outdated
{
int b1 = read8();
int b2 = read8();
int b3 = read8();

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 'b3' of type 'int' can be declared 'const' [misc-const-correctness]

Suggested change
int b3 = read8();
int const b3 = read8();

Comment thread src/dio/decoder.h Outdated
int b1 = read8();
int b2 = read8();
int b3 = read8();
int b4 = read8();

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 'b4' of type 'int' can be declared 'const' [misc-const-correctness]

Suggested change
int b4 = read8();
int const b4 = read8();

Added Decoder::tell/seek() functions to reduce a little of noise from
AsepriteDecoder code.

@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 AsepriteEncoder::writePropertyValue(const UserData::Variant& value)

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 'writePropertyValue' is within a recursive call chain [misc-no-recursion]

void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)
                      ^
Additional context

src/dio/aseprite_encoder.cpp:1178: example recursive call chain, starting from function 'writePropertyValue'

void AsepriteEncoder::writePropertyValue(const UserData::Variant& value)
                      ^

src/dio/aseprite_encoder.cpp:1228: Frame #1: function 'writePropertyValue' calls function 'writePropertyValue' here:

        writePropertyValue(v);
        ^

src/dio/aseprite_encoder.cpp:1228: ... which was the starting point of the recursive call chain; there may be other cycles

        writePropertyValue(v);
        ^

Comment thread src/dio/encode_delegate.h
virtual bool composeGroups() = 0;
virtual bool preserveColorProfile() = 0;
virtual bool cacheCompressedTilesets() = 0;
virtual int preferredCelType() { return ASE_FILE_COMPRESSED_CEL; }

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: use of undeclared identifier 'ASE_FILE_COMPRESSED_CEL' [clang-diagnostic-error]

  virtual int preferredCelType() { return ASE_FILE_COMPRESSED_CEL; }
                                          ^

Comment thread src/dio/encode_delegate.h
virtual bool preserveColorProfile() = 0;
virtual bool cacheCompressedTilesets() = 0;
virtual int preferredCelType() { return ASE_FILE_COMPRESSED_CEL; }
virtual int preferredTilemapCelType() { return ASE_FILE_COMPRESSED_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: use of undeclared identifier 'ASE_FILE_COMPRESSED_TILEMAP' [clang-diagnostic-error]

  virtual int preferredTilemapCelType() { return ASE_FILE_COMPRESSED_TILEMAP; }
                                                 ^

Comment thread src/dio/encoder.h
#include <cstdint>
#include <cstring>

#include "dio/file_interface.h"

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: 'dio/file_interface.h' file not found [clang-diagnostic-error]

#include "dio/file_interface.h"
         ^

Comment thread src/dio/pixel_io.h
return 0;
}

void write_pixel(FileInterface* f, doc::TilemapTraits::pixel_t 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: method 'write_pixel' can be made static [readability-convert-member-functions-to-static]

Suggested change
void write_pixel(FileInterface* f, doc::TilemapTraits::pixel_t c)
static void write_pixel(FileInterface* f, doc::TilemapTraits::pixel_t c)

Comment thread src/dio/pixel_io.h
*address = ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
}
}
void write_scanline(doc::TilemapTraits::address_t address, int w, uint8_t* buffer)

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 'write_scanline' can be made static [readability-convert-member-functions-to-static]

Suggested change
void write_scanline(doc::TilemapTraits::address_t address, int w, uint8_t* buffer)
static void write_scanline(doc::TilemapTraits::address_t address, int w, uint8_t* buffer)

Comment thread src/dio/stdio.cpp

size_t StdioFileInterface::writeBytes(uint8_t* buf, size_t n)
{
size_t r = fwrite(buf, 1, n, m_file);

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 'r' of type 'size_t' (aka 'unsigned long') can be declared 'const' [misc-const-correctness]

Suggested change
size_t r = fwrite(buf, 1, n, m_file);
size_t const r = fwrite(buf, 1, n, m_file);

@dacap dacap marked this pull request as ready for review February 26, 2026 14:19
@dacap dacap merged commit a92804c into aseprite:beta Feb 26, 2026
12 checks passed
@dacap dacap deleted the dio-refactor branch February 26, 2026 14:25
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.

2 participants