FastLED 3.9.15
Loading...
Searching...
No Matches
rgbw.cpp.hpp
Go to the documentation of this file.
1
3
4#include "fl/stl/stdint.h"
5
6#define FASTLED_INTERNAL
7#include "fl/system/fastled.h"
8
9#include "fl/gfx/rgbw.h"
10#include "fl/log/log.h"
11#include "fl/stl/singleton.h"
12
13#if FASTLED_RGBW_COLORIMETRIC
14// Pulls in the type decls + tiny inline helpers. The heavy solver / LUT /
15// RGBCCT implementations live in rgbw_colorimetric.cpp.hpp, included once
16// by the unity build manifest (fl/gfx/_build.cpp.hpp).
18#endif
19
20
21namespace fl {
22
23// Default diode profile: SK6812 RGBW3535 at ~6000K. Datasheet wavelength
24// midpoints (R 625nm, G 523nm, B 468nm) converted to CIE 1931 xy; W at
25// nominal 6000K (xy = 0.32208, 0.33805). Luminance ratios normalized so
26// W = 1.0; R/G/B set from datasheet typical mcd ratios for a 5050-class
27// part.
28//
29// Source space defaults to **native LED gamut + D65 white** (#2710): the
30// input primaries equal the LED's measured primaries so a saturated input
31// like RGB=(255,0,0) reaches the full saturation of the LED red diode,
32// and RGB=(255,255,255) lands on D65. This is what most FastLED users
33// expect — full use of their hardware's chromatic range. Users who want
34// named-gamut input semantics (Rec709 / sRGB, Rec2020, DCI-P3 D65/D60)
35// should explicitly call `set_input_gamut()` after copying / constructing
36// their profile. See #2705 for the source-space transform itself.
38 /* xy_r */ { 0.700606f, 0.299300f },
39 /* xy_g */ { 0.097940f, 0.831593f },
40 /* xy_b */ { 0.129086f, 0.049450f },
41 /* xy_w */ { 0.322080f, 0.338050f },
42 /* lum_r */ 0.10f,
43 /* lum_g */ 0.37f,
44 /* lum_b */ 0.08f,
45 /* lum_w */ 1.00f,
46 /* nominal_cct */ 6000,
47 /* input_xy_r */ { 0.700606f, 0.299300f }, // native LED R
48 /* input_xy_g */ { 0.097940f, 0.831593f }, // native LED G
49 /* input_xy_b */ { 0.129086f, 0.049450f }, // native LED B
50 /* input_xy_w */ { 0.31272f, 0.32903f }, // D65 white
51};
52
53
54namespace {
55inline u8 min3(u8 a, u8 b, u8 c) {
56 if (a < b) {
57 if (a < c) {
58 return a;
59 } else {
60 return c;
61 }
62 } else {
63 if (b < c) {
64 return b;
65 } else {
66 return c;
67 }
68 }
69}
70
71inline u8 divide_by_3(u8 x) {
72 u16 y = (u16(x) * 85) >> 8;
73 return static_cast<u8>(y);
74}
75} // namespace
76
77// @brief Converts RGB to RGBW using a color transfer method
78// from color channels to 3x white.
79// @author Jonathanese
80void rgb_2_rgbw_exact(u16 w_color_temperature, u8 r, u8 g,
81 u8 b, u8 r_scale, u8 g_scale,
82 u8 b_scale, u8 *out_r, u8 *out_g,
83 u8 *out_b, u8 *out_w) {
84 (void)w_color_temperature;
85 r = scale8(r, r_scale);
86 g = scale8(g, g_scale);
87 b = scale8(b, b_scale);
88 u8 min_component = min3(r, g, b);
89 *out_r = r - min_component;
90 *out_g = g - min_component;
91 *out_b = b - min_component;
92 *out_w = min_component;
93}
94
95void rgb_2_rgbw_max_brightness(u16 w_color_temperature, u8 r,
96 u8 g, u8 b, u8 r_scale,
97 u8 g_scale, u8 b_scale, u8 *out_r,
98 u8 *out_g, u8 *out_b, u8 *out_w) {
99 (void)w_color_temperature;
100 *out_r = scale8(r, r_scale);
101 *out_g = scale8(g, g_scale);
102 *out_b = scale8(b, b_scale);
103 *out_w = min3(*out_r, *out_g, *out_b);
104}
105
106void rgb_2_rgbw_null_white_pixel(u16 w_color_temperature, u8 r,
107 u8 g, u8 b, u8 r_scale,
108 u8 g_scale, u8 b_scale,
109 u8 *out_r, u8 *out_g, u8 *out_b,
110 u8 *out_w) {
111 (void)w_color_temperature;
112 *out_r = scale8(r, r_scale);
113 *out_g = scale8(g, g_scale);
114 *out_b = scale8(b, b_scale);
115 *out_w = 0;
116}
117
118void rgb_2_rgbw_white_boosted(u16 w_color_temperature, u8 r,
119 u8 g, u8 b, u8 r_scale,
120 u8 g_scale, u8 b_scale, u8 *out_r,
121 u8 *out_g, u8 *out_b, u8 *out_w) {
122 (void)w_color_temperature;
123 r = scale8(r, r_scale);
124 g = scale8(g, g_scale);
125 b = scale8(b, b_scale);
126 u8 min_component = min3(r, g, b);
127 u8 w;
128 bool is_min = true;
129 if (min_component <= 84) {
130 w = 3 * min_component;
131 } else {
132 w = 255;
133 is_min = false;
134 }
135 u8 r_prime;
136 u8 g_prime;
137 u8 b_prime;
138 if (is_min) {
139 r_prime = r - min_component;
140 g_prime = g - min_component;
141 b_prime = b - min_component;
142 } else {
143 u8 w3 = divide_by_3(w);
144 r_prime = r - w3;
145 g_prime = g - w3;
146 b_prime = b - w3;
147 }
148
149 *out_r = r_prime;
150 *out_g = g_prime;
151 *out_b = b_prime;
152 *out_w = w;
153}
154
155// User-installable RGB→RGBW function pointer, held behind a lazily-constructed
156// Singleton<T>. Replaces the previous static-init form
157// (`rgb_2_rgbw_function g_user_function = rgb_2_rgbw_exact`), which forced
158// `rgb_2_rgbw_exact` to be ODR-used at static init even when no caller ever
159// reached the kRGBWUserFunction code path.
160//
161// The Singleton's instance is constructed on first call to set_… or
162// rgb_2_rgbw_user_function below — never at static init. The default value of
163// `fn` is `nullptr`; rgb_2_rgbw_user_function uses `rgb_2_rgbw_exact` as the
164// fallback algorithm when nothing has been installed.
165//
166// See issue #2424 for the broader binary-bloat investigation.
167namespace {
171} // namespace
172
176
177void rgb_2_rgbw_user_function(u16 w_color_temperature, u8 r,
178 u8 g, u8 b, u8 r_scale,
179 u8 g_scale, u8 b_scale, u8 *out_r,
180 u8 *out_g, u8 *out_b, u8 *out_w) {
182 if (fn == nullptr) {
183 fn = rgb_2_rgbw_exact;
184 }
185 fn(w_color_temperature, r, g, b, r_scale, g_scale, b_scale,
186 out_r, out_g, out_b, out_w);
187}
188
189// Active colorimetric profile pointer. Defaults to &kRgbwDefaultProfile on
190// first access. Held behind a lazy Singleton<T> (same pattern as the user
191// function above) so neither the profile constant nor the singleton itself
192// is ODR-used at static init when no colorimetric code path is invoked.
193namespace {
195 const DiodeProfile* profile = nullptr;
196};
197} // namespace
198
202
208
209// Standard primary chromaticities for the named input gamuts (#2710). The
210// numbers come straight from each gamut's published spec:
211// Rec.709 / sRGB — ITU-R BT.709, IEC 61966-2-1
212// Rec.2020 — ITU-R BT.2020
213// DCI-P3 D65 — SMPTE EG 432-1 (consumer display variant)
214// DCI-P3 D60 (ACES) — ACES AP1 white = (0.32168, 0.33767)
215// White-point chromaticities likewise: D65 = (0.31272, 0.32903),
216// ACES D60 = (0.32168, 0.33767).
217namespace {
219 float xy_r[2];
220 float xy_g[2];
221 float xy_b[2];
222 float xy_w[2];
223};
224constexpr NamedGamut kRec709 = {{0.6400f, 0.3300f}, {0.3000f, 0.6000f},
225 {0.1500f, 0.0600f}, {0.31272f, 0.32903f}};
226constexpr NamedGamut kRec2020 = {{0.7080f, 0.2920f}, {0.1700f, 0.7970f},
227 {0.1310f, 0.0460f}, {0.31272f, 0.32903f}};
228constexpr NamedGamut kDciP3D65 = {{0.6800f, 0.3200f}, {0.2650f, 0.6900f},
229 {0.1500f, 0.0600f}, {0.31272f, 0.32903f}};
230constexpr NamedGamut kDciP3D60 = {{0.6800f, 0.3200f}, {0.2650f, 0.6900f},
231 {0.1500f, 0.0600f}, {0.32168f, 0.33767f}};
232} // namespace
233
234// Forward declaration: real implementation lives inside the colorimetric
235// branch below (and is a no-op when colorimetric math is compiled out).
236// Called from set_input_gamut to evict per-process caches keyed on a profile
237// whose input_xy_* fields were just mutated in place — without this hook the
238// pointer+CCT cache key stays equal and stale M_src/LUT data is reused
239// after a gamut switch on the currently active profile.
240namespace { void invalidate_colorimetric_caches_for(const DiodeProfile* profile) FL_NOEXCEPT; }
241
243 const float white_xy[2]) FL_NOEXCEPT {
244 if (profile == nullptr) return;
245 auto apply = [profile](const float r[2], const float gp[2],
246 const float b[2], const float w[2]) {
247 profile->input_xy_r[0] = r[0]; profile->input_xy_r[1] = r[1];
248 profile->input_xy_g[0] = gp[0]; profile->input_xy_g[1] = gp[1];
249 profile->input_xy_b[0] = b[0]; profile->input_xy_b[1] = b[1];
250 profile->input_xy_w[0] = w[0]; profile->input_xy_w[1] = w[1];
251 };
252 switch (g) {
253 case InputGamut::Native: {
254 // For Native, the input gamut tracks this profile's LED primaries —
255 // copy them over rather than picking a fixed sRGB-like fallback.
256 const float d65[2] = {0.31272f, 0.32903f};
257 const float* w = (white_xy != nullptr) ? white_xy : d65;
258 apply(profile->xy_r, profile->xy_g, profile->xy_b, w);
259 invalidate_colorimetric_caches_for(profile);
260 return;
261 }
262 case InputGamut::Rec709: apply(kRec709.xy_r, kRec709.xy_g, kRec709.xy_b,
263 white_xy != nullptr ? white_xy : kRec709.xy_w);
264 invalidate_colorimetric_caches_for(profile); return;
265 case InputGamut::Rec2020: apply(kRec2020.xy_r, kRec2020.xy_g, kRec2020.xy_b,
266 white_xy != nullptr ? white_xy : kRec2020.xy_w);
267 invalidate_colorimetric_caches_for(profile); return;
268 case InputGamut::DciP3D65: apply(kDciP3D65.xy_r, kDciP3D65.xy_g, kDciP3D65.xy_b,
269 white_xy != nullptr ? white_xy : kDciP3D65.xy_w);
270 invalidate_colorimetric_caches_for(profile); return;
271 case InputGamut::DciP3D60: apply(kDciP3D60.xy_r, kDciP3D60.xy_g, kDciP3D60.xy_b,
272 white_xy != nullptr ? white_xy : kDciP3D60.xy_w);
273 invalidate_colorimetric_caches_for(profile); return;
274 }
275 // Default-fallthrough for forward-compat with future enum additions:
276 // leave the profile's input_xy_* untouched — also nothing to invalidate.
277}
278
280 set_input_gamut(profile, g, nullptr);
281}
282
283#if FASTLED_RGBW_COLORIMETRIC
284
285namespace {
286// Per-process cached profile data (XYZ columns + matrix inverses). Rebuilt
287// whenever the active profile pointer OR the requested CCT changes. The CCT
288// part of the key is what makes Rgbw::white_color_temp actually shift the
289// W vertex chromaticity at solve time.
290struct ColorimetricCacheHolder {
291 colorimetric_detail::ProfileCache cache;
292 const DiodeProfile* cached_for = nullptr;
293 int cached_cct = 0;
294};
295
296// Resolve the CCT to use for the W vertex: if the dispatch passed a value in
297// the valid Krystek range AND it differs from the profile's nominal CCT,
298// shift W. Otherwise use the profile's xy_w as-is (cct_override = 0).
299inline int resolve_cct_override(const DiodeProfile* p, int requested_cct) FL_NOEXCEPT {
300 if (requested_cct < 1500 || requested_cct > 15000) return 0;
301 if (requested_cct == p->nominal_cct) return 0;
302 return requested_cct;
303}
304
305inline const colorimetric_detail::ProfileCache& get_cache(int cct) FL_NOEXCEPT {
306 ColorimetricCacheHolder& h =
309 const int override_cct = resolve_cct_override(active, cct);
310 if (h.cached_for != active || h.cached_cct != override_cct) {
311 colorimetric_detail::build_profile_cache(active, override_cct, &h.cache);
312 h.cached_for = active;
313 h.cached_cct = override_cct;
314 }
315 return h.cache;
316}
317
318// ===== LUT state (issue #2545 Phase 2) ==================================
319// Always-compiled when the outer FASTLED_RGBW_COLORIMETRIC gate is on. The
320// inner FASTLED_RGBW_COLORIMETRIC_LUT gate that used to wrap this block was
321// redundant — once you've opted into colorimetric math, the LUT state +
322// rebuild_lut_if_stale + build_lut reference are gc-section-droppable for
323// sketches that never call enable_rgbw_colorimetric_lut() (the `enabled`
324// flag stays false, the fl::unique_ptr<LutTable> stays empty, and only the
325// LutStateHolder singleton instance itself is retained — ~32 bytes).
326struct LutStateHolder {
327 fl::unique_ptr<colorimetric_detail::LutTable> table;
328 const DiodeProfile* built_for = nullptr;
329 int built_cct = 0;
330 int requested_grid_n = 0;
331 RgbwLutInterp requested_interp = RgbwLutInterp::Hermite;
332 bool enabled = false;
333};
334
335inline colorimetric_detail::LutInterp to_internal_interp(
337 return (i == RgbwLutInterp::Hermite)
340}
341
342inline void rebuild_lut_if_stale(LutStateHolder& s, int cct) FL_NOEXCEPT {
343 if (!s.enabled || s.requested_grid_n <= 0) return;
345 const int override_cct = resolve_cct_override(active, cct);
346 const colorimetric_detail::LutInterp interp =
347 to_internal_interp(s.requested_interp);
348 if (s.table && s.built_for == active && s.built_cct == override_cct
349 && s.table->N == s.requested_grid_n
350 && s.table->interp == interp) {
351 return; // up-to-date
352 }
354 colorimetric_detail::build_lut(get_cache(cct), s.requested_grid_n,
355 interp));
356 s.built_for = active;
357 s.built_cct = override_cct;
358}
359
360// Drop both the ProfileCache and the LUT cache when `profile` matches the
361// current cache key. set_input_gamut() mutates input_xy_* in place without
362// touching the profile pointer or CCT, so the (pointer, cct) cache key
363// stays equal and would otherwise serve stale M_src / LUT data.
365 ColorimetricCacheHolder& ch =
367 if (ch.cached_for == profile) {
368 ch.cached_for = nullptr;
369 ch.cached_cct = 0;
370 }
371 LutStateHolder& lh = fl::Singleton<LutStateHolder>::instance();
372 if (lh.built_for == profile) {
373 lh.built_for = nullptr;
374 lh.built_cct = 0;
375 }
376}
377} // namespace
378
379void rgb_2_rgbw_colorimetric(u16 w_color_temperature, u8 r,
380 u8 g, u8 b, u8 r_scale,
381 u8 g_scale, u8 b_scale, u8 *out_r,
382 u8 *out_g, u8 *out_b, u8 *out_w) FL_NOEXCEPT {
383 r = scale8(r, r_scale);
384 g = scale8(g, g_scale);
385 b = scale8(b, b_scale);
386 if ((r | g | b) == 0) {
387 *out_r = *out_g = *out_b = *out_w = 0;
388 return;
389 }
390 const float s_r = r * (1.0f / 255.0f);
391 const float s_g = g * (1.0f / 255.0f);
392 const float s_b = b * (1.0f / 255.0f);
393 const colorimetric_detail::ProfileCache& cache = get_cache(w_color_temperature);
394 float rgbw[4];
395
396 // LUT fast path — gc-sections drops the branch + singleton + lookup_lut
397 // for sketches that never call enable_rgbw_colorimetric_lut().
398 LutStateHolder& lut_state = fl::Singleton<LutStateHolder>::instance();
399 if (lut_state.enabled) {
400 rebuild_lut_if_stale(lut_state, w_color_temperature);
401 float X_t[3];
402 if (cache.has_source_space) {
403 // #2705: use source-space matrix so the LUT lookup targets the
404 // same chromaticity as the closed-form solver.
405 const float s_vec[3] = { s_r, s_g, s_b };
406 colorimetric_detail::matvec3(cache.M_src, s_vec, X_t);
407 } else {
408 X_t[0] = cache.P_R[0] * s_r + cache.P_G[0] * s_g + cache.P_B[0] * s_b;
409 X_t[1] = cache.P_R[1] * s_r + cache.P_G[1] * s_g + cache.P_B[1] * s_b;
410 X_t[2] = cache.P_R[2] * s_r + cache.P_G[2] * s_g + cache.P_B[2] * s_b;
411 }
412 const float sum = X_t[0] + X_t[1] + X_t[2];
413 if (sum < 1e-9f) {
414 *out_r = *out_g = *out_b = *out_w = 0;
415 return;
416 }
417 const float xy_t[2] = { X_t[0] / sum, X_t[1] / sum };
418 colorimetric_detail::lookup_lut(*lut_state.table, xy_t, X_t[1], rgbw);
423 return;
424 }
425
426 const bool ok =
428 if (!ok) {
429 rgb_2_rgbw_exact(w_color_temperature, r, g, b, 255, 255, 255,
430 out_r, out_g, out_b, out_w);
431 return;
432 }
437}
438
439void rgb_2_rgbw_colorimetric_boosted(u16 w_color_temperature, u8 r,
440 u8 g, u8 b, u8 r_scale,
441 u8 g_scale, u8 b_scale, u8 *out_r,
442 u8 *out_g, u8 *out_b, u8 *out_w) FL_NOEXCEPT {
443 r = scale8(r, r_scale);
444 g = scale8(g, g_scale);
445 b = scale8(b, b_scale);
446 if ((r | g | b) == 0) {
447 *out_r = *out_g = *out_b = *out_w = 0;
448 return;
449 }
450 const float s_r = r * (1.0f / 255.0f);
451 const float s_g = g * (1.0f / 255.0f);
452 const float s_b = b * (1.0f / 255.0f);
453 float rgbw[4];
455 get_cache(w_color_temperature),
456 s_r, s_g, s_b,
458 rgbw);
463}
464
465bool enable_rgbw_colorimetric_lut(int grid_n,
466 RgbwLutInterp interp) FL_NOEXCEPT {
467 if (grid_n < 4) grid_n = 4;
468 if (grid_n > 256) grid_n = 256;
469 LutStateHolder& s = fl::Singleton<LutStateHolder>::instance();
470 s.enabled = true;
471 s.requested_grid_n = grid_n;
472 s.requested_interp = interp;
473 // Force a rebuild on next colorimetric call.
474 s.built_for = nullptr;
475 s.built_cct = -1;
476 return true;
477}
478
481}
482
484 LutStateHolder& s = fl::Singleton<LutStateHolder>::instance();
485 s.enabled = false;
486 s.table.reset(); // unique_ptr destructor frees the cells storage
487 s.requested_grid_n = 0;
488 s.requested_interp = RgbwLutInterp::Hermite;
489 s.built_for = nullptr;
490 s.built_cct = 0;
491}
492
495}
496
497#else // FASTLED_RGBW_COLORIMETRIC
498
499// No-op cache invalidation stub — the colorimetric cache machinery doesn't
500// exist when FASTLED_RGBW_COLORIMETRIC=0, so set_input_gamut has nothing to
501// evict. Keeping the forward-declared symbol available means the dispatch
502// path above doesn't need its own #if gate.
504
505// Stub APIs for the LUT/CCT/RGBCCT path — no-ops when colorimetric is off.
507 RgbwLutInterp /*interp*/) FL_NOEXCEPT {
508 return false;
509}
510bool enable_rgbw_colorimetric_lut(int /*grid_n*/) FL_NOEXCEPT { return false; }
513
514// Stub path: warn-once + fall back to rgb_2_rgbw_exact. Does not pull in the
515// simplex solver, profile cache, or float math machinery.
516void rgb_2_rgbw_colorimetric(u16 w_color_temperature, u8 r,
517 u8 g, u8 b, u8 r_scale,
518 u8 g_scale, u8 b_scale, u8 *out_r,
519 u8 *out_g, u8 *out_b, u8 *out_w) FL_NOEXCEPT {
520#ifndef FASTLED_SUPPRESS_COLORIMETRIC_FALLBACK_WARNING
521 FL_WARN_ONCE("RGBW: kRGBWColorimetric requested but FASTLED_RGBW_COLORIMETRIC is not defined — falling back to kRGBWExactColors. Define FASTLED_RGBW_COLORIMETRIC=1 to enable the colorimetric path.");
522#endif
523 rgb_2_rgbw_exact(w_color_temperature, r, g, b, r_scale, g_scale, b_scale,
524 out_r, out_g, out_b, out_w);
525}
526
527void rgb_2_rgbw_colorimetric_boosted(u16 w_color_temperature, u8 r,
528 u8 g, u8 b, u8 r_scale,
529 u8 g_scale, u8 b_scale, u8 *out_r,
530 u8 *out_g, u8 *out_b, u8 *out_w) FL_NOEXCEPT {
531#ifndef FASTLED_SUPPRESS_COLORIMETRIC_FALLBACK_WARNING
532 FL_WARN_ONCE("RGBW: kRGBWColorimetricBoosted requested but FASTLED_RGBW_COLORIMETRIC is not defined — falling back to kRGBWExactColors. Define FASTLED_RGBW_COLORIMETRIC=1 to enable the colorimetric path.");
533#endif
534 rgb_2_rgbw_exact(w_color_temperature, r, g, b, r_scale, g_scale, b_scale,
535 out_r, out_g, out_b, out_w);
536}
537
538#endif // FASTLED_RGBW_COLORIMETRIC
539
540void rgbw_partial_reorder(EOrderW w_placement, u8 b0, u8 b1,
541 u8 b2, u8 w, u8 *out_b0,
542 u8 *out_b1, u8 *out_b2, u8 *out_b3) {
543
544 u8 out[4] = {b0, b1, b2, 0};
545 switch (w_placement) {
546 // unrolled loop for speed.
547 case W3:
548 out[3] = w;
549 break;
550 case W2:
551 out[3] = out[2]; // memmove and copy.
552 out[2] = w;
553 break;
554 case W1:
555 out[3] = out[2];
556 out[2] = out[1];
557 out[1] = w;
558 break;
559 case W0:
560 out[3] = out[2];
561 out[2] = out[1];
562 out[1] = out[0];
563 out[0] = w;
564 break;
565 }
566 *out_b0 = out[0];
567 *out_b1 = out[1];
568 *out_b2 = out[2];
569 *out_b3 = out[3];
570}
571
572} // namespace fl
Rgbw rgbw
static T & instance() FL_NOEXCEPT
Definition singleton.h:41
Functions for red, green, blue, white (RGBW) output.
Internal FastLED header for implementation files.
#define FL_WARN_ONCE(X)
Definition log.h:278
Centralized logging categories for FastLED hardware interfaces and subsystems.
void invalidate_colorimetric_caches_for(const DiodeProfile *profile) FL_NOEXCEPT
Definition rgbw.cpp.hpp:503
LutTable build_lut(const ProfileCache &cache, int grid_n, LutInterp interp) FL_NOEXCEPT
void matvec3(const float M[3][3], const float v[3], float out[3]) FL_NOEXCEPT
void solve_wx_overdrive(const ProfileCache &cache, float s_r, float s_g, float s_b, float overdrive_ratio, float out_rgbw[4]) FL_NOEXCEPT
constexpr float kDefaultOverdriveRatio
void build_profile_cache(const DiodeProfile *p, int cct_override, ProfileCache *cache) FL_NOEXCEPT
void lookup_lut(const LutTable &lut, const float xy_t[2], float Y_t, float out_rgbw[4]) FL_NOEXCEPT
u8 quantize_u8(float v) FL_NOEXCEPT
bool solve_strict_subgamut(const ProfileCache &cache, float s_r, float s_g, float s_b, float out_rgbw[4]) FL_NOEXCEPT
unsigned char u8
Definition stdint.h:131
void rgbw_partial_reorder(EOrderW w_placement, u8 b0, u8 b1, u8 b2, u8 w, u8 *out_b0, u8 *out_b1, u8 *out_b2, u8 *out_b3)
Definition rgbw.cpp.hpp:540
bool rgbw_colorimetric_lut_enabled() FL_NOEXCEPT
Definition rgbw.cpp.hpp:512
void rgb_2_rgbw_colorimetric_boosted(u16 w_color_temperature, 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_w) FL_NOEXCEPT
Definition rgbw.cpp.hpp:527
const DiodeProfile kRgbwDefaultProfile
Definition rgbw.cpp.hpp:37
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
Definition unique_ptr.h:261
void set_rgbw_colorimetric_profile(const DiodeProfile *profile) FL_NOEXCEPT
Definition rgbw.cpp.hpp:199
const DiodeProfile * get_rgbw_colorimetric_profile() FL_NOEXCEPT
Definition rgbw.cpp.hpp:203
InputGamut
Definition rgbw.h:77
RgbwLutInterp
Definition rgbw.h:121
void(* rgb_2_rgbw_function)(u16 w_color_temperature, 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_w)
Definition rgbw.h:231
void rgb_2_rgbw_exact(u16 w_color_temperature, 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_w)
Converts RGB to RGBW using a color transfer method from saturated color channels to white.
Definition rgbw.cpp.hpp:80
void rgb_2_rgbw_null_white_pixel(u16 w_color_temperature, 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_w)
Converts RGB to RGBW with the W channel set to black, always.
Definition rgbw.cpp.hpp:106
void disable_rgbw_colorimetric_lut() FL_NOEXCEPT
Definition rgbw.cpp.hpp:511
void rgb_2_rgbw_user_function(u16 w_color_temperature, 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_w)
Definition rgbw.cpp.hpp:177
void rgb_2_rgbw_white_boosted(u16 w_color_temperature, 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_w)
Converts RGB to RGBW with a boosted white channel.
Definition rgbw.cpp.hpp:118
bool enable_rgbw_colorimetric_lut(int, RgbwLutInterp) FL_NOEXCEPT
Definition rgbw.cpp.hpp:506
EOrderW
Definition eorder.h:23
@ W1
White is second.
Definition eorder.h:26
@ W2
White is third.
Definition eorder.h:25
@ W3
White is fourth.
Definition eorder.h:24
@ W0
White is first.
Definition eorder.h:27
void set_rgb_2_rgbw_function(rgb_2_rgbw_function func)
Definition rgbw.cpp.hpp:173
void rgb_2_rgbw_colorimetric(u16 w_color_temperature, 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_w) FL_NOEXCEPT
Definition rgbw.cpp.hpp:516
void set_input_gamut(DiodeProfile *profile, InputGamut g, const float white_xy[2]) FL_NOEXCEPT
Definition rgbw.cpp.hpp:242
void rgb_2_rgbw_max_brightness(u16 w_color_temperature, 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_w)
The minimum brigthness of the RGB channels is used to set the W channel.
Definition rgbw.cpp.hpp:95
Base definition for an LED controller.
Definition crgb.hpp:179
Chromaticity-aware RGBW solvers — strict sub-gamut + wx_lp_legacy white extraction + boosted overdriv...
#define FL_NOEXCEPT