Fix RGBGFX to support -C/--color-curve in -r/--reverse mode (#2110)

Clarifies error message when RGBGFX reverse mode has `-c` mismatching `-p`.
This commit is contained in:
Rangi
2026-09-18 17:35:22 -04:00
committed by GitHub
parent 0286d4119e
commit 89403ca23b
9 changed files with 75 additions and 39 deletions
+1 -1
View File
@@ -528,7 +528,7 @@ static void verboseOutputConfig() {
for (auto const &pal : options.palSpec) {
fputs("\t\t", stderr);
for (auto const &color : pal) {
if (color) {
if (color.has_value()) {
fprintf(stderr, "#%06x, ", color->toCSS() >> 8);
} else {
fputs("#none, ", stderr);
+4 -4
View File
@@ -542,10 +542,10 @@ static void parseGBCFile(char const *filename, std::filebuf &file) {
}
options.palSpec.push_back({
Rgba::fromCGBColor(toWord(buf[0], buf[1])),
Rgba::fromCGBColor(toWord(buf[2], buf[3])),
Rgba::fromCGBColor(toWord(buf[4], buf[5])),
Rgba::fromCGBColor(toWord(buf[6], buf[7])),
Rgba::fromCGBColor(toWord(buf[0], buf[1]), false),
Rgba::fromCGBColor(toWord(buf[2], buf[3]), false),
Rgba::fromCGBColor(toWord(buf[4], buf[5]), false),
Rgba::fromCGBColor(toWord(buf[6], buf[7]), false),
});
}
}
+16 -7
View File
@@ -119,10 +119,6 @@ void reverse() {
warnx("Tile deduplication is enabled, but no tilemap is provided");
}
if (options.useColorCurve) {
warnx("The color curve is not yet supported in reverse mode");
}
if (options.inputSlice.left != 0 || options.inputSlice.top != 0
|| options.inputSlice.height != 0) {
warnx("\"Sliced-off\" pixels are ignored in reverse mode");
@@ -230,14 +226,14 @@ void reverse() {
palSize
);
}
// Expand the colors
// Expand the little-endian RGB555 colors to RGB888
auto &palette = palettes.emplace_back();
std::generate(
palette.begin(),
palette.begin() + options.nbColorsPerPal,
[&buf, i = 0]() mutable {
i += 2;
return Rgba::fromCGBColor(buf[i - 2] | buf[i - 1] << 8); // little-endian
return Rgba::fromCGBColor(buf[i - 2] | buf[i - 1] << 8, options.useColorCurve);
}
);
}
@@ -251,7 +247,20 @@ void reverse() {
}
if (options.hasExplicitPalSpec() && palettes != options.palSpec) {
warnx("Colors in the palette file do not match those specified with '-c'");
// The explicit `-c` pal spec does not match the input `-p` palette file.
// Check whether their 8-to-5-bit-reduced GB colors nevertheless match.
std::vector<std::array<std::optional<Rgba>, 4>> palSpecQuantized(options.palSpec);
for (auto &pal : palSpecQuantized) {
for (auto &color : pal) {
if (color.has_value()) {
color = Rgba::fromCGBColor(color->cgbColor(), options.useColorCurve);
}
}
}
warnx(
"Colors %s the palette file do not match those specified with '-c'",
palettes == palSpecQuantized ? "reversed from" : "in"
);
// This spacing aligns "...versus with `-c`" above the column of `-c` palettes
fputs("Colors specified in the palette file: ...versus with '-c':\n", stderr);
for (size_t i = 0; i < palettes.size() || i < options.palSpec.size(); ++i) {
+38 -8
View File
@@ -24,16 +24,24 @@ std::string toCGB(uint16_t color) {
return buf;
}
// Based on inverting the "Modern - Accurate" formula used by SameBoy
// since commit b5a611c5db46d6a0649d04d24d8d6339200f9ca1 (Dec 2020),
// with gaps in the scale curve filled by polynomial interpolation.
// Copied from the "Modern - Accurate" (`GB_COLOR_CORRECTION_MODERN_ACCURATE`)
// formula used by SameBoy in its `scale_channel_with_curve` function since
// commit b5a611c5db46d6a0649d04d24d8d6339200f9ca1 (Dec 2020).
// clang-format off: vertically align columns of values
static std::array<uint8_t, 32> color_curve{
0, 6, 12, 20, 28, 36, 45, 56, 66, 76, 88, 100, 113, 125, 137, 149,
161, 172, 182, 192, 202, 210, 218, 225, 232, 238, 243, 247, 250, 252, 254, 255,
};
// clang-format on
// Inverted `color_curve`, with gaps filled by polynomial interpolation.
// clang-format off: vertically align columns of values
static std::array<uint8_t, 256> reverse_curve{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10,
10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13,
13, 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16,
16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 19, 19,
@@ -48,6 +56,28 @@ static std::array<uint8_t, 256> reverse_curve{
};
// clang-format on
Rgba Rgba::fromCGBColor(uint16_t color, bool useColorCurve) {
uint8_t r = color & 0b11111, g = (color >> 5) & 0b11111, b = (color >> 10) & 0b11111;
if (useColorCurve) {
r = color_curve[r];
g = color_curve[g];
b = color_curve[b];
if (g != b) {
g = round(pow((pow(g / 255.0, 2.2) * 3 + pow(b / 255.0, 2.2)) / 4, 1 / 2.2) * 255);
}
} else {
r = r << 3 | r >> 2;
g = g << 3 | g >> 2;
b = b << 3 | b >> 2;
}
return {
r,
g,
b,
static_cast<uint8_t>(color & transparent ? 0x00 : 0xFF),
};
}
uint16_t Rgba::cgbColor() const {
if (isTransparent()) {
return transparent;