Last active
February 11, 2025 23:49
-
-
Save TuxSH/e95ba3d34a18b9030c09d709bca84b32 to your computer and use it in GitHub Desktop.
Optimized color conversion function with rounding
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compute round(r*x) where r = (2^outdepth - 1)/(2^indepth - 1) using (a*x + b) >> c as approx. | |
// This results in 3 (or 2 for a = 2^n +- 1) Arm instructions when a < 256. | |
// This can be done by leveraging fixed-point math instead of blindly bruteforcing: | |
// given we actually want to compute floor(r*x + 1/2) in fixed-point, the coefficients become: | |
// * a = round(2^n * r) | |
// * b = 2^(n - 1), or 0 if n = 0 | |
// * c = n, with n so that the approx is satisfied, and if possible a < 256 | |
// Given x < 256 and we have 32-bit registers, this approximation will always work. | |
// It is sometimes possible to add a quantity up to +- c, to negate very small rounding errors, | |
// instead of increasing n. This doesn't always work, however. | |
// The lower r is, the more likely a < 256. | |
// Do note that |a - b| < 0.5 does not mean round(a) != round(b) | |
if constexpr (From == Rgba4) { | |
if constexpr (To == Rgba4) { | |
return c; | |
} else if constexpr (To == Rgb5a1) { | |
return (33*c + 8) >> 3; | |
} else if constexpr (To == Rgb565) { | |
return IsGreen ? (65*c + 16) >> 5 : (33*c + 8) >> 3; | |
} else if constexpr (To == Rgb8 || To == Rgba8) { | |
return 17 * c; | |
} | |
} else if constexpr (From == Rgb5a1) { | |
if constexpr (To == Rgba4) { | |
return (31*c + 32) >> 6; | |
} else if constexpr (To == Rgb5a1) { | |
return c; | |
} else if constexpr (To == Rgb565) { | |
return IsGreen ? (33 * c) >> 4 : c; | |
} else if constexpr (To == Rgb8 || To == Rgba8) { | |
return (527*c + 23) >> 6; // cannot be simplified into u8 immediate :( (approx failure) | |
} | |
} else if constexpr (From == Rgb565) { | |
if constexpr (To == Rgba4) { | |
return IsGreen ? (61*c + 21) >> 8 : (31*c + 32) >> 6; | |
} else if constexpr (To == Rgb5a1) { | |
return IsGreen ? (31*c + 32) >> 6 : c; | |
} else if constexpr (To == Rgb565) { | |
return c; | |
} else if constexpr (To == Rgb8 || To == Rgba8) { | |
return IsGreen ? (259*c + 33) >> 6: (527*c + 23) >> 6; // cannot be simplified into u8 immediate :( | |
} | |
} else if constexpr (From == Rgb8 || From == Rgba8) { | |
if constexpr (To == Rgba4) { | |
return (15*c + 135) >> 8; | |
} else if constexpr (To == Rgb5a1) { | |
return (249*c + 1024) >> 11; | |
} else if constexpr (To == Rgb565) { | |
return IsGreen ? (253*c + 512) >> 10 : (249*c + 1024) >> 11; | |
} else if constexpr (To == Rgb8 || To == Rgba8) { | |
return c; | |
} | |
} | |
ABORT("Bad path in ConvertColorComponent reached"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment