19#define FASTLED_INTERNAL
37 *r = 0; *g = 0; *b = 0; *ww = 0; *wc = 0;
55 { 0.700606f, 0.299300f },
56 { 0.097940f, 0.831593f },
57 { 0.129086f, 0.049450f },
58 { 0.460000f, 0.410700f },
64 { 0.700606f, 0.299300f },
65 { 0.097940f, 0.831593f },
66 { 0.129086f, 0.049450f },
67 { 0.31272f, 0.32903f },
70 { 0.700606f, 0.299300f },
71 { 0.097940f, 0.831593f },
72 { 0.129086f, 0.049450f },
73 { 0.313500f, 0.323700f },
79 { 0.700606f, 0.299300f },
80 { 0.097940f, 0.831593f },
81 { 0.129086f, 0.049450f },
82 { 0.31272f, 0.32903f },
100 if (profile ==
nullptr) {
101 state.has_profile =
false;
104 state.profile = *profile;
105 state.has_profile =
true;
130 u8 *out_b0,
u8 *out_b1,
u8 *out_b2,
134 const u8 enc =
static_cast<u8>(ww_placement);
135 const u8 ww_idx = (enc >> 4) & 0x07;
136 const u8 wc_idx = enc & 0x07;
141 const u8 rgb_bytes[3] = { b0, b1, b2 };
142 for (
u8 k = 0; k < 5; ++k) {
143 if (k != ww_idx && k != wc_idx) {
144 out[k] = rgb_bytes[b_idx++];
156 u8 r_scale,
u8 g_scale,
u8 b_scale,
157 u8 *out_r,
u8 *out_g,
u8 *out_b,
162 zero_out(out_r, out_g, out_b, out_ww, out_wc);
165 fn(cfg, r, g, b, r_scale, g_scale, b_scale,
166 out_r, out_g, out_b, out_ww, out_wc);
170#if FASTLED_RGBW_COLORIMETRIC
184struct EtaSourceMatrixCache {
185 float xy_r[2] = {0.0f, 0.0f};
186 float xy_g[2] = {0.0f, 0.0f};
187 float xy_b[2] = {0.0f, 0.0f};
188 float xy_w[2] = {0.0f, 0.0f};
189 float M_src[3][3] = {{0}};
190 bool has_M_src =
false;
191 bool initialized =
false;
194inline bool xy_equal(
const float a[2],
const float b[2])
FL_NOEXCEPT {
195 return a[0] == b[0] && a[1] == b[1];
206 EtaSourceMatrixCache& cache =
208 const fl::DiodeProfile& wp = profile.warm_path;
209 const bool key_changed =
210 !cache.initialized ||
224 cache.initialized =
true;
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 };
232 X = xyz[0]; Y = xyz[1]; Z = xyz[2];
237 float P_R[3], P_G[3], P_B[3];
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;
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;
265resolve_active_rgbcct_profile(
const Rgbww& cfg,
267 if (cfg.profile !=
nullptr) {
271 if (
static_cast<int>(cfg.warm_cct) == base->warm_path.nominal_cct
272 &&
static_cast<int>(cfg.cool_cct) == base->cool_path.nominal_cct) {
277 if (
static_cast<int>(cfg.warm_cct) != base->warm_path.nominal_cct) {
280 scratch.warm_path.xy_w[0] =
xy[0];
281 scratch.warm_path.xy_w[1] =
xy[1];
282 scratch.warm_path.nominal_cct = cfg.warm_cct;
284 if (
static_cast<int>(cfg.cool_cct) != base->cool_path.nominal_cct) {
287 scratch.cool_path.xy_w[0] =
xy[0];
288 scratch.cool_path.xy_w[1] =
xy[1];
289 scratch.cool_path.nominal_cct = cfg.cool_cct;
298 u8 r_scale,
u8 g_scale,
u8 b_scale,
299 u8 *out_r,
u8 *out_g,
u8 *out_b,
301 r = scale8(r, r_scale);
302 g = scale8(g, g_scale);
303 b = scale8(b, b_scale);
304 if ((r | g | b) == 0) {
zero_out(out_r, out_g, out_b, out_ww, out_wc);
return; }
308 resolve_active_rgbcct_profile(cfg, scratch);
310 const float s_r = r * (1.0f / 255.0f);
311 const float s_g = g * (1.0f / 255.0f);
312 const float s_b = b * (1.0f / 255.0f);
313 const float eta = compute_eta_from_input(profile, s_r, s_g, s_b);
327 u8 r_scale,
u8 g_scale,
u8 b_scale,
328 u8 *out_r,
u8 *out_g,
u8 *out_b,
334 r = scale8(r, r_scale);
335 g = scale8(g, g_scale);
336 b = scale8(b, b_scale);
337 if ((r | g | b) == 0) {
zero_out(out_r, out_g, out_b, out_ww, out_wc);
return; }
341 resolve_active_rgbcct_profile(cfg, scratch);
343 const float s_r = r * (1.0f / 255.0f);
344 const float s_g = g * (1.0f / 255.0f);
345 const float s_b = b * (1.0f / 255.0f);
346 const float eta_chroma = compute_eta_from_input(profile, s_r, s_g, s_b);
348 const float eta = 0.5f * eta_chroma + 0.5f * 0.5f;
368 u8 r_scale,
u8 g_scale,
u8 b_scale,
369 u8 *out_r,
u8 *out_g,
u8 *out_b,
371 (void)cfg; (void)r; (void)g; (void)b;
372 (void)r_scale; (void)g_scale; (void)b_scale;
373#ifndef FASTLED_SUPPRESS_RGBWW_FALLBACK_WARNING
374 FL_WARN_ONCE(
"RGBWW: kRGBWWColorimetric requires FASTLED_RGBW_COLORIMETRIC=1 "
375 "(the math library that provides solve_rgbcct). Outputting zeros.");
377 zero_out(out_r, out_g, out_b, out_ww, out_wc);
382 u8 r_scale,
u8 g_scale,
u8 b_scale,
383 u8 *out_r,
u8 *out_g,
u8 *out_b,
385 (void)cfg; (void)r; (void)g; (void)b;
386 (void)r_scale; (void)g_scale; (void)b_scale;
387#ifndef FASTLED_SUPPRESS_RGBWW_FALLBACK_WARNING
388 FL_WARN_ONCE(
"RGBWW: kRGBWWColorimetricBoosted requires FASTLED_RGBW_COLORIMETRIC=1. "
389 "Outputting zeros.");
391 zero_out(out_r, out_g, out_b, out_ww, out_wc);
unsigned int xy(unsigned int x, unsigned int y)
static T & instance() FL_NOEXCEPT
Internal FastLED header for implementation files.
Centralized logging categories for FastLED hardware interfaces and subsystems.
void zero_out(u8 *r, u8 *g, u8 *b, u8 *ww, u8 *wc) FL_NOEXCEPT
colorimetric_detail::RgbcctProfile profile
void xyY_to_XYZ(float x, float y, float Y, float out[3]) FL_NOEXCEPT
void cct_to_xy(int cct, float out[2]) FL_NOEXCEPT
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_NOEXCEPT
void matvec3(const float M[3][3], const float v[3], float out[3]) FL_NOEXCEPT
u8 quantize_u8(float v) FL_NOEXCEPT
void solve_rgbcct(const RgbcctProfile &profile, float s_r, float s_g, float s_b, float eta, float out[5]) FL_NOEXCEPT
void rgb_2_rgbww_colorimetric_boosted(const Rgbww &cfg, u8 r, u8 g, u8 b, u8 r_scale, u8 g_scale, u8 b_scale, u8 *out_r, u8 *out_g, u8 *out_b, u8 *out_ww, u8 *out_wc) FL_NOEXCEPT
Colorimetric white-overdrive solver for RGBWW (wx_lp_legacy + RGBCCT layered blend).
const colorimetric_detail::RgbcctProfile kRgbwwDefaultProfile
EOrderWW
White-byte ordering for 5-channel RGBWW output.
void set_rgb_2_rgbww_function(rgb_2_rgbww_function func) FL_NOEXCEPT
void rgb_2_rgbww_user_function(const Rgbww &cfg, u8 r, u8 g, u8 b, u8 r_scale, u8 g_scale, u8 b_scale, u8 *out_r, u8 *out_g, u8 *out_b, u8 *out_ww, u8 *out_wc) FL_NOEXCEPT
User-installable RGB->RGBWW function.
void rgb_2_rgbww_colorimetric(const Rgbww &cfg, u8 r, u8 g, u8 b, u8 r_scale, u8 g_scale, u8 b_scale, u8 *out_r, u8 *out_g, u8 *out_b, u8 *out_ww, u8 *out_wc) FL_NOEXCEPT
Colorimetric strict sub-gamut solver for RGBWW (gist sec 5 + sec 11-12, using solve_rgbcct from rgbw_...
void(* rgb_2_rgbww_function)(const Rgbww &cfg, fl::u8 r, fl::u8 g, fl::u8 b, fl::u8 r_scale, fl::u8 g_scale, fl::u8 b_scale, fl::u8 *out_r, fl::u8 *out_g, fl::u8 *out_b, fl::u8 *out_ww, fl::u8 *out_wc)
void set_rgbww_colorimetric_profile(const colorimetric_detail::RgbcctProfile *profile) FL_NOEXCEPT
const colorimetric_detail::RgbcctProfile * get_rgbww_colorimetric_profile() FL_NOEXCEPT
void rgbww_partial_reorder(EOrderWW ww_placement, u8 b0, u8 b1, u8 b2, u8 ww, u8 wc, u8 *out_b0, u8 *out_b1, u8 *out_b2, u8 *out_b3, u8 *out_b4) FL_NOEXCEPT
Dispatch RGB->RGBWW for a given mode.
Base definition for an LED controller.
Chromaticity-aware RGBW solvers — strict sub-gamut + wx_lp_legacy white extraction + boosted overdriv...
5-channel RGB + warm-W + cool-W (RGBWW / RGBCCT) configuration types (issue #2558,...
Per-strip RGBWW configuration.