Implement -c embedded:multiple (#2194)

This commit is contained in:
Rangi authored and GitHub committed 2026-10-03 19:26:02 +02:00
1 parent c592d665a8
commit ffa32a94e8
12 files changed
+66 -3

No files matched your search

+4
View File
@@ -28,6 +28,7 @@ struct Options {
INLINE,
EXTERNAL,
EMBEDDED,
EMBEDDED_MULTIPLE,
DMG,
} palSpecType = NO_SPEC; // -c
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
@@ -61,6 +62,9 @@ struct Options {
uint16_t maxNbColors() const { return nbColorsPerPal * nbPalettes; }
bool hasExplicitPalSpec() const { return palSpecType == INLINE || palSpecType == EXTERNAL; }
bool hasEmbeddedPalSpec() const {
return palSpecType == EMBEDDED || palSpecType == EMBEDDED_MULTIPLE;
}
uint8_t dmgColors[4] = {};
uint8_t dmgValue(uint8_t i) const {
+1
View File
@@ -10,6 +10,7 @@
void parseInlinePalSpec(char const * const rawArg);
void parseEmbeddedPalSpec(Png const &png);
void parseEmbeddedMultiplePalSpec(Png const &png);
void parseExternalPalSpec(char const *arg);
void parseDmgPalSpec(char const * const rawArg);
void parseBackgroundPalSpec(char const *arg);
+5
View File
@@ -189,6 +189,11 @@ is the case-insensitive word
then the first four colors of the input PNG's embedded palette are used.
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
If
.Ar pal_spec
is case-insensitive
.Cm embedded:multiple ,
then every four colors of the input PNG's embedded palette are used for multiple palettes.
.It Sy DMG palette spec
If
.Ar pal_spec
+12
View File
@@ -200,6 +200,9 @@ static void parseArg(int ch, char *arg) {
} else if (strcasecmp(arg, "embedded") == 0) {
// Use PLTE, error out if missing
options.palSpecType = Options::EMBEDDED;
} else if (strcasecmp(arg, "embedded:multiple") == 0) {
// Use PLTE, error out if missing
options.palSpecType = Options::EMBEDDED_MULTIPLE;
} else if (strcasecmp(arg, "auto") == 0) {
options.palSpecType = Options::NO_SPEC;
} else if (strcasecmp(arg, "dmg") == 0) {
@@ -526,7 +529,10 @@ static void verboseOutputConfig() {
case Options::INLINE:
case Options::EXTERNAL:
case Options::EMBEDDED:
case Options::EMBEDDED_MULTIPLE:
if (options.palSpecType == Options::EMBEDDED) {
fputs("\tEmbedded single palette spec from PNG indexed PLTE chunk\n", stderr);
} else if (options.palSpecType == Options::EMBEDDED_MULTIPLE) {
fputs("\tEmbedded palette spec from PNG indexed PLTE chunk\n", stderr);
} else {
fputs("\tExplicit palette spec\n", stderr);
@@ -695,6 +701,12 @@ int main(int argc, char *argv[]) {
parseEmbeddedPalSpec(png);
}
break;
case Options::EMBEDDED_MULTIPLE:
assume(!localOptions.palSpec);
if (useInputImage) {
parseEmbeddedMultiplePalSpec(png);
}
break;
case Options::INLINE:
assume(localOptions.palSpec);
parseInlinePalSpec(localOptions.palSpec->c_str());
+42 -2
View File
@@ -185,8 +185,8 @@ void parseEmbeddedPalSpec(Png const &png) {
// Ignore extraneous colors if they are unused
size_t nbColors = png.palette.size();
if (nbColors > options.maxOpaqueColors()) {
nbColors = options.maxOpaqueColors();
if (nbColors > options.nbColorsPerPal) {
nbColors = options.nbColorsPerPal;
}
// Fill in the palette spec
@@ -198,6 +198,46 @@ void parseEmbeddedPalSpec(Png const &png) {
}
}
void parseEmbeddedMultiplePalSpec(Png const &png) {
// Generate a palette spec from N palettes' length of colors in the embedded palette
if (png.palette.empty()) {
error("\"-c embedded:multiple\" was given, but the PNG does not have an embedded palette");
return;
}
// Use enough embedded colors to evenly divide into N palettes
size_t nbColors = png.palette.size();
size_t nbPalettes = nbColors / options.nbColorsPerPal;
if (uint16_t maxNbColors = options.maxNbColors(); nbColors > maxNbColors) {
warnx(
"PNG embedded palette contains %zu colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors,
maxNbColors
);
nbColors = maxNbColors;
nbPalettes = options.nbPalettes;
}
if (size_t nbUsedColors = options.nbColorsPerPal * nbPalettes; nbColors > nbUsedColors) {
warnx(
"PNG embedded palette contains %zu colors, but only %zu can be used for %zu palettes; "
"ignoring extra",
nbColors,
nbUsedColors,
nbPalettes
);
}
// Fill in the palette spec
options.palSpec.clear();
for (size_t i = 0; i < nbPalettes; ++i) {
auto &palette = options.palSpec.emplace_back();
for (size_t j = 0; j < options.nbColorsPerPal; ++j) {
palette[j] = png.palette[i * options.nbColorsPerPal + j];
}
}
}
// Appends the first line read from `file` to the end of the provided `buffer`.
// Returns true if a line was read.
[[nodiscard]]
+1 -1
View File
@@ -282,7 +282,7 @@ void reverse() {
palettes[0][i] = grayColors[options.dmgValue(i)];
}
grayscale = true;
} else if (options.palSpecType == Options::EMBEDDED) {
} else if (options.hasEmbeddedPalSpec()) {
warnx("An embedded palette was requested, but no palette file was specified; ignoring "
"request");
grayscale = true;
+1
View File
@@ -0,0 +1 @@
-c embedded:multiple
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B