MCPcopy Create free account
hub / github.com/FastLED/FastLED / build_profile_cache

Function build_profile_cache

src/fl/gfx/rgbw_colorimetric.cpp.hpp:62–171  ·  view source on GitHub ↗

Build all per-profile quantities once so the per-pixel runtime path is just a few matrix-vector operations and tiny fixed-size solves. Important scale convention: P_R/P_G/P_B/P_W are measured emitter columns in absolute device XYZ/Y. build_source_matrix() returns a normalized source matrix where white has Y=1. We scale M_src by the same white-fit factor used by the reference math model so source

Source from the content-addressed store, hash-verified

60// a Y=1 source target against hundreds/thousands of device-Y emitter units.
61// The result is underpowered solves and the wrong active-channel ratios.
62void build_profile_cache(const DiodeProfile* p, int cct_override,
63 ProfileCache* cache) FL_NOEXCEPT {
64 cache->profile = p;
65 xyY_to_XYZ(p->xy_r[0], p->xy_r[1], p->lum_r, cache->P_R);
66 xyY_to_XYZ(p->xy_g[0], p->xy_g[1], p->lum_g, cache->P_G);
67 xyY_to_XYZ(p->xy_b[0], p->xy_b[1], p->lum_b, cache->P_B);
68 cache->xy_w[0] = p->xy_w[0];
69 cache->xy_w[1] = p->xy_w[1];
70 if (cct_override >= 1500 && cct_override <= 15000) {
71 cct_to_xy(cct_override, cache->xy_w);
72 }
73 xyY_to_XYZ(cache->xy_w[0], cache->xy_w[1], p->lum_w, cache->P_W);
74
75 auto pack = [](const float* a, const float* b, const float* c,
76 float out[3][3]) FL_NOEXCEPT {
77 out[0][0] = a[0]; out[0][1] = b[0]; out[0][2] = c[0];
78 out[1][0] = a[1]; out[1][1] = b[1]; out[1][2] = c[1];
79 out[2][0] = a[2]; out[2][1] = b[2]; out[2][2] = c[2];
80 };
81
82 float P_RGB[3][3], P_RGW[3][3], P_RBW[3][3], P_BGW[3][3];
83 pack(cache->P_R, cache->P_G, cache->P_B, P_RGB);
84 pack(cache->P_R, cache->P_G, cache->P_W, P_RGW);
85 pack(cache->P_R, cache->P_B, cache->P_W, P_RBW);
86 pack(cache->P_B, cache->P_G, cache->P_W, P_BGW);
87
88 // Inverting a singular matrix leaves the destination with whatever
89 // invert3x3 wrote — solvers reading it would silently produce garbage.
90 // Warn once if any inversion fails so the user knows their profile has
91 // degenerate primaries (colinear chromaticities, near-zero luminance, etc).
92 const bool ok_rgb = invert3x3(P_RGB, cache->P_RGB_inv);
93 const bool ok_rgw = invert3x3(P_RGW, cache->P_RGW_inv);
94 const bool ok_rbw = invert3x3(P_RBW, cache->P_RBW_inv);
95 const bool ok_bgw = invert3x3(P_BGW, cache->P_BGW_inv);
96 if (!(ok_rgb && ok_rgw && ok_rbw && ok_bgw)) {
97 FL_WARN_ONCE("RGBW colorimetric: profile has degenerate primaries — "
98 "one or more sub-gamut matrix inversions failed. Output "
99 "colors will be incorrect. Check DiodeProfile xy/lum values.");
100 // Zero-init any failed inverse so downstream matvec3() output is
101 // deterministic (zero) rather than UB-valued garbage.
102 auto zero3x3 = [](float m[3][3]) FL_NOEXCEPT {
103 for (int i = 0; i < 3; ++i)
104 for (int j = 0; j < 3; ++j) m[i][j] = 0.0f;
105 };
106 if (!ok_rgb) zero3x3(cache->P_RGB_inv);
107 if (!ok_rgw) zero3x3(cache->P_RGW_inv);
108 if (!ok_rbw) zero3x3(cache->P_RBW_inv);
109 if (!ok_bgw) zero3x3(cache->P_BGW_inv);
110 }
111
112 matvec3(cache->P_RGB_inv, cache->P_W, cache->d_W);
113
114 // Source-space matrix (#2705). When input_xy_w is degenerate (the
115 // legacy value-initialized case `DiodeProfile{}`), leave M_src empty
116 // and signal the solvers to fall back to the device-emitter projection.
117 cache->has_source_space = (p->input_xy_w[1] > 1e-6f)
118 && build_source_matrix(p->input_xy_r, p->input_xy_g,
119 p->input_xy_b, p->input_xy_w,

Callers 1

solve_rgbcctFunction · 0.70

Calls 7

xyY_to_XYZFunction · 0.85
cct_to_xyFunction · 0.85
invert3x3Function · 0.85
matvec3Function · 0.85
build_source_matrixFunction · 0.85
maxFunction · 0.85
fabsFunction · 0.85

Tested by

no test coverage detected