From 25240e9fc0fddf44036371ec21877e521f924fc7 Mon Sep 17 00:00:00 2001 From: Rangi Date: Fri, 4 Sep 2026 13:25:49 -0400 Subject: [PATCH] Fix but deprecate an implicit transparent color #0 --- include/gfx/rgba.hpp | 1 + man/rgbgfx.1 | 10 +++----- src/gfx/process.cpp | 35 +++++++++++++++++++++++++--- test/gfx/implicit-overflow.err | 3 +++ test/gfx/implicit-overflow.flags | 1 + test/gfx/implicit-overflow.png | Bin 0 -> 77 bytes test/gfx/implicit-transparent.err | 1 + test/gfx/implicit-transparent.flags | 1 + test/gfx/implicit-transparent.png | Bin 0 -> 77 bytes 9 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 test/gfx/implicit-overflow.err create mode 100644 test/gfx/implicit-overflow.flags create mode 100644 test/gfx/implicit-overflow.png create mode 100644 test/gfx/implicit-transparent.err create mode 100644 test/gfx/implicit-transparent.flags create mode 100644 test/gfx/implicit-transparent.png diff --git a/include/gfx/rgba.hpp b/include/gfx/rgba.hpp index df1dea68..87f1f602 100644 --- a/include/gfx/rgba.hpp +++ b/include/gfx/rgba.hpp @@ -52,6 +52,7 @@ struct Rgba { bool isTransparent() const { return alpha < transparency_threshold; } static constexpr uint8_t opacity_threshold = 0xF0; bool isOpaque() const { return alpha >= opacity_threshold; } + bool isAmbiguous() const { return isTransparent() == isOpaque(); } // Computes the equivalent CGB color, respects the color curve depending on options uint16_t cgbColor() const; diff --git a/man/rgbgfx.1 b/man/rgbgfx.1 index 53bc87e1..253cdc9b 100644 --- a/man/rgbgfx.1 +++ b/man/rgbgfx.1 @@ -585,15 +585,11 @@ for example because you want to use palette swaps, please use .Fl c to specify the palette explicitly. .Pp -First, if the image contains +Note that if the image contains .Em any -transparent pixel, color #0 of +transparent pixels, color #0 of .Em all -palettes will be allocated to it. -This is done -.Sy even if palettes were explicitly specified using Fl c ; -then the specification only covers color #1 onwards. -.Pq If you do not want this, ask your image editor to remove the alpha channel. +palettes will be transparent. .Pp After generating palettes, .Nm diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index b5b054aa..f576894e 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -181,7 +181,7 @@ struct Image { // Register colors from `png` into `colors` for (uint32_t y = 0; y < png.height; ++y) { for (uint32_t x = 0; x < png.width; ++x) { - if (Rgba const &color = pixel(x, y); color.isTransparent() == color.isOpaque()) { + if (Rgba const &color = pixel(x, y); color.isAmbiguous()) { // Report ambiguously transparent or opaque colors if (uint32_t css = color.toCSS(); ambiguous.find(css) == ambiguous.end()) { error( @@ -402,12 +402,41 @@ static std::pair, std::vector> makePalsAsSpecified(std::vector const &colorSets) { // Convert the palette spec to actual palettes std::vector palettes(options.palSpec.size()); + bool gaveDeprecationWarning = false; for (auto [spec, pal] : zip(options.palSpec, palettes)) { + bool skipFirst = false; + // If the image contains any transparent pixels, color #0 of all palettes is transparent. + // Thus, all explicit palette specs should leave color #0 as "#none" or transparent. + // If they specify an opaque color #0, we have legacy behavior of implicitly inserting + // a transparent color #0, and expecting the spec to only cover the subsequent colors. + if (options.hasTransparentPixels && spec.front().has_value() && spec.front()->isOpaque()) { + skipFirst = true; + if (!gaveDeprecationWarning) { + warning( + WARNING_OBSOLETE, + "Implicit transparent color #0 is deprecated; leave an explicit gap in the " + "palette specs" + ); + gaveDeprecationWarning = true; + } + } for (size_t i = 0; i < options.nbColorsPerPal; ++i) { // If the spec has a gap, there's no need to copy anything. - if (spec[i].has_value() && spec[i]->isOpaque()) { - pal[i] = spec[i]->cgbColor(); + if (!spec[i].has_value() || !spec[i]->isOpaque()) { + continue; } + // If we're skipping color #0 as implicitly transparent, a full spec + // plus the implicit transparent color will be too large for a palette. + if (i + skipFirst >= options.nbColorsPerPal) { + error( + "Each palette spec can only contain up to %" PRIu8 + " color%s plus the implict transparent color", + options.nbColorsPerPal - 1, + options.nbColorsPerPal - 1 == 1 ? "" : "s" + ); + giveUp(); + } + pal[i + skipFirst] = spec[i]->cgbColor(); } } diff --git a/test/gfx/implicit-overflow.err b/test/gfx/implicit-overflow.err new file mode 100644 index 00000000..655fe6af --- /dev/null +++ b/test/gfx/implicit-overflow.err @@ -0,0 +1,3 @@ +warning: Implicit transparent color #0 is deprecated; leave an explicit gap in the palette specs [-Wobsolete] +error: Each palette spec can only contain up to 1 color plus the implict transparent color +Conversion aborted after 1 error diff --git a/test/gfx/implicit-overflow.flags b/test/gfx/implicit-overflow.flags new file mode 100644 index 00000000..0b3bb6c8 --- /dev/null +++ b/test/gfx/implicit-overflow.flags @@ -0,0 +1 @@ +-c #0000FF,#FF0000 -s 2 diff --git a/test/gfx/implicit-overflow.png b/test/gfx/implicit-overflow.png new file mode 100644 index 0000000000000000000000000000000000000000..30eaac121f7adc86a6c6a515f9066b7539f9045f GIT binary patch literal 77 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqBAzaeAr*6y6BwHR|1V75GwHA^ ZBZG}oZS&@8LlvMrgQu&X%Q~loCIET~6T|=j literal 0 HcmV?d00001 diff --git a/test/gfx/implicit-transparent.err b/test/gfx/implicit-transparent.err new file mode 100644 index 00000000..71870a93 --- /dev/null +++ b/test/gfx/implicit-transparent.err @@ -0,0 +1 @@ +warning: Implicit transparent color #0 is deprecated; leave an explicit gap in the palette specs [-Wobsolete] diff --git a/test/gfx/implicit-transparent.flags b/test/gfx/implicit-transparent.flags new file mode 100644 index 00000000..2cf014a2 --- /dev/null +++ b/test/gfx/implicit-transparent.flags @@ -0,0 +1 @@ +-c #0000FF,#FF0000 diff --git a/test/gfx/implicit-transparent.png b/test/gfx/implicit-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..30eaac121f7adc86a6c6a515f9066b7539f9045f GIT binary patch literal 77 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqBAzaeAr*6y6BwHR|1V75GwHA^ ZBZG}oZS&@8LlvMrgQu&X%Q~loCIET~6T|=j literal 0 HcmV?d00001