More accurate 8-bit <=> 5-bit RGB color conversion (#1827)
@@ -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),
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 177 B After Width: | Height: | Size: 174 B |
@@ -1 +1 @@
|
|||||||
妉U<EFBFBD><EFBFBD><EFBFBD>
|
媓U<EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 556 B |
@@ -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)
|
||||||
|
|||||||