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

Function compute_eta_from_input

src/fl/gfx/rgbww.cpp.hpp:198–255  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

196}
197
198inline float compute_eta_from_input(const colorimetric_detail::RgbcctProfile& profile,
199 float s_r, float s_g, float s_b) FL_NOEXCEPT {
200 // Compute the input chromaticity in the *source* color space (#2705) when
201 // the warm path carries populated input_xy_* fields. Falling back to the
202 // emitter-space projection used previously would bias eta toward the LED
203 // gamut, so a neutral source white wouldn't blend symmetrically across
204 // warm/cool when the source primaries (e.g. sRGB) differ from the LED
205 // primaries — exactly the regression flagged on this PR.
206 EtaSourceMatrixCache& cache =
207 fl::Singleton<EtaSourceMatrixCache>::instance();
208 const fl::DiodeProfile& wp = profile.warm_path;
209 const bool key_changed =
210 !cache.initialized ||
211 !xy_equal(cache.xy_r, wp.input_xy_r) ||
212 !xy_equal(cache.xy_g, wp.input_xy_g) ||
213 !xy_equal(cache.xy_b, wp.input_xy_b) ||
214 !xy_equal(cache.xy_w, wp.input_xy_w);
215 if (key_changed) {
216 cache.xy_r[0] = wp.input_xy_r[0]; cache.xy_r[1] = wp.input_xy_r[1];
217 cache.xy_g[0] = wp.input_xy_g[0]; cache.xy_g[1] = wp.input_xy_g[1];
218 cache.xy_b[0] = wp.input_xy_b[0]; cache.xy_b[1] = wp.input_xy_b[1];
219 cache.xy_w[0] = wp.input_xy_w[0]; cache.xy_w[1] = wp.input_xy_w[1];
220 cache.has_M_src = wp.input_xy_w[1] > 1e-6f &&
221 colorimetric_detail::build_source_matrix(
222 wp.input_xy_r, wp.input_xy_g,
223 wp.input_xy_b, wp.input_xy_w, cache.M_src);
224 cache.initialized = true;
225 }
226
227 float X = 0.0f, Y = 0.0f, Z = 0.0f;
228 if (cache.has_M_src) {
229 const float s[3] = { s_r, s_g, s_b };
230 float xyz[3];
231 colorimetric_detail::matvec3(cache.M_src, s, xyz);
232 X = xyz[0]; Y = xyz[1]; Z = xyz[2];
233 } else {
234 // Legacy emitter-space fallback for profiles without populated source.
235 // Three xyY_to_XYZ calls per pixel — degraded but still cheap, and
236 // only reached when the user explicitly opts out of source space.
237 float P_R[3], P_G[3], P_B[3];
238 colorimetric_detail::xyY_to_XYZ(wp.xy_r[0], wp.xy_r[1], wp.lum_r, P_R);
239 colorimetric_detail::xyY_to_XYZ(wp.xy_g[0], wp.xy_g[1], wp.lum_g, P_G);
240 colorimetric_detail::xyY_to_XYZ(wp.xy_b[0], wp.xy_b[1], wp.lum_b, P_B);
241 X = P_R[0]*s_r + P_G[0]*s_g + P_B[0]*s_b;
242 Y = P_R[1]*s_r + P_G[1]*s_g + P_B[1]*s_b;
243 Z = P_R[2]*s_r + P_G[2]*s_g + P_B[2]*s_b;
244 }
245 const float sum = X + Y + Z;
246 if (sum < 1e-9f) return 0.5f;
247 const float input_x = X / sum;
248 const float warm_x = profile.warm_path.xy_w[0];
249 const float cool_x = profile.cool_path.xy_w[0];
250 if (cool_x == warm_x) return 0.5f;
251 const float eta = (input_x - warm_x) / (cool_x - warm_x);
252 if (eta < 0.0f) return 0.0f;
253 if (eta > 1.0f) return 1.0f;
254 return eta;
255}

Callers 2

rgb_2_rgbww_colorimetricFunction · 0.85

Calls 4

xy_equalFunction · 0.85
build_source_matrixFunction · 0.85
matvec3Function · 0.85
xyY_to_XYZFunction · 0.85

Tested by

no test coverage detected