Created
August 17, 2025 16:12
-
-
Save memononen/f67961d16c1ce27e2322c0fbe71214db to your computer and use it in GitHub Desktop.
hsluv
This file contains hidden or 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
| static const double ref_u = 0.19783000664283680764; | |
| static const double ref_v = 0.46831999493879100370; | |
| static const double kappa = 903.29629629629629629630; | |
| static const double epsilon = 0.00885645167903563082; | |
| static const double xyz_to_rgb[9] = { | |
| 3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366, | |
| -0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247, | |
| 0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072, | |
| }; | |
| static double max_chroma_for_lh(double l, double hu, double hv) | |
| { | |
| double min_len = DBL_MAX; | |
| const double tl = l + 16.0; | |
| const double sub1 = (tl * tl * tl) / 1560896.0; | |
| const double sub2 = (sub1 > epsilon ? sub1 : (l / kappa)); | |
| for (int i = 0; i < 3; i++) { | |
| // Intersect hue with RGB gamut bounds | |
| const double m1 = xyz_to_rgb[i*3 + 0]; | |
| const double m2 = xyz_to_rgb[i*3 + 1]; | |
| const double m3 = xyz_to_rgb[i*3 + 2]; | |
| double top1 = (284517.0 * m1 - 94839.0 * m3) * sub2; | |
| double top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2; | |
| double bottom = (632260.0 * m3 - 126452.0 * m2) * sub2; | |
| const double line0_a = top1 / bottom; | |
| const double line0_b = top2 / bottom; | |
| top2 -= 769860.0 * l; | |
| bottom += 126452.0; | |
| const double line1_a = top1 / bottom; | |
| const double line1_b = top2 / bottom; | |
| // Intersect ray from (0,0) towards the hue direction against the gamut lines. | |
| double len = line0_b / (hv - line0_a * hu); | |
| if (len >= 0.0 && len < min_len) | |
| min_len = len; | |
| len = line1_b / (hv - line1_a * hu); | |
| if (len >= 0.0 && len < min_len) | |
| min_len = len; | |
| } | |
| return min_len; | |
| } | |
| static double from_linear(double c) | |
| { | |
| const double v = (c <= 0.0031308) ? 12.92 * c : (1.055 * pow(c, 1.0 / 2.4) - 0.055); | |
| if (v < 0.0) return 0.0; | |
| if (v > 1.0) return 1.0; | |
| return v; | |
| } | |
| #define ARB_PI 3.14159265359 | |
| arb_color_t hsluv_to_rgb(double h, double s, double l) | |
| { | |
| if (l <= 0.00000001) | |
| return (arb_color_t){0, 0, 0, 255 }; | |
| if (l >= 99.9999999) | |
| return (arb_color_t){255, 255, 255, 255 }; | |
| const double hrad = h * ARB_PI / 180.0; | |
| const double hu = cos(hrad); | |
| const double hv = sin(hrad); | |
| // hsluv2lch | |
| const double c = max_chroma_for_lh(l, hu, hv) / 100.0 * s; | |
| // lch2xyz | |
| const double var_u = (hu * c) / (13.0 * l) + ref_u; | |
| const double var_v = (hv * c) / (13.0 * l) + ref_v; | |
| const double y = (l <= 8.0) ? (l / kappa) : pow((l + 16.0) / 116.0, 3.0); | |
| const double x = -(9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v); | |
| const double z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v); | |
| // xyz2rgb | |
| const double r = from_linear(x * xyz_to_rgb[0] + y * xyz_to_rgb[1] + z * xyz_to_rgb[2]); | |
| const double g = from_linear(x * xyz_to_rgb[3] + y * xyz_to_rgb[4] + z * xyz_to_rgb[5]); | |
| const double b = from_linear(x * xyz_to_rgb[6] + y * xyz_to_rgb[7] + z * xyz_to_rgb[8]); | |
| return (arb_color_t) { | |
| .r = (uint8_t)(r * 255.0), | |
| .g = (uint8_t)(g * 255.0), | |
| .b = (uint8_t)(b * 255.0), | |
| .a = 255, | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment