FastLED 3.9.15
Loading...
Searching...
No Matches
pixel_controller.h
Go to the documentation of this file.
1#pragma once
2
5
6// Note that new code should use the PixelIterator concrete object to write out
7// led data.
8// Using this class deep in driver code is deprecated because it's templates will
9// infact everything it touches. PixelIterator is concrete and doesn't have these
10// problems. See PixelController::as_iterator() for how to create a PixelIterator.
11
12
13#include "fl/math/intmap.h"
14#include "fl/math/math8.h"
15#include "platforms/is_platform.h"
16
17#include "rgbw.h"
18#include "fl/gfx/rgbww.h"
20#include "fl/log/log.h"
23#include "fl/math/scale8.h"
24#include "fl/math/math8.h"
25#include "eorder.h"
26#include "dither_mode.h"
27#include "pixel_iterator.h"
28#include "crgb.h"
29#include "fl/stl/variant.h" // for PixelControllerAny.
30
35
36
37
38
39
46#define RO(X) RGB_BYTE(RGB_ORDER, X)
47
54#define RGB_BYTE(RO,X) ((static_cast<int>(RO)>>(3*(2-(X)))) & 0x3)
55
58#define RGB_BYTE0(RO) ((static_cast<int>(RO)>>6) & 0x3)
61#define RGB_BYTE1(RO) ((static_cast<int>(RO)>>3) & 0x3)
64#define RGB_BYTE2(RO) (static_cast<int>(RO) & 0x3)
65
66// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
67
76 #if FASTLED_HD_COLOR_MIXING
77 CRGB color;
79 #endif
80
85 adj.premixed = CRGB(255, 255, 255);
86 #if FASTLED_HD_COLOR_MIXING
87 adj.color = CRGB(255, 255, 255);
88 adj.brightness = 255;
89 #endif
90 return adj;
91 }
92};
93
94
101template<EOrder RGB_ORDER, int LANES=1, fl::u32 MASK=0xFFFFFFFF>
103 const fl::u8 *mData;
104 int mLen;
109 int mOffsets[LANES];
111
112 enum {
113 kLanes = LANES,
114 kMask = MASK
115 };
116
120
122 #if FASTLED_HD_COLOR_MIXING
123 mColorAdjustment.premixed = CRGB(mColorAdjustment.brightness, mColorAdjustment.brightness, mColorAdjustment.brightness);
124 mColorAdjustment.color = CRGB(0xff, 0xff, 0xff);
125 #endif
126 }
127
131 copy(other);
132 }
133
134 template<EOrder RGB_ORDER_OTHER>
138
139 template<typename PixelControllerT>
140 void copy(const PixelControllerT& other) {
141 FL_STATIC_ASSERT(int(kLanes) == int(PixelControllerT::kLanes), "PixelController lanes must match or mOffsets will be wrong");
142 FL_STATIC_ASSERT(int(kMask) == int(PixelControllerT::kMask), "PixelController mask must match or else one or the other controls different lanes");
143 d[0] = other.d[0];
144 d[1] = other.d[1];
145 d[2] = other.d[2];
146 e[0] = other.e[0];
147 e[1] = other.e[1];
148 e[2] = other.e[2];
149 mData = other.mData;
150 mColorAdjustment = other.mColorAdjustment;
151 mAdvance = other.mAdvance;
152 mLenRemaining = mLen = other.mLen;
153 for(int i = 0; i < LANES; ++i) { mOffsets[i] = other.mOffsets[i]; }
154 }
155
158 void initOffsets(int len) {
159 int nOffset = 0;
160 for(int i = 0; i < LANES; ++i) {
161 mOffsets[i] = nOffset;
162 if((1<<i) & MASK) { nOffset += (len * mAdvance); }
163 }
164 }
165
174 const fl::u8 *d, int len, ColorAdjustment color_adjustment,
175 EDitherMode dither, bool advance, fl::u8 skip)
176 : mData(d), mLen(len), mLenRemaining(len), mColorAdjustment(color_adjustment) {
178 mData += skip;
179 mAdvance = (advance) ? 3+skip : 0;
180 initOffsets(len);
181 }
182
189 const CRGB *d, int len, ColorAdjustment color_adjustment,
191 : mData((const fl::u8*)d), mLen(len), mLenRemaining(len), mColorAdjustment(color_adjustment) {
193 mAdvance = 3;
194 initOffsets(len);
195 }
196
203 const CRGB &d, int len, ColorAdjustment color_adjustment, EDitherMode dither)
204 : mData((const fl::u8*)&d), mLen(len), mLenRemaining(len), mColorAdjustment(color_adjustment) {
206 mAdvance = 0;
207 initOffsets(len);
208 }
209
210 #if FASTLED_HD_COLOR_MIXING
211 fl::u8 global_brightness() const {
212 return mColorAdjustment.brightness;
213 }
214 #endif
215
219 const fl::u8* getRawPixelData() const {
220 return mData;
221 }
222
223
224// ============================================================================
225// TEMPORAL DITHERING OVERVIEW
226// ============================================================================
227//
228// Temporal dithering recovers fractional brightness precision lost to integer
229// quantization by varying pixel values across frames. At refresh rates above
230// ~50Hz, human vision integrates these variations, perceiving the true
231// fractional brightness.
232//
233// THE PROBLEM:
234// Integer scaling causes color shifts at low brightness. For example:
235// CRGB(100, 60, 20) at 20% brightness → RGB(19, 11, 3)
236// Each channel loses different fractional precision, distorting the color.
237//
238// THE SOLUTION:
239// Add frame-varying noise BEFORE scaling, causing different rounding outcomes:
240// Frame 1: scale8(100+0, 51) = 19
241// Frame 2: scale8(100+3, 51) = 20 ← noise pushed over threshold
242// Your eye averages these to perceive the correct fractional brightness.
243//
244// THE ALGORITHM:
245// 1. Frame counter R cycles 0-7, creating an 8-frame pattern
246// 2. Bit-reverse R to Q (0→0, 1→128, 2→64...) to distribute pattern temporally
247// 3. Center pattern: Q += 16
248// 4. Scale per channel: e[i] = 256/brightness, d[i] = scale8(Q, e[i])
249// Lower brightness needs BIGGER dither to compensate for larger % error
250// 5. Toggle between pixels: d[i] = e[i] - d[i] (spatial distribution)
251// 6. Apply: pixel = scale8(qadd8(pixel, d[i]), brightness)
252//
253// VIRTUAL BITS:
254// 8-frame cycle at 400Hz = 50Hz complete cycle → +3 "virtual" bits
255// Result: 8-bit hardware provides 11-bit perceived precision (0-2047 levels)
256//
257// DISABLE FOR:
258// - Cameras/photography (captures individual frames, sees flicker)
259// - Slow refresh <50Hz (visible flickering)
260// - Video recording (frame rate mismatches create artifacts)
261// Use: FastLED.setDither(DISABLE_DITHER)
262//
263// NOTE: This is NOT gamma correction. Dithering is pure temporal averaging
264// to recover quantization precision. See init_binary_dithering() below.
265//
266// ============================================================================
267
268#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
269
271#define MAX_LIKELY_UPDATE_RATE_HZ 400
272
274#define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
275
277#define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
278
292#define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
293 (UPDATES_PER_FULL_DITHER_CYCLE>2) + \
294 (UPDATES_PER_FULL_DITHER_CYCLE>4) + \
295 (UPDATES_PER_FULL_DITHER_CYCLE>8) + \
296 (UPDATES_PER_FULL_DITHER_CYCLE>16) + \
297 (UPDATES_PER_FULL_DITHER_CYCLE>32) + \
298 (UPDATES_PER_FULL_DITHER_CYCLE>64) + \
299 (UPDATES_PER_FULL_DITHER_CYCLE>128) )
300
302#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
303
304#endif
305
306
310#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
311 // STEP 1: Increment frame counter (creates temporal variation)
312 static fl::u8 R = 0; // okay static in header
313 ++R;
314
315 // STEP 2: Wrap counter at 2^ditherBits (creates 8-frame cycle: 0,1,2,3,4,5,6,7,0...)
316 fl::u8 ditherBits = VIRTUAL_BITS;
317 R &= (0x01 << ditherBits) - 1;
318
319 // STEP 3: Bit-reverse R to create maximally-spaced pattern Q
320 // Why? Prevents visible ramping patterns. Turns 0,1,2,3,4,5,6,7 → 0,128,64,192,32,160,96,224
321 fl::u8 Q = 0;
322
323 // Bit reversal magic: mirrors bit positions (bit 0 ↔ bit 7, bit 1 ↔ bit 6, etc.)
324 {
325 if(R & 0x01) { Q |= 0x80; }
326 if(R & 0x02) { Q |= 0x40; }
327 if(R & 0x04) { Q |= 0x20; }
328 if(R & 0x08) { Q |= 0x10; }
329 if(R & 0x10) { Q |= 0x08; }
330 if(R & 0x20) { Q |= 0x04; }
331 if(R & 0x40) { Q |= 0x02; }
332 if(R & 0x80) { Q |= 0x01; }
333 }
334
335 // STEP 4: Center the pattern (shifts values to middle of quantization bins)
336 // Example: 0,128,64,192 becomes 16,144,80,208 (adds 16 when ditherBits=3)
337 if( ditherBits < 8) {
338 Q += 0x01 << (7 - ditherBits);
339 }
340
341 // STEP 5: Scale per-channel based on brightness
342 // Key insight: Lower brightness needs BIGGER dithering offsets!
343 // e[i] = max dither range (inversely proportional to brightness)
344 // d[i] = current dither offset (Q scaled by e[i])
345 for(int i = 0; i < 3; ++i) {
346 fl::u8 s = mColorAdjustment.premixed.raw[i]; // Brightness scale factor
347
348 // Calculate max dither range: e = 256/brightness
349 // At 100% (255): e≈1 (tiny range), At 20% (51): e≈5 (large range)
350 e[i] = s ? (256/s) + 1 : 0;
351
352 // Scale Q by the dither range to get current offset
353 d[i] = fl::scale8(Q, e[i]);
354
355#if (FASTLED_SCALE8_FIXED == 1)
356 // Adjust for scale8 implementation quirk
357 if(d[i]) (--d[i]);
358#endif
359 // Finalize e[i] value for later toggling
360 if(e[i]) --e[i];
361 }
362#endif
363 }
364
369 return mLenRemaining >= n;
370 }
371
375 switch(dither) {
376 case BINARY_DITHER: init_binary_dithering(); break; // Initialize dithering algorithm
377 default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break; // Clear dither values (disabled)
378 }
379 }
380
383 FASTLED_FORCE_INLINE int size() const { return mLen; }
384
387 FASTLED_FORCE_INLINE int lanes() { return LANES; }
388
392
395
399 // Toggles d between two values: if d=2 and e=5, becomes 3, then back to 2, etc.
400 // This spreads dithering spatially along the strip, preventing visible patterns
401 d[0] = e[0] - d[0];
402 d[1] = e[1] - d[1];
403 d[2] = e[2] - d[2];
404 }
405
408 d[RO(0)] = e[RO(0)] - d[RO(0)];
409 }
410
414
418 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
419
424 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
425
431 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 dither(PixelController & pc, fl::u8 b) { return b ? fl::qadd8(b, pc.d[RO(SLOT)]) : 0; }
432
438 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 dither(PixelController & , fl::u8 b, fl::u8 d) { return b ? fl::qadd8(b,d) : 0; }
439
445 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 scale(PixelController & pc, fl::u8 b) { return fl::scale8(b, pc.mColorAdjustment.premixed.raw[RO(SLOT)]); }
446
451 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 scale(PixelController & , fl::u8 b, fl::u8 scale) { return fl::scale8(b, scale); }
452
458
459
462 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadAndScale(PixelController & pc) {
463 return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc)));
464 }
465
467 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadAndScale(PixelController & pc, int lane) {
468 return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane)));
469 }
470
472 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadAndScale(PixelController & pc, int lane, fl::u8 d, fl::u8 scale) {
473 return fl::scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale);
474 }
475
481 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 loadAndScale(PixelController & pc, int lane, fl::u8 scale) { return fl::scale8(pc.loadByte<SLOT>(pc, lane), scale); }
482
483
486 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
487
491 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
492
497 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 advanceAndLoadAndScale(PixelController & pc, int lane, fl::u8 scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane, scale); }
498
500
501
506
512 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
513
519 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 getscale(PixelController & pc) { return pc.mColorAdjustment.premixed.raw[RO(SLOT)]; }
520
522
523
525
526 // Helper functions to get around gcc stupidities
532
533 FASTLED_FORCE_INLINE fl::u8 loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
534 FASTLED_FORCE_INLINE fl::u8 loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
535 FASTLED_FORCE_INLINE fl::u8 loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
538
539 // LoadAndScale0 loads the pixel data in the order specified by RGB_ORDER and then scales it by the color correction values
540 // For example in color order GRB, loadAndScale0() will return the green channel scaled by the color correction value for green.
546
550
551 #if FASTLED_HD_COLOR_MIXING
552 template<int SLOT> FASTLED_FORCE_INLINE static fl::u8 getScaleFullBrightness(PixelController & pc) { return pc.mColorAdjustment.color.raw[RO(SLOT)]; }
553
558 FASTLED_FORCE_INLINE void loadRGBScaleAndBrightness(fl::u8* c0, fl::u8* c1, fl::u8* c2, fl::u8* brightness) {
559 *c0 = getScaleFullBrightness<0>(*this);
560 *c1 = getScaleFullBrightness<1>(*this);
561 *c2 = getScaleFullBrightness<2>(*this);
562 *brightness = mColorAdjustment.brightness;
563 }
564
566 FL_DEPRECATED("Use loadRGBScaleAndBrightness() instead")
567 FASTLED_FORCE_INLINE void getHdScale(fl::u8* c0, fl::u8* c1, fl::u8* c2, fl::u8* brightness) {
568 loadRGBScaleAndBrightness(c0, c1, c2, brightness);
569 }
570
573 FASTLED_FORCE_INLINE fl::u8 getBrightness() {
574 return mColorAdjustment.brightness;
575 }
576 #endif
577
578 // NOTE: loadAndScale_APA102_HD() has been moved to src/fl/chipsets/encoders/apa102.h
579 // Use fl::loadAndScale_APA102_HD<RGB_ORDER>(pixels, ...) instead
580
582 fl::u8 *b2_out) {
583 *b0_out = loadAndScale0();
584 *b1_out = loadAndScale1();
585 *b2_out = loadAndScale2();
586 }
587
588 // NOTE: loadAndScale_WS2816_HD() has been moved to src/fl/chipsets/encoders/ws2816.h
589 // Use fl::loadAndScale_WS2816_HD<RGB_ORDER>(pixels, ...) instead
590
592 fl::u8 *b2_out, fl::u8 *b3_out) {
593#ifdef FL_IS_AVR
594 // Don't do RGBW conversion for AVR, just set the W pixel to black.
595 fl::u8 out[4] = {
596 // Get the pixels in native order.
600 0,
601 };
602 EOrderW w_placement = rgbw.w_placement;
603 // Apply w-component insertion.
605 w_placement, out[0], out[1], out[2],
606 0, // Pre-ordered RGB data with a 0 white component.
607 b0_out, b1_out, b2_out, b3_out);
608#else
609 const fl::u8 b0_index = RGB_BYTE0(RGB_ORDER); // Needed to re-order RGB back into led native order.
610 const fl::u8 b1_index = RGB_BYTE1(RGB_ORDER);
611 const fl::u8 b2_index = RGB_BYTE2(RGB_ORDER);
612 // Get the naive RGB data order in r,g,b order.
613 CRGB rgb(mData[0], mData[1], mData[2]);
614 fl::u8 w = 0;
615 fl::rgb_2_rgbw(rgbw.rgbw_mode,
616 rgbw.white_color_temp,
617 rgb.r, rgb.g, rgb.b, // Input colors
618 mColorAdjustment.premixed.r, mColorAdjustment.premixed.g, mColorAdjustment.premixed.b, // How these colors are scaled for color balance.
619 &rgb.r, &rgb.g, &rgb.b, &w);
620 // Now finish the ordering so that the output is in the native led order for all of RGBW.
622 rgbw.w_placement,
623 rgb.raw[b0_index], // in-place re-ordering for the RGB data.
624 rgb.raw[b1_index],
625 rgb.raw[b2_index],
626 w, // The white component is not ordered in this call.
627 b0_out, b1_out, b2_out, b3_out); // RGBW data now in total native led order.
628#endif
629 }
630
636 fl::u8 *b0_out, fl::u8 *b1_out,
637 fl::u8 *b2_out, fl::u8 *b3_out,
638 fl::u8 *b4_out) {
639#ifdef FL_IS_AVR
640 // AVR: float colorimetric math is too expensive on the 8-bit core;
641 // the strip will get RGB-only output with both white channels black.
642 // Surface this with a FL_WARN_ONCE so the silent dropout is visible
643 // when debugging — a user configuring RGBWW on an AVR target almost
644 // certainly didn't expect their warm/cool W channels to be inert.
645 FL_WARN_ONCE("RGBWW colorimetric is not supported on AVR — the warm "
646 "and cool white channels will be black. Use an ESP32 / "
647 "Teensy / RP2040 target for full RGBWW support.");
648 fl::u8 r_pre = loadAndScale0();
649 fl::u8 g_pre = loadAndScale1();
650 fl::u8 b_pre = loadAndScale2();
652 rgbww.w_placement,
653 r_pre, g_pre, b_pre,
654 0, 0,
655 b0_out, b1_out, b2_out, b3_out, b4_out);
656#else
657 const fl::u8 b0_index = RGB_BYTE0(RGB_ORDER);
658 const fl::u8 b1_index = RGB_BYTE1(RGB_ORDER);
659 const fl::u8 b2_index = RGB_BYTE2(RGB_ORDER);
660 CRGB rgb(mData[0], mData[1], mData[2]);
661 fl::u8 ww = 0;
662 fl::u8 wc = 0;
663 fl::rgb_2_rgbww(rgbww,
664 rgb.r, rgb.g, rgb.b,
665 mColorAdjustment.premixed.r,
666 mColorAdjustment.premixed.g,
667 mColorAdjustment.premixed.b,
668 &rgb.r, &rgb.g, &rgb.b, &ww, &wc);
670 rgbww.w_placement,
671 rgb.raw[b0_index],
672 rgb.raw[b1_index],
673 rgb.raw[b2_index],
674 ww, wc,
675 b0_out, b1_out, b2_out, b3_out, b4_out);
676#endif
677 }
678};
679
680/*
681using fl::RGB;
682using fl::RBG;
683using fl::GRB;
684using fl::GBR;
685using fl::BRG;
686using fl::BGR;
687*/
688
689
690
691
692
693
694
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
fl::UISlider scale("Scale", 4,.1, 4,.1)
#define RGB_ORDER
Rgbw rgbw
Legacy header.
#define BINARY_DITHER
Enable dithering using binary dithering (only option)
Definition dither_mode.h:12
fl::u8 EDitherMode
The dither setting, either DISABLE_DITHER or BINARY_DITHER.
Definition dither_mode.h:15
Declares dithering options and types.
fl::EOrderW EOrderW
Definition eorder.h:13
Defines color channel ordering enumerations.
Declares functions for five-bit gamma correction.
Integer mapping functions between different integer sizes.
Legacy compatibility header for 8-bit math functions.
Legacy compatibility header for 8-bit scaling functions.
fl::CRGB CRGB
Definition crgb.h:25
unsigned char u8
Definition stdint.h:131
#define FL_WARN_ONCE(X)
Definition log.h:278
Centralized logging categories for FastLED hardware interfaces and subsystems.
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
FASTLED_FORCE_INLINE void rgb_2_rgbw(RGBW_MODE mode, 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
Converts RGB to RGBW using one of the functions.
Definition rgbw.h:317
FASTLED_FORCE_INLINE void rgb_2_rgbww(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) FL_NOEXCEPT
Definition rgbww.h:173
signed char i8
Definition stdint.h:130
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.
Definition crgb.hpp:179
#define VIRTUAL_BITS
Alias for RECOMMENDED_VIRTUAL_BITS.
#define RGB_BYTE2(RO)
Gets the color channel for byte 2.
#define RGB_BYTE1(RO)
Gets the color channel for byte 1.
#define RGB_BYTE0(RO)
Gets the color channel for byte 0.
#define RO(X)
Gets the assigned color channel for a byte's position in the output, using the color order (EOrder) t...
Legacy header.
Functions for red, green, blue, white (RGBW) output.
5-channel RGB + warm-W + cool-W (RGBWW / RGBCCT) configuration types (issue #2558,...
#define FL_DISABLE_WARNING_IMPLICIT_INT_CONVERSION
#define FL_STATIC_ASSERT(...)
#define FASTLED_FORCE_INLINE
#define FL_DISABLE_WARNING_PUSH
#define FL_DEPRECATED(msg)
#define FL_DISABLE_WARNING_SIGN_CONVERSION
#define FL_DISABLE_WARNING_POP
#define FL_DISABLE_WARNING_FLOAT_CONVERSION
Portable compile-time assertion wrapper.
static ColorAdjustment noAdjustment()
the per-channel scale values premixed with brightness.
Color adjustment structure for pixel output.
static FASTLED_FORCE_INLINE fl::u8 loadAndScale(PixelController &pc, int lane, fl::u8 d, fl::u8 scale)
Complete pipeline: load → dither → scale (explicit dither/scale values)
static FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale(PixelController &pc, int lane)
A version of loadAndScale() that advances the output data pointer.
FASTLED_FORCE_INLINE fl::u8 loadAndScale1()
FASTLED_FORCE_INLINE int size() const
Get the length of the LED strip.
PixelController(const fl::u8 *d, int len, ColorAdjustment color_adjustment, EDitherMode dither, bool advance, fl::u8 skip)
Constructor.
static FASTLED_FORCE_INLINE fl::u8 scale(PixelController &, fl::u8 b, fl::u8 scale)
Scale a value.
static FASTLED_FORCE_INLINE fl::u8 loadByte(PixelController &pc)
Read a byte of LED data.
void init_binary_dithering()
Set up the values for binary dithering.
static FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale(PixelController &pc, int lane, fl::u8 scale)
A version of loadAndScale() that advances the output data pointer without dithering.
int mLenRemaining
counter for the number of LEDs left to process
void initOffsets(int len)
Initialize the PixelController::mOffsets array based on the length of the strip.
FASTLED_FORCE_INLINE fl::u8 stepAdvanceAndLoadAndScale0(int lane, fl::u8 scale)
stepDithering() and advanceAndLoadAndScale0()
FASTLED_FORCE_INLINE fl::u8 getScale1()
non-template alias of getscale<1>()
FASTLED_FORCE_INLINE void preStepFirstByteDithering()
Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately.
fl::u8 e[3]
[DITHER] Max dither range per R,G,B channel (inversely proportional to brightness)
FASTLED_FORCE_INLINE void loadAndScaleRGBWW(fl::Rgbww rgbww, fl::u8 *b0_out, fl::u8 *b1_out, fl::u8 *b2_out, fl::u8 *b3_out, fl::u8 *b4_out)
Load + scale a single pixel to 5-channel RGBWW (issue #2558).
FASTLED_FORCE_INLINE fl::u8 loadAndScale0()
FASTLED_FORCE_INLINE int advanceBy()
Get the amount to advance the pointer by.
static FASTLED_FORCE_INLINE fl::u8 loadAndScale(PixelController &pc, int lane)
Complete pipeline: load → dither → scale (parallel output version)
static FASTLED_FORCE_INLINE fl::u8 loadByte(PixelController &pc, int lane)
Read a byte of LED data for parallel output.
static FASTLED_FORCE_INLINE fl::u8 scale(PixelController &pc, fl::u8 b)
Scale a value using the per-channel scale data.
FASTLED_FORCE_INLINE int lanes()
Get the number of lanes of the Controller.
fl::u8 d[3]
[DITHER] Current dither offset per R,G,B channel (toggles via stepDithering)
FASTLED_FORCE_INLINE fl::u8 stepAdvanceAndLoadAndScale0()
int mOffsets[LANES]
the number of bytes to offset each lane from the starting pointer
FASTLED_FORCE_INLINE fl::u8 loadAndScale2()
static FASTLED_FORCE_INLINE fl::u8 getscale(PixelController &pc)
Gets the scale data for the provided output slot.
PixelController(const PixelController< RGB_ORDER_OTHER, LANES, MASK > &other)
FASTLED_FORCE_INLINE void loadAndScaleRGBW(Rgbw rgbw, fl::u8 *b0_out, fl::u8 *b1_out, fl::u8 *b2_out, fl::u8 *b3_out)
PixelController(const CRGB &d, int len, ColorAdjustment color_adjustment, EDitherMode dither)
Constructor.
ColorAdjustment mColorAdjustment
const fl::u8 * mData
pointer to the underlying LED data
FASTLED_FORCE_INLINE void loadAndScaleRGB(fl::u8 *b0_out, fl::u8 *b1_out, fl::u8 *b2_out)
int mLen
number of LEDs in the data for one lane
FASTLED_FORCE_INLINE fl::u8 loadAndScale0(int lane, fl::u8 scale)
non-template alias of loadAndScale<0>()
FASTLED_FORCE_INLINE fl::u8 loadAndScale2(int lane)
const fl::u8 * getRawPixelData() const
Get read-only access to the current pixel data Used by encoders to access raw RGB values for HD proce...
static FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale(PixelController &pc)
A version of loadAndScale() that advances the output data pointer.
FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale0()
void enable_dithering(EDitherMode dither)
Toggle dithering enable/disable.
FASTLED_FORCE_INLINE fl::u8 getScale0()
non-template alias of getscale<0>()
FASTLED_FORCE_INLINE void advanceData()
Advance the data pointer forward, adjust position counter.
static FASTLED_FORCE_INLINE fl::u8 dither(PixelController &, fl::u8 b, fl::u8 d)
Add explicit dither offset to pixel value (BEFORE scaling).
FASTLED_FORCE_INLINE fl::u8 loadAndScale1(int lane, fl::u8 scale)
non-template alias of loadAndScale<1>()
FASTLED_FORCE_INLINE fl::u8 stepAdvanceAndLoadAndScale0(int lane)
FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale0(int lane, fl::u8 scale)
non-template alias of advanceAndLoadAndScale<0>()
FASTLED_FORCE_INLINE bool has(int n)
Do we have n pixels left to process?
static FASTLED_FORCE_INLINE fl::u8 getd(PixelController &pc)
Gets the dithering data for the provided output slot.
PixelController(const CRGB *d, int len, ColorAdjustment color_adjustment, EDitherMode dither)
Constructor.
FASTLED_FORCE_INLINE void stepDithering()
Step the dithering forward - creates triangular wave that toggles between pixels.
FASTLED_FORCE_INLINE fl::u8 loadAndScale2(int lane, fl::u8 scale)
non-template alias of loadAndScale<2>()
FASTLED_FORCE_INLINE fl::PixelIterator as_iterator(const Rgbw &rgbw)
static FASTLED_FORCE_INLINE fl::u8 loadAndScale(PixelController &pc)
Complete pipeline: load → dither → scale (THE MAGIC HAPPENS HERE!) Order is critical: pixel + dither ...
FASTLED_FORCE_INLINE fl::u8 loadAndScale1(int lane)
PixelController(const PixelController &other)
Copy constructor.
FASTLED_FORCE_INLINE fl::u8 getScale2()
non-template alias of getscale<2>()
static FASTLED_FORCE_INLINE fl::u8 loadAndScale(PixelController &pc, int lane, fl::u8 scale)
Loads and scales a single byte for a given output slot and lane.
static FASTLED_FORCE_INLINE fl::u8 dither(PixelController &pc, fl::u8 b)
Add dither offset to pixel value (BEFORE scaling).
void copy(const PixelControllerT &other)
FASTLED_FORCE_INLINE fl::u8 advanceAndLoadAndScale0(int lane)
fl::i8 mAdvance
how many bytes to advance the pointer by each time. For CRGB this is 3.
FASTLED_FORCE_INLINE fl::u8 loadAndScale0(int lane)
Pixel controller class.
EOrderWW w_placement
Definition rgbww.h:72
Per-strip RGBWW configuration.
Definition rgbww.h:60