43 const float T =
static_cast<float>(
44 (cct < 1500) ? 1500 : ((cct > 15000) ? 15000 : cct));
45 const float T2 = T * T;
46 const float u_num = 0.860117757f + 1.54118254e-4f * T + 1.28641212e-7f * T2;
47 const float u_den = 1.0f + 8.42420235e-4f * T + 7.08145163e-7f * T2;
48 const float v_num = 0.317398726f + 4.22806245e-5f * T + 4.20481691e-8f * T2;
49 const float v_den = 1.0f - 2.89741816e-5f * T + 1.61456053e-7f * T2;
50 const float u = u_num / u_den;
51 const float v = v_num / v_den;
52 const float den = 2.0f * u - 8.0f * v + 4.0f;
53 out[0] = 3.0f * u / den;
54 out[1] = 2.0f * v / den;
59 out[0] = out[1] = out[2] = 0.0f;
62 const float inv_y = 1.0f /
y;
63 out[0] =
x * Y * inv_y;
65 out[2] = (1.0f -
x -
y) * Y * inv_y;
106 float largest =
x >
y ?
x :
y;
116 const float a = in[0][0], b = in[0][1], c = in[0][2];
117 const float d = in[1][0], e = in[1][1], f = in[1][2];
118 const float g = in[2][0], h = in[2][1], i = in[2][2];
119 const float det = a * (e * i - f * h) - b * (d * i - f *
g) + c * (d * h - e *
g);
142 const float an = a / scale_a, dn = d / scale_a, gn =
g / scale_a;
143 const float bn = b / scale_b, en = e / scale_b, hn = h / scale_b;
144 const float cn = c / scale_c, fn = f / scale_c, in_ = i / scale_c;
145 const float det_normalized = an * (en * in_ - fn * hn) -
146 bn * (dn * in_ - fn * gn) +
147 cn * (dn * hn - en * gn);
152 const float inv_det = 1.0f / det;
153 out[0][0] = (e * i - f * h) * inv_det;
154 out[0][1] = (c * h - b * i) * inv_det;
155 out[0][2] = (b * f - c * e) * inv_det;
156 out[1][0] = (f *
g - d * i) * inv_det;
157 out[1][1] = (a * i - c *
g) * inv_det;
158 out[1][2] = (c * d - a * f) * inv_det;
159 out[2][0] = (d * h - e *
g) * inv_det;
160 out[2][1] = (b *
g - a * h) * inv_det;
161 out[2][2] = (a * e - b * d) * inv_det;
166 out[0] = M[0][0] * v[0] + M[0][1] * v[1] + M[0][2] * v[2];
167 out[1] = M[1][0] * v[0] + M[1][1] * v[1] + M[1][2] * v[2];
168 out[2] = M[2][0] * v[0] + M[2][1] * v[1] + M[2][2] * v[2];
173 const float v0x =
B[0] - A[0], v0y =
B[1] - A[1];
174 const float v1x = C[0] - A[0], v1y = C[1] - A[1];
175 const float v2x =
t[0] - A[0], v2y =
t[1] - A[1];
176 const float d00 = v0x * v0x + v0y * v0y;
177 const float d01 = v0x * v1x + v0y * v1y;
178 const float d11 = v1x * v1x + v1y * v1y;
179 const float d20 = v2x * v0x + v2y * v0y;
180 const float d21 = v2x * v1x + v2y * v1y;
181 const float den = d00 * d11 - d01 * d01;
185 const float inv_den = 1.0f / den;
186 const float u = (d11 * d20 - d01 * d21) * inv_den;
187 const float v = (d00 * d21 - d01 * d20) * inv_den;
188 bary[0] = 1.0f - u - v;
195 const float scaled = v * 255.0f + 0.5f;
196 if (scaled <= 0.0f)
return 0;
197 if (scaled >= 255.0f)
return 255;
198 return static_cast<u8>(scaled);
209 const float xy_b[2],
const float xy_w[2],
211 float xyz_R[3], xyz_G[3], xyz_B[3], xyz_W[3];
218 P[0][0] = xyz_R[0];
P[0][1] = xyz_G[0];
P[0][2] = xyz_B[0];
219 P[1][0] = xyz_R[1];
P[1][1] = xyz_G[1];
P[1][2] = xyz_B[1];
220 P[2][0] = xyz_R[2];
P[2][1] = xyz_G[2];
P[2][2] = xyz_B[2];
229 M_out[0][0] = k[0] * xyz_R[0]; M_out[0][1] = k[1] * xyz_G[0]; M_out[0][2] = k[2] * xyz_B[0];
230 M_out[1][0] = k[0] * xyz_R[1]; M_out[1][1] = k[1] * xyz_G[1]; M_out[1][2] = k[2] * xyz_B[1];
231 M_out[2][0] = k[0] * xyz_R[2]; M_out[2][1] = k[1] * xyz_G[2]; M_out[2][2] = k[2] * xyz_B[2];
242inline void nnls3(
const float M[3][3],
const float b[3],
244 float t[3] = {0.0f, 0.0f, 0.0f};
245 constexpr float kStep = 0.01f;
246 constexpr int kIters = 500;
247 for (
int it = 0; it < kIters; ++it) {
249 r[0] = M[0][0]*
t[0] + M[0][1]*
t[1] + M[0][2]*
t[2] - b[0];
250 r[1] = M[1][0]*
t[0] + M[1][1]*
t[1] + M[1][2]*
t[2] - b[1];
251 r[2] = M[2][0]*
t[0] + M[2][1]*
t[1] + M[2][2]*
t[2] - b[2];
254 g[0] = M[0][0]*r[0] + M[1][0]*r[1] + M[2][0]*r[2];
255 g[1] = M[0][1]*r[0] + M[1][1]*r[1] + M[2][1]*r[2];
256 g[2] = M[0][2]*r[0] + M[1][2]*r[1] + M[2][2]*r[2];
257 for (
int j = 0; j < 3; ++j) {
258 float v =
t[j] - kStep *
g[j];
259 t[j] = v > 0.0f ? v : 0.0f;
262 if (residual_out !=
nullptr) {
264 r[0] = M[0][0]*
t[0] + M[0][1]*
t[1] + M[0][2]*
t[2] - b[0];
265 r[1] = M[1][0]*
t[0] + M[1][1]*
t[1] + M[1][2]*
t[2] - b[1];
266 r[2] = M[2][0]*
t[0] + M[2][1]*
t[1] + M[2][2]*
t[2] - b[2];
267 *residual_out =
fl::sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);
269 t_out[0] =
t[0]; t_out[1] =
t[1]; t_out[2] =
t[2];
302 const float t2 =
t *
t;
303 const float t3 = t2 *
t;
304 out[0] = 2.0f * t3 - 3.0f * t2 + 1.0f;
305 out[1] = -2.0f * t3 + 3.0f * t2;
306 out[2] = t3 - 2.0f * t2 +
t;
311 const float scaled = v *
static_cast<float>(
kLutQ) + 0.5f;
312 if (scaled <= -32768.0f)
return -32768;
313 if (scaled >= 32767.0f)
return 32767;
314 return static_cast<i16
>(scaled);
369 float red_y,
float green_y,
float blue_y,
370 const char* provenance =
nullptr,
372 return EmitterProfile{{red.
x, red.
y}, {green.
x, green.
y}, {blue.
x, blue.
y}, red_y, green_y, blue_y,
373 {0, 0}, {0, 0}, {0, 0}, {0, 0}, profile_id,
nullptr,
nullptr,
nullptr, 0, 8,
376 nullptr,
nullptr, 0, 0};
382 float green_y,
float blue_y,
384 const char* provenance =
nullptr,
387 red_y, green_y, blue_y,
388 {0, 0}, {0, 0}, {0, 0}, {0, 0}, profile_id,
389 nullptr,
nullptr,
nullptr, 0, 8,
393 white_y, 0,
nullptr,
nullptr, 0, 0};
400 float green_y,
float blue_y,
401 float warm_white_y,
float cool_white_y,
402 const char* provenance =
nullptr,
405 red_y, green_y, blue_y,
406 {0, 0}, {0, 0}, {0, 0}, {0, 0}, profile_id,
407 nullptr,
nullptr,
nullptr, 0, 8,
411 {warm_white.
x, warm_white.
y},
412 {cool_white.
x, cool_white.
y},
413 warm_white_y, cool_white_y,
414 nullptr,
nullptr, 0, 0};
425#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6
429 "ws2812b/placeholder/uncalibrated",
Chromaticity(.640f, .330f),
431 "placeholder",
"uncalibrated");
467 xyY_to_XYZ(p.xy_r[0], p.xy_r[1], p.lum_r, cache->P_R);
468 xyY_to_XYZ(p.xy_g[0], p.xy_g[1], p.lum_g, cache->P_G);
469 xyY_to_XYZ(p.xy_b[0], p.xy_b[1], p.lum_b, cache->P_B);
471 const float P_RGB[3][3] = {
472 { cache->P_R[0], cache->P_G[0], cache->P_B[0] },
473 { cache->P_R[1], cache->P_G[1], cache->P_B[1] },
474 { cache->P_R[2], cache->P_G[2], cache->P_B[2] },
476 const bool ok_rgb =
invert3x3(P_RGB, cache->P_RGB_inv);
477 const bool declares_source_space = (p.input_xy_w[1] > 1e-6f);
478 cache->has_source_space = declares_source_space
480 p.input_xy_b, p.input_xy_w,
482 if (cache->has_source_space && ok_rgb) {
484 xyY_to_XYZ(p.input_xy_w[0], p.input_xy_w[1], 1.0f, X_w);
490 const float scale_k = 1.0f / mt;
491 for (
int i = 0; i < 3; ++i) {
492 for (
int j = 0; j < 3; ++j) {
493 cache->M_src[i][j] *= scale_k;
499 for (
int i = 0; i < 3; ++i) {
500 for (
int j = 0; j < 3; ++j) {
501 cache->P_RGB_inv[i][j] = 0.0f;
505 if (!cache->has_source_space) {
506 for (
int i = 0; i < 3; ++i) {
507 for (
int j = 0; j < 3; ++j) {
508 cache->M_src[i][j] = 0.0f;
512 if (declares_source_space && !cache->has_source_space) {
523 float s_g,
float s_b,
525 if (cache.has_source_space) {
526 const float s[3] = { s_r, s_g, s_b };
529 X_t[0] = cache.P_R[0] * s_r + cache.P_G[0] * s_g + cache.P_B[0] * s_b;
530 X_t[1] = cache.P_R[1] * s_r + cache.P_G[1] * s_g + cache.P_B[1] * s_b;
531 X_t[2] = cache.P_R[2] * s_r + cache.P_G[2] * s_g + cache.P_B[2] * s_b;
538 float s_r,
float s_g,
float s_b,
542 matvec3(cache.P_RGB_inv, X_t, out_rgb);
543 out_rgb[0] =
fl::max(out_rgb[0], 0.0f);
544 out_rgb[1] =
fl::max(out_rgb[1], 0.0f);
545 out_rgb[2] =
fl::max(out_rgb[2], 0.0f);
546 const float m =
fl::max(
fl::max(out_rgb[0], out_rgb[1]), out_rgb[2]);
548 const float inv_m = 1.0f / m;
constexpr float kRelativeSingularity
How small a determinant may be, relative to the matrix's own scale, before the inverse is rounding no...
float columnScale(float a, float b, float c) FL_NO_EXCEPT
Largest absolute entry of a column, which is its infinity norm.
static constexpr EmitterProfile WS2812B
EmitterProfile RgbColorimetricProfile
u8 quantize_u8(float v) FL_NO_EXCEPT
void hermite_basis(float t, float out[4]) FL_NO_EXCEPT
void matvec3(const float M[3][3], const float v[3], float out[3]) FL_NO_EXCEPT
bool invert3x3(const float in[3][3], float out[3][3]) FL_NO_EXCEPT
bool barycentric_xy(const float t[2], const float A[2], const float B[2], const float C[2], float bary[3]) FL_NO_EXCEPT
constexpr int kLutStrideBilinear
void nnls3(const float M[3][3], const float b[3], float t_out[3], float *residual_out) FL_NO_EXCEPT
bool build_source_matrix(const float xy_r[2], const float xy_g[2], const float xy_b[2], const float xy_w[2], float M_out[3][3]) FL_NO_EXCEPT
constexpr int kLutStrideHermite
void rgb_source_to_XYZ(const RgbColorimetricCache &cache, float s_r, float s_g, float s_b, float X_t[3]) FL_NO_EXCEPT
void xyY_to_XYZ(float x, float y, float Y, float out[3]) FL_NO_EXCEPT
bool solve_rgb_colorimetric(const RgbColorimetricCache &cache, float s_r, float s_g, float s_b, float out_rgb[3]) FL_NO_EXCEPT
void cct_to_xy(int cct, float out[2]) FL_NO_EXCEPT
bool build_rgb_colorimetric_cache(const EmitterProfile &p, RgbColorimetricCache *cache) FL_NO_EXCEPT
i16 quantize_lut_cell(float v) FL_NO_EXCEPT
constexpr common_type_t< T, U > max(T a, U b) FL_NO_EXCEPT
FL_DISABLE_WARNING_PUSH unsigned char * B
FASTLED_FORCE_INLINE fl::u8 P(fl::u8 x)
InputGamut g FL_NO_EXCEPT
double fabs(double value) FL_NO_EXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type sqrt(T x) FL_NO_EXCEPT
Base definition for an LED controller.
CIE 1931 xy chromaticity, independent of encoded storage and chipset.
float electrical_milliwatts_white1
float electrical_milliwatts_b
float electrical_milliwatts_white2
float reference_temperature_c
const char * provenance_kind
const u16 * response_lut_r
const u16 * response_lut_white1
static constexpr EmitterProfile rgb(const char *profile_id, Chromaticity red, Chromaticity green, Chromaticity blue, float red_y, float green_y, float blue_y, const char *provenance=nullptr, const char *report=nullptr) FL_NO_EXCEPT
const u16 * response_lut_g
const u16 * response_lut_b
static constexpr EmitterProfile rgbww(const char *profile_id, Chromaticity red, Chromaticity green, Chromaticity blue, Chromaticity warm_white, Chromaticity cool_white, float red_y, float green_y, float blue_y, float warm_white_y, float cool_white_y, const char *provenance=nullptr, const char *report=nullptr) FL_NO_EXCEPT
FiveBitSemantics five_bit_semantics
static constexpr EmitterProfile rgbw(const char *profile_id, Chromaticity red, Chromaticity green, Chromaticity blue, Chromaticity white, float red_y, float green_y, float blue_y, float white_y, const char *provenance=nullptr, const char *report=nullptr) FL_NO_EXCEPT
const u16 * response_lut_white2
float electrical_milliwatts_r
float electrical_idle_milliwatts
float electrical_milliwatts_g