5#error "cielab_impl.h should not be included directly; include cielab.h instead"
12constexpr double DELTA {6.0 / 29.0};
13constexpr double DELTA_CUBED {DELTA * DELTA * DELTA};
14constexpr double KAPPA {1.0 / (3.0 * DELTA * DELTA)};
15constexpr double EPSILON {16.0 / 116.0};
18constexpr double SRGB_LINEAR_THRESHOLD {0.04045};
19constexpr double SRGB_LINEAR_FACTOR {12.92};
20constexpr double SRGB_GAMMA_OFFSET {0.055};
21constexpr double SRGB_GAMMA {2.4};
22constexpr double SRGB_GAMMA_INV {1.0 / SRGB_GAMMA};
38constexpr double SRGB_R_TO_X {0.4124564};
39constexpr double SRGB_G_TO_X {0.3575761};
40constexpr double SRGB_B_TO_X {0.1804375};
41constexpr double SRGB_R_TO_Y {0.2126729};
42constexpr double SRGB_G_TO_Y {0.7151522};
43constexpr double SRGB_B_TO_Y {0.0721750};
44constexpr double SRGB_R_TO_Z {0.0193339};
45constexpr double SRGB_G_TO_Z {0.1191920};
46constexpr double SRGB_B_TO_Z {0.9503041};
64constexpr double SRGB_X_TO_R {3.240970};
65constexpr double SRGB_Y_TO_R {-1.537383};
66constexpr double SRGB_Z_TO_R {-0.498611};
67constexpr double SRGB_X_TO_G {-0.969244};
68constexpr double SRGB_Y_TO_G {1.875968};
69constexpr double SRGB_Z_TO_G {0.041555};
70constexpr double SRGB_X_TO_B {0.055630};
71constexpr double SRGB_Y_TO_B {-0.203977};
72constexpr double SRGB_Z_TO_B {1.056972};
75constexpr double D65_Xn {0.95047};
76constexpr double D65_Yn {1.0};
77constexpr double D65_Zn {1.08883};
79constexpr double LAB_L_FACTOR {116.0};
80constexpr double LAB_L_OFFSET {16.0};
81constexpr double LAB_A_FACTOR {500.0};
82constexpr double LAB_B_FACTOR {200.0};
85inline double xyz_to_lab(
const double t) {
87 const double safe_t {std::max(0.0, t)};
88 return (safe_t > DELTA_CUBED) ? std::cbrt(safe_t) : (KAPPA * safe_t) + EPSILON;
93inline double srgb_to_linear(
const double c) {
94 const double safe_c {std::clamp(c, 0.0, 1.0)};
95 return (safe_c <= SRGB_LINEAR_THRESHOLD)
96 ? safe_c / SRGB_LINEAR_FACTOR
97 : std::pow((safe_c + SRGB_GAMMA_OFFSET) / (1.0 + SRGB_GAMMA_OFFSET), SRGB_GAMMA);
100template <
typename Tin,
typename Tout>
102 const Tin r_u8,
const Tin g_u8,
const Tin b_u8, Tout& out_l, Tout& out_a, Tout& out_b
106 double _r =
static_cast<double>(r_u8);
107 double _g =
static_cast<double>(g_u8);
108 double _b =
static_cast<double>(b_u8);
110 double r {srgb_to_linear(_r / 255.0)};
111 double g {srgb_to_linear(_g / 255.0)};
112 double b {srgb_to_linear(_b / 255.0)};
116 const double x {SRGB_R_TO_X * r + SRGB_G_TO_X * g + SRGB_B_TO_X * b};
117 const double y {SRGB_R_TO_Y * r + SRGB_G_TO_Y * g + SRGB_B_TO_Y * b};
118 const double z {SRGB_R_TO_Z * r + SRGB_G_TO_Z * g + SRGB_B_TO_Z * b};
121 const double Xr {x / D65_Xn};
122 const double Yr {y / D65_Yn};
123 const double Zr {z / D65_Zn};
126 const double fx {xyz_to_lab(Xr)};
127 const double fy {xyz_to_lab(Yr)};
128 const double fz {xyz_to_lab(Zr)};
131 out_l =
static_cast<Tout
>(LAB_L_FACTOR * fy - LAB_L_OFFSET);
132 out_a =
static_cast<Tout
>(LAB_A_FACTOR * (fx - fy));
133 out_b =
static_cast<Tout
>(LAB_B_FACTOR * (fy - fz));
135 out_l = std::clamp(out_l,
static_cast<Tout
>(0.0),
static_cast<Tout
>(100.0));
138constexpr double inverse_xyz_to_lab(
double t) {
139 return (t > DELTA) ? (t * t * t) : (3 * DELTA * DELTA * (t - EPSILON));
142inline double gamma_encode(
double u) {
144 u = std::max(0.0, u);
145 return (u <= SRGB_LINEAR_THRESHOLD / SRGB_LINEAR_FACTOR)
146 ? SRGB_LINEAR_FACTOR * u
147 : (1.0 + SRGB_GAMMA_OFFSET) * std::pow(u, SRGB_GAMMA_INV) - SRGB_GAMMA_OFFSET;
150template <
typename Tin,
typename Tout>
152 const Tin L,
const Tin A,
const Tin B, Tout& out_r_u8, Tout& out_g_u8, Tout& out_b_u8
154 const double _L =
static_cast<double>(L);
155 const double _A =
static_cast<double>(A);
156 const double _B =
static_cast<double>(B);
159 const double fy {(_L + LAB_L_OFFSET) / LAB_L_FACTOR};
160 const double fx {fy + _A / LAB_A_FACTOR};
161 const double fz {fy - _B / LAB_B_FACTOR};
163 const double X {D65_Xn * inverse_xyz_to_lab(fx)};
164 const double Y {D65_Yn * inverse_xyz_to_lab(fy)};
165 const double Z {D65_Zn * inverse_xyz_to_lab(fz)};
168 double r {SRGB_X_TO_R * X + SRGB_Y_TO_R * Y + SRGB_Z_TO_R * Z};
169 double g {SRGB_X_TO_G * X + SRGB_Y_TO_G * Y + SRGB_Z_TO_G * Z};
170 double b {SRGB_X_TO_B * X + SRGB_Y_TO_B * Y + SRGB_Z_TO_B * Z};
173 r = gamma_encode(std::clamp(r, 0.0, 1.0));
174 g = gamma_encode(std::clamp(g, 0.0, 1.0));
175 b = gamma_encode(std::clamp(b, 0.0, 1.0));
178 out_r_u8 =
static_cast<Tout
>(std::round(255.0 * std::clamp(r, 0.0, 1.0)));
179 out_g_u8 =
static_cast<Tout
>(std::round(255.0 * std::clamp(g, 0.0, 1.0)));
180 out_b_u8 =
static_cast<Tout
>(std::round(255.0 * std::clamp(b, 0.0, 1.0)));
183template <
typename Tin,
typename Tout>
189 rgb_to_lab(rgba.red, rgba.green, rgba.blue, l, a, b);
195 laba.alpha =
static_cast<Tout
>(rgba.alpha);
198template <
typename Tin,
typename Tout>
200 rgb_to_lab<Tin, Tout>(rgb.red, rgb.green, rgb.blue, lab.l, lab.a, lab.b);
203template <
typename Tin,
typename Tout>
209 lab_to_rgb<Tin, Tout>(laba.l, laba.a, laba.b, r, g, b);
215 rgba.alpha =
static_cast<Tout
>(laba.alpha);
218template <
typename Tin,
typename Tout>
220 lab_to_rgb<Tin, Tout>(lab.l, lab.a, lab.b, rgb.red, rgb.green, rgb.blue);