More accurate 8-bit <=> 5-bit RGB color conversion (#1827)

This commit is contained in:
Rangi
2025-09-08 15:13:25 -04:00
committed by GitHub
parent 65d408eb5d
commit 223b3d1921
20 changed files with 31 additions and 15 deletions

View File

@@ -18,10 +18,7 @@ struct Rgba {
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {} : red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
static constexpr Rgba fromCGBColor(uint16_t color) { static constexpr Rgba fromCGBColor(uint16_t color) {
constexpr auto _5to8 = [](uint8_t channel) -> uint8_t { constexpr auto _5to8 = [](uint8_t c) -> uint8_t { return ((c & 0b11111) * 255 + 15) / 31; };
channel &= 0b11111; // For caller's convenience
return channel << 3 | channel >> 2;
};
return { return {
_5to8(color), _5to8(color),
_5to8(color >> 5), _5to8(color >> 5),

View File

@@ -461,6 +461,24 @@ Previously we had
.Pp .Pp
Instead, now we have Instead, now we have
.Ql p ** q ** r == p ** (q ** r) . .Ql p ** q ** r == p ** (q ** r) .
.Ss 8-bit and 5-bit color conversion
Changed in 1.0.0.
.Pp
RGBGFX takes 8-bit RGB colors as its PNG input, and outputs 5-bit GBC colors.
Its
.Ql -r/--reverse
mode does the opposite 5-bit to 8-bit conversion.
Instead of the previous inaccurate conversions, we now do accurate rounding to the nearest equivalent.
.Pp
Previously to convert an 8-bit color channel to 5-bit, we truncated it as
.Ql c >> 3 ;
and to reverse a 5-bit color channel to 8-bit, we extended it as
.Ql (c << 3) | (c >> 2) .
.Pp
Instead, now we round 8-bit to 5-bit as
.Ql (c * 31 + 127) / 255 ,
and round 5-bit to 8-bit as
.Ql (c * 255 + 15) / 31 .
.Sh BUGS .Sh BUGS
These are misfeatures that may have been possible by mistake. These are misfeatures that may have been possible by mistake.
They do not get deprecated, just fixed. They do not get deprecated, just fixed.

View File

@@ -50,9 +50,10 @@ uint16_t Rgba::cgbColor() const {
g = reverse_curve[g]; g = reverse_curve[g];
b = reverse_curve[b]; b = reverse_curve[b];
} else { } else {
r >>= 3; constexpr auto _8to5 = [](uint8_t c) -> uint8_t { return (c * 31 + 127) / 255; };
g >>= 3; r = _8to5(r);
b >>= 3; g = _8to5(g);
b = _8to5(b);
} }
return r | g << 5 | b << 10; return r | g << 5 | b << 10;
} }

View File

@@ -1,4 +1,4 @@
error: Failed to fit tile colors [$1527, $15cc, $1ab4] in specified palettes error: Failed to fit tile colors [$1527, $15cc, $1a93] in specified palettes
note: The following palette was specified: note: The following palette was specified:
[$1ab4, $15cc] [$1a93, $15cc]
Conversion aborted after 1 error Conversion aborted after 1 error

View File

@@ -1,4 +1,4 @@
error: Failed to fit tile colors [$6c8a, $7f55, $7fff] in specified palettes error: Failed to fit tile colors [$688b, $7f55, $7fff] in specified palettes
note: The following palettes were specified: note: The following palettes were specified:
[$5f77, $213d, $41a6, $40ee] [$5f77, $213d, $41a6, $40ee]
[$3f65, $36b3, $262a, $50b0] [$3f65, $36b3, $262a, $50b0]

View File

@@ -1,5 +1,5 @@
error: Failed to fit tile colors [$6c8a, $7f55, $7fff] in specified palettes error: Failed to fit tile colors [$688b, $7f55, $7fff] in specified palettes
note: The following palettes were specified: note: The following palettes were specified:
[$7fff, $7f55] [$7fff, $7f55]
[$7fff, $6c8a] [$7fff, $688b]
Conversion aborted after 1 error Conversion aborted after 1 error

Binary file not shown.

View File

@@ -1,3 +1,3 @@
warning: Fusing colors #a9b9c9ff and #aabbccff into Game Boy color $66f5 [first seen at x: 1, y: 1] warning: Fusing colors #a9bacbff and #aabbccff into Game Boy color $66f5 [first seen at x: 1, y: 1]
FATAL: Tile (0, 0) contains the background color (#aabbccff)! FATAL: Tile (0, 0) contains the background color (#aabbccff)!
Conversion aborted after 1 error Conversion aborted after 1 error

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -1 +1 @@
U<EFBFBD><EFBFBD><EFBFBD> U<EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 B

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

View File

@@ -144,7 +144,7 @@ static void generate_palettes(uint16_t palettes[/* 60 */][4]) {
* Expand a 5-bit color component to 8 bits with minimal bias * Expand a 5-bit color component to 8 bits with minimal bias
*/ */
static uint8_t _5to8(uint8_t five) { static uint8_t _5to8(uint8_t five) {
return five << 3 | five >> 2; return (five * 255 + 15) / 31;
} }
// Can't mark as `const`, as the array type is otherwise not compatible (augh) // Can't mark as `const`, as the array type is otherwise not compatible (augh)