This commit is contained in:
Gaspar Capello 2025-07-23 18:49:17 -04:00 committed by GitHub
commit 21b327ee74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 4 deletions

View File

@ -1050,7 +1050,47 @@ public:
m_fop->newBlend(), m_fop->newBlend(),
RgbMapAlgorithm::OCTREE, // TODO configurable? RgbMapAlgorithm::OCTREE, // TODO configurable?
false); // Do not add the transparent color yet false); // Do not add the transparent color yet
m_transparentIndex = 0;
m_globalColormapPalette = newPalette;
m_globalColormap = createColorMap(&m_globalColormapPalette);
}
}
// The following "if" block is intended to address cases where
// the Color Mode of the animation is RGB and can be represented by
// an absolute palette because it contains fewer than 256 colors
// throughout the entire animation. In this way the memory space
// used to generate the GIF is much more efficient, since local
// palettes don't need to be inserted in each frame. It also
// fixes a display issue with GIF files (generated by
// Aseprite 1.3.13) on Discord when the GIF parameters were:
// - disposal = DO_NOT_DISPOSE = 1
// - Local palettes exist.
if (m_spec.colorMode() == ColorMode::RGB || m_spec.colorMode() == ColorMode::GRAYSCALE) {
Palette newPalette(0, 512);
render::create_palette_from_sprite(m_sprite,
0,
totalFrames() - 1,
false,
&newPalette,
nullptr,
m_fop->newBlend(),
RgbMapAlgorithm::OCTREE,
false); // No effect on OctreeMap.
// Case: palette with (256 colors + mask color) == 257 but
// the mask color isn't used in the sprite.
if (newPalette.size() == 257 && !m_sprite->isColorUsed(0)) {
// Forcing GIF with background
m_transparentIndex = -1;
m_hasBackground = true;
// Discard the mask color (palette entry = 0)
for (int i = 0; i < 256; i++)
newPalette.setEntry(i, newPalette.getEntry(i + 1));
newPalette.resize(256);
m_globalColormapPalette = newPalette;
m_globalColormap = createColorMap(&m_globalColormapPalette);
}
else if (newPalette.size() <= 256) {
m_transparentIndex = 0; m_transparentIndex = 0;
m_globalColormapPalette = newPalette; m_globalColormapPalette = newPalette;
m_globalColormap = createColorMap(&m_globalColormapPalette); m_globalColormap = createColorMap(&m_globalColormapPalette);
@ -1388,15 +1428,18 @@ private:
if (!m_preservePaletteOrder) { if (!m_preservePaletteOrder) {
const LockImageBits<RgbTraits> srcBits(m_deltaImage.get()); const LockImageBits<RgbTraits> srcBits(m_deltaImage.get());
const LockImageBits<RgbTraits> preBits(m_previousImage, frameBounds);
LockImageBits<IndexedTraits> dstBits(frameImage.get()); LockImageBits<IndexedTraits> dstBits(frameImage.get());
auto srcIt = srcBits.begin(); auto srcIt = srcBits.begin();
auto dstIt = dstBits.begin(); auto dstIt = dstBits.begin();
auto preIt = preBits.begin();
for (int y = 0; y < frameBounds.h; ++y) { for (int y = 0; y < frameBounds.h; ++y) {
for (int x = 0; x < frameBounds.w; ++x, ++srcIt, ++dstIt) { for (int x = 0; x < frameBounds.w; ++x, ++srcIt, ++dstIt, ++preIt) {
ASSERT(srcIt != srcBits.end()); ASSERT(srcIt != srcBits.end());
ASSERT(dstIt != dstBits.end()); ASSERT(dstIt != dstBits.end());
ASSERT(preIt != preBits.end());
color_t color = *srcIt; color_t color = *srcIt;
int i; int i;
@ -1410,9 +1453,19 @@ private:
if (i < 0) if (i < 0)
i = octree.mapColor(color | rgba_a_mask); // alpha=255 i = octree.mapColor(color | rgba_a_mask); // alpha=255
} }
// If the alpha in a pixel from m_deltaImage is < 128, the
// pixel is assumed to be 0. Then it should draw the pixel
// according defined m_transparentIndex or disposal method
else { else {
if (m_transparentIndex >= 0) if (m_transparentIndex >= 0)
i = m_transparentIndex; i = m_transparentIndex;
else if (disposal == DisposalMethod::DO_NOT_DISPOSE) {
i = framePalette.findExactMatch(rgba_getr(*preIt),
rgba_getg(*preIt),
rgba_getb(*preIt),
255,
-1);
}
else else
i = m_bgIndex; i = m_bgIndex;
} }

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2018-2023 Igara Studio S.A. // Copyright (c) 2018-2025 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -352,6 +352,18 @@ bool is_plain_image_templ(const Image* img, const color_t color)
return true; return true;
} }
template<typename ImageTraits>
bool is_color_used_templ(const Image* img, const doc::color_t color)
{
const LockImageBits<ImageTraits> bits(img);
auto it = bits.begin(), end = bits.end();
for (; it != end; ++it) {
if (*it == color)
return true;
}
return false;
}
template<typename ImageTraits> template<typename ImageTraits>
int count_diff_between_images_templ(const Image* i1, const Image* i2) int count_diff_between_images_templ(const Image* i1, const Image* i2)
{ {
@ -464,6 +476,16 @@ bool is_plain_image(const Image* img, color_t c)
return false; return false;
} }
bool is_color_used(const Image* img, color_t c)
{
ASSERT(img->pixelFormat() == IMAGE_RGB || img->pixelFormat() == IMAGE_GRAYSCALE);
switch (img->pixelFormat()) {
case IMAGE_RGB: return is_color_used_templ<RgbTraits>(img, c);
case IMAGE_GRAYSCALE: return is_color_used_templ<GrayscaleTraits>(img, c);
}
return false;
}
bool is_empty_image(const Image* img) bool is_empty_image(const Image* img)
{ {
color_t c = 0; // alpha = 0 color_t c = 0; // alpha = 0

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2018-2023 Igara Studio S.A. // Copyright (c) 2018-2025 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -65,6 +65,7 @@ void fill_ellipse(Image* image,
color_t color); color_t color);
bool is_plain_image(const Image* img, color_t c); bool is_plain_image(const Image* img, color_t c);
bool is_color_used(const Image* img, color_t c);
bool is_empty_image(const Image* img); bool is_empty_image(const Image* img);
int count_diff_between_images(const Image* i1, const Image* i2); int count_diff_between_images(const Image* i1, const Image* i2);

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // 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 // Copyright (C) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -223,6 +223,18 @@ bool Sprite::isOpaque() const
return (bg && bg->isVisible()); return (bg && bg->isVisible());
} }
bool Sprite::isColorUsed(const doc::color_t c) const
{
ASSERT(pixelFormat() == IMAGE_RGB || pixelFormat() == IMAGE_GRAYSCALE);
for (Cel* cel : cels()) {
if (cel && cel->image()) {
if (is_color_used(cel->image(), c))
return true;
}
}
return false;
}
bool Sprite::needAlpha() const bool Sprite::needAlpha() const
{ {
switch (pixelFormat()) { switch (pixelFormat()) {

View File

@ -110,6 +110,9 @@ public:
// Returns true if the sprite has a background layer and it's visible // Returns true if the sprite has a background layer and it's visible
bool isOpaque() const; bool isOpaque() const;
// Returns true if the sprite is using a pixel with color c
bool isColorUsed(const doc::color_t c) const;
// Returns true if the rendered images will contain alpha values less // Returns true if the rendered images will contain alpha values less
// than 255. Only RGBA and Grayscale images without background needs // than 255. Only RGBA and Grayscale images without background needs
// alpha channel in the render. // alpha channel in the render.