Skip to content

Instantly share code, notes, and snippets.

@TuxSH
Last active February 11, 2025 23:49
Show Gist options
  • Save TuxSH/e95ba3d34a18b9030c09d709bca84b32 to your computer and use it in GitHub Desktop.
Save TuxSH/e95ba3d34a18b9030c09d709bca84b32 to your computer and use it in GitHub Desktop.
Optimized color conversion function with rounding
// 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