FastLED 3.7.8
Loading...
Searching...
No Matches
pixel_controller.h
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#include "FastLED.h"
13#include <stddef.h>
14#include "rgbw.h"
15#include "five_bit_hd_gamma.h"
16#include "force_inline.h"
17#include "namespace.h"
18#include "eorder.h"
19#include "dither_mode.h"
20#include "pixel_iterator.h"
21
22
23FASTLED_NAMESPACE_BEGIN
24
25
32#define RO(X) RGB_BYTE(RGB_ORDER, X)
33
40#define RGB_BYTE(RO,X) (((RO)>>(3*(2-(X)))) & 0x3)
41
44#define RGB_BYTE0(RO) ((RO>>6) & 0x3)
47#define RGB_BYTE1(RO) ((RO>>3) & 0x3)
50#define RGB_BYTE2(RO) ((RO) & 0x3)
51
52// operator byte *(struct CRGB[] arr) { return (byte*)arr; }
53
54
61template<EOrder RGB_ORDER, int LANES=1, uint32_t MASK=0xFFFFFFFF>
63 const uint8_t *mData;
64 int mLen;
66 uint8_t d[3];
67 uint8_t e[3];
69 int8_t mAdvance;
70 int mOffsets[LANES];
71
72 PixelIterator as_iterator(const Rgbw& rgbw) {
73 return PixelIterator(this, rgbw);
74 }
75
79 copy(other);
80 }
81
82 template<EOrder RGB_ORDER_OTHER>
84 copy(other);
85 }
86
87 template<typename PixelControllerT>
88 void copy(const PixelControllerT& other) {
89 d[0] = other.d[0];
90 d[1] = other.d[1];
91 d[2] = other.d[2];
92 e[0] = other.e[0];
93 e[1] = other.e[1];
94 e[2] = other.e[2];
95 mData = other.mData;
96 mScale = other.mScale;
97 mAdvance = other.mAdvance;
98 mLenRemaining = mLen = other.mLen;
99 for(int i = 0; i < LANES; ++i) { mOffsets[i] = other.mOffsets[i]; }
100 }
101
104 void initOffsets(int len) {
105 int nOffset = 0;
106 for(int i = 0; i < LANES; ++i) {
107 mOffsets[i] = nOffset;
108 if((1<<i) & MASK) { nOffset += (len * mAdvance); }
109 }
110 }
111
120 const uint8_t *d, int len, CRGB & pre_mixed,
121 EDitherMode dither, bool advance, uint8_t skip)
122 : mData(d), mLen(len), mLenRemaining(len), mScale(pre_mixed) {
124 mData += skip;
125 mAdvance = (advance) ? 3+skip : 0;
126 initOffsets(len);
127 }
128
135 const CRGB *d, int len, CRGB & pre_mixed,
136 EDitherMode dither)
137 : mData((const uint8_t*)d), mLen(len), mLenRemaining(len), mScale(pre_mixed) {
139 mAdvance = 3;
140 initOffsets(len);
141 }
142
149 const CRGB &d, int len, CRGB & pre_mixed, EDitherMode dither)
150 : mData((const uint8_t*)&d), mLen(len), mLenRemaining(len), mScale(pre_mixed) {
152 mAdvance = 0;
153 initOffsets(len);
154 }
155
156
157#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
158
160#define MAX_LIKELY_UPDATE_RATE_HZ 400
161
163#define MIN_ACCEPTABLE_DITHER_RATE_HZ 50
164
166#define UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ)
167
181#define RECOMMENDED_VIRTUAL_BITS ((UPDATES_PER_FULL_DITHER_CYCLE>1) + \
182 (UPDATES_PER_FULL_DITHER_CYCLE>2) + \
183 (UPDATES_PER_FULL_DITHER_CYCLE>4) + \
184 (UPDATES_PER_FULL_DITHER_CYCLE>8) + \
185 (UPDATES_PER_FULL_DITHER_CYCLE>16) + \
186 (UPDATES_PER_FULL_DITHER_CYCLE>32) + \
187 (UPDATES_PER_FULL_DITHER_CYCLE>64) + \
188 (UPDATES_PER_FULL_DITHER_CYCLE>128) )
189
191#define VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS
192
193#endif
194
195
198#if !defined(NO_DITHERING) || (NO_DITHERING != 1)
199 // R is the digther signal 'counter'.
200 static uint8_t R = 0;
201 ++R;
202
203 // R is wrapped around at 2^ditherBits,
204 // so if ditherBits is 2, R will cycle through (0,1,2,3)
205 uint8_t ditherBits = VIRTUAL_BITS;
206 R &= (0x01 << ditherBits) - 1;
207
208 // Q is the "unscaled dither signal" itself.
209 // It's initialized to the reversed bits of R.
210 // If 'ditherBits' is 2, Q here will cycle through (0,128,64,192)
211 uint8_t Q = 0;
212
213 // Reverse bits in a byte
214 {
215 if(R & 0x01) { Q |= 0x80; }
216 if(R & 0x02) { Q |= 0x40; }
217 if(R & 0x04) { Q |= 0x20; }
218 if(R & 0x08) { Q |= 0x10; }
219 if(R & 0x10) { Q |= 0x08; }
220 if(R & 0x20) { Q |= 0x04; }
221 if(R & 0x40) { Q |= 0x02; }
222 if(R & 0x80) { Q |= 0x01; }
223 }
224
225 // Now we adjust Q to fall in the center of each range,
226 // instead of at the start of the range.
227 // If ditherBits is 2, Q will be (0, 128, 64, 192) at first,
228 // and this adjustment makes it (31, 159, 95, 223).
229 if( ditherBits < 8) {
230 Q += 0x01 << (7 - ditherBits);
231 }
232
233 // D and E form the "scaled dither signal"
234 // which is added to pixel values to affect the
235 // actual dithering.
236
237 // Setup the initial D and E values
238 for(int i = 0; i < 3; ++i) {
239 uint8_t s = mScale.raw[i];
240 e[i] = s ? (256/s) + 1 : 0;
241 d[i] = scale8(Q, e[i]);
242#if (FASTLED_SCALE8_FIXED == 1)
243 if(d[i]) (--d[i]);
244#endif
245 if(e[i]) --e[i];
246 }
247#endif
248 }
249
253 FASTLED_FORCE_INLINE bool has(int n) {
254 return mLenRemaining >= n;
255 }
256
262 void enable_dithering(EDitherMode dither) {
263 switch(dither) {
264 case BINARY_DITHER: init_binary_dithering(); break;
265 default: d[0]=d[1]=d[2]=e[0]=e[1]=e[2]=0; break;
266 }
267 }
268
271 FASTLED_FORCE_INLINE int size() { return mLen; }
272
275 FASTLED_FORCE_INLINE int lanes() { return LANES; }
276
279 FASTLED_FORCE_INLINE int advanceBy() { return mAdvance; }
280
282 FASTLED_FORCE_INLINE void advanceData() { mData += mAdvance; --mLenRemaining;}
283
286 FASTLED_FORCE_INLINE void stepDithering() {
287 // IF UPDATING HERE, BE SURE TO UPDATE THE ASM VERSION IN
288 // clockless_trinket.h!
289 d[0] = e[0] - d[0];
290 d[1] = e[1] - d[1];
291 d[2] = e[2] - d[2];
292 }
293
295 FASTLED_FORCE_INLINE void preStepFirstByteDithering() {
296 d[RO(0)] = e[RO(0)] - d[RO(0)];
297 }
298
302
306 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadByte(PixelController & pc) { return pc.mData[RO(SLOT)]; }
307
312 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadByte(PixelController & pc, int lane) { return pc.mData[pc.mOffsets[lane] + RO(SLOT)]; }
313
319 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t dither(PixelController & pc, uint8_t b) { return b ? qadd8(b, pc.d[RO(SLOT)]) : 0; }
320
325 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t dither(PixelController & , uint8_t b, uint8_t d) { return b ? qadd8(b,d) : 0; }
326
332 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t scale(PixelController & pc, uint8_t b) { return scale8(b, pc.mScale.raw[RO(SLOT)]); }
333
338 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t scale(PixelController & , uint8_t b, uint8_t scale) { return scale8(b, scale); }
339
345
346
350 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadAndScale(PixelController & pc) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc))); }
351
356 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadAndScale(PixelController & pc, int lane) { return scale<SLOT>(pc, pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane))); }
357
364 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t d, uint8_t scale) { return scale8(pc.dither<SLOT>(pc, pc.loadByte<SLOT>(pc, lane), d), scale); }
365
371 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t loadAndScale(PixelController & pc, int lane, uint8_t scale) { return scale8(pc.loadByte<SLOT>(pc, lane), scale); }
372
373
376 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t advanceAndLoadAndScale(PixelController & pc) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc); }
377
381 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane); }
382
387 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t advanceAndLoadAndScale(PixelController & pc, int lane, uint8_t scale) { pc.advanceData(); return pc.loadAndScale<SLOT>(pc, lane, scale); }
388
390
391
396
402 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t getd(PixelController & pc) { return pc.d[RO(SLOT)]; }
403
409 template<int SLOT> FASTLED_FORCE_INLINE static uint8_t getscale(PixelController & pc) { return pc.mScale.raw[RO(SLOT)]; }
410
412
413
415
416 // Helper functions to get around gcc stupidities
417 FASTLED_FORCE_INLINE uint8_t loadAndScale0(int lane, uint8_t scale) { return loadAndScale<0>(*this, lane, scale); }
418 FASTLED_FORCE_INLINE uint8_t loadAndScale1(int lane, uint8_t scale) { return loadAndScale<1>(*this, lane, scale); }
419 FASTLED_FORCE_INLINE uint8_t loadAndScale2(int lane, uint8_t scale) { return loadAndScale<2>(*this, lane, scale); }
420 FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale) { return advanceAndLoadAndScale<0>(*this, lane, scale); }
421 FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane, scale); }
422
423 FASTLED_FORCE_INLINE uint8_t loadAndScale0(int lane) { return loadAndScale<0>(*this, lane); }
424 FASTLED_FORCE_INLINE uint8_t loadAndScale1(int lane) { return loadAndScale<1>(*this, lane); }
425 FASTLED_FORCE_INLINE uint8_t loadAndScale2(int lane) { return loadAndScale<2>(*this, lane); }
426 FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0(int lane) { return advanceAndLoadAndScale<0>(*this, lane); }
427 FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0(int lane) { stepDithering(); return advanceAndLoadAndScale<0>(*this, lane); }
428
429 // LoadAndScale0 loads the pixel data in the order specified by RGB_ORDER and then scales it by the color correction values
430 // For example in color order GRB, loadAndScale0() will return the green channel scaled by the color correction value for green.
431 FASTLED_FORCE_INLINE uint8_t loadAndScale0() { return loadAndScale<0>(*this); }
432 FASTLED_FORCE_INLINE uint8_t loadAndScale1() { return loadAndScale<1>(*this); }
433 FASTLED_FORCE_INLINE uint8_t loadAndScale2() { return loadAndScale<2>(*this); }
434 FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0() { return advanceAndLoadAndScale<0>(*this); }
435 FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0() { stepDithering(); return advanceAndLoadAndScale<0>(*this); }
436
437 FASTLED_FORCE_INLINE uint8_t getScale0() { return getscale<0>(*this); }
438 FASTLED_FORCE_INLINE uint8_t getScale1() { return getscale<1>(*this); }
439 FASTLED_FORCE_INLINE uint8_t getScale2() { return getscale<2>(*this); }
440
441
442 FASTLED_FORCE_INLINE void loadAndScale_APA102_HD(uint8_t *b0_out, uint8_t *b1_out,
443 uint8_t *b2_out,
444 uint8_t *brightness_out) {
445 CRGB rgb = CRGB(mData[0], mData[1], mData[2]);
446 uint8_t brightness = 0;
447 if (rgb) {
448 five_bit_hd_gamma_bitshift(
449 rgb.r, rgb.g, rgb.b,
450 // Note this mScale has the global brightness scale mixed in
451 // with the color correction scale.
453 &rgb.r, &rgb.g, &rgb.b, &brightness);
454 }
455 const uint8_t b0_index = RGB_BYTE0(RGB_ORDER);
456 const uint8_t b1_index = RGB_BYTE1(RGB_ORDER);
457 const uint8_t b2_index = RGB_BYTE2(RGB_ORDER);
458 *b0_out = rgb.raw[b0_index];
459 *b1_out = rgb.raw[b1_index];
460 *b2_out = rgb.raw[b2_index];
461 *brightness_out = brightness;
462 }
463
464 FASTLED_FORCE_INLINE void loadAndScaleRGB(uint8_t *b0_out, uint8_t *b1_out,
465 uint8_t *b2_out) {
466 *b0_out = loadAndScale0();
467 *b1_out = loadAndScale1();
468 *b2_out = loadAndScale2();
469 }
470
471 FASTLED_FORCE_INLINE void loadAndScaleRGBW(Rgbw rgbw, uint8_t *b0_out, uint8_t *b1_out,
472 uint8_t *b2_out, uint8_t *b3_out) {
473#ifdef __AVR__
474 // Don't do RGBW conversion for AVR, just set the W pixel to black.
475 uint8_t out[4] = {
476 // Get the pixels in native order.
480 0,
481 };
482 EOrderW w_placement = rgbw.w_placement;
483 // Apply w-component insertion.
484 rgbw_partial_reorder(
485 w_placement, out[0], out[1], out[2],
486 0, // Pre-ordered RGB data with a 0 white component.
487 b0_out, b1_out, b2_out, b3_out);
488#else
489 const uint8_t b0_index = RGB_BYTE0(RGB_ORDER); // Needed to re-order RGB back into led native order.
490 const uint8_t b1_index = RGB_BYTE1(RGB_ORDER);
491 const uint8_t b2_index = RGB_BYTE2(RGB_ORDER);
492 // Get the naive RGB data order in r,g,b order.
493 CRGB rgb(mData[0], mData[1], mData[2]);
494 uint8_t w = 0;
495 rgb_2_rgbw(rgbw.rgbw_mode,
496 rgbw.white_color_temp,
497 rgb.r, rgb.b, rgb.g, // Input colors
498 mScale.r, mScale.g, mScale.b, // How these colors are scaled for color balance.
499 &rgb.r, &rgb.g, &rgb.b, &w);
500 // Now finish the ordering so that the output is in the native led order for all of RGBW.
501 rgbw_partial_reorder(
502 rgbw.w_placement,
503 rgb.raw[b0_index], // in-place re-ordering for the RGB data.
504 rgb.raw[b1_index],
505 rgb.raw[b2_index],
506 w, // The white component is not ordered in this call.
507 b0_out, b1_out, b2_out, b3_out); // RGBW data now in total native led order.
508#endif
509 }
510};
511
512
513FASTLED_NAMESPACE_END
514
central include file for FastLED, defines the CFastLED class/object
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8(uint8_t i, uint8_t j)
Add one byte to another, saturating at 0xFF.
Definition math8.h:27
uint8_t raw[3]
Access the red, green, and blue data as an array.
Definition crgb.h:46
uint8_t r
Red channel value.
Definition crgb.h:29
uint8_t g
Green channel value.
Definition crgb.h:33
uint8_t b
Blue channel value.
Definition crgb.h:37
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale)
Scale one byte by a second one, which is treated as the numerator of a fraction whose denominator is ...
Definition scale8.h:29
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:25
Pixel controller class.
FASTLED_FORCE_INLINE uint8_t loadAndScale0(int lane)
non-template alias of loadAndScale<0>()
static FASTLED_FORCE_INLINE uint8_t scale(PixelController &pc, uint8_t b)
Scale a value using the per-channel scale data.
FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0()
stepDithering() and advanceAndLoadAndScale0()
int8_t mAdvance
how many bytes to advance the pointer by each time. For CRGB this is 3.
FASTLED_FORCE_INLINE uint8_t loadAndScale1(int lane, uint8_t scale)
non-template alias of loadAndScale<1>()
FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0(int lane)
stepDithering() and advanceAndLoadAndScale0()
static FASTLED_FORCE_INLINE uint8_t loadAndScale(PixelController &pc, int lane, uint8_t scale)
Loads and scales a single byte for a given output slot and lane.
void init_binary_dithering()
Set up the values for binary dithering.
FASTLED_FORCE_INLINE uint8_t loadAndScale1(int lane)
non-template alias of loadAndScale<1>()
FASTLED_FORCE_INLINE uint8_t getScale0()
non-template alias of getscale<0>()
int mLenRemaining
counter for the number of LEDs left to process
FASTLED_FORCE_INLINE uint8_t loadAndScale2(int lane, uint8_t scale)
non-template alias of loadAndScale<2>()
void initOffsets(int len)
Initialize the PixelController::mOffsets array based on the length of the strip.
FASTLED_FORCE_INLINE void preStepFirstByteDithering()
Some chipsets pre-cycle the first byte, which means we want to cycle byte 0's dithering separately.
FASTLED_FORCE_INLINE uint8_t loadAndScale2(int lane)
non-template alias of loadAndScale<2>()
FASTLED_FORCE_INLINE uint8_t loadAndScale0(int lane, uint8_t scale)
non-template alias of loadAndScale<0>()
FASTLED_FORCE_INLINE uint8_t loadAndScale0()
non-template alias of loadAndScale<0>()
FASTLED_FORCE_INLINE int advanceBy()
Get the amount to advance the pointer by.
const uint8_t * mData
pointer to the underlying LED data
static FASTLED_FORCE_INLINE uint8_t loadAndScale(PixelController &pc)
Loads, dithers, and scales a single byte for a given output slot, using class dither and scale values...
uint8_t d[3]
values for the scaled dither signal
FASTLED_FORCE_INLINE int lanes()
Get the number of lanes of the Controller.
int mOffsets[LANES]
the number of bytes to offset each lane from the starting pointer
FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0(int lane, uint8_t scale)
non-template alias of advanceAndLoadAndScale<0>()
FASTLED_FORCE_INLINE uint8_t getScale2()
non-template alias of getscale<2>()
static FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale(PixelController &pc)
A version of loadAndScale() that advances the output data pointer.
int mLen
number of LEDs in the data for one lane
static FASTLED_FORCE_INLINE uint8_t loadAndScale(PixelController &pc, int lane)
Loads, dithers, and scales a single byte for a given output slot and lane, using class dither and sca...
static FASTLED_FORCE_INLINE uint8_t loadByte(PixelController &pc)
Read a byte of LED data.
static FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale(PixelController &pc, int lane, uint8_t scale)
A version of loadAndScale() that advances the output data pointer without dithering.
static FASTLED_FORCE_INLINE uint8_t loadAndScale(PixelController &pc, int lane, uint8_t d, uint8_t scale)
Loads, dithers, and scales a single byte for a given output slot and lane.
FASTLED_FORCE_INLINE int size()
Get the length of the LED strip.
static FASTLED_FORCE_INLINE uint8_t getd(PixelController &pc)
Gets the dithering data for the provided output slot.
FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0(int lane)
non-template alias of advanceAndLoadAndScale<0>()
uint8_t e[3]
values for the scaled dither signal
CRGB mScale
the per-channel scale values, provided by a color correction function such as CLEDController::compute...
static FASTLED_FORCE_INLINE uint8_t getscale(PixelController &pc)
Gets the scale data for the provided output slot.
static FASTLED_FORCE_INLINE uint8_t dither(PixelController &, uint8_t b, uint8_t d)
Calculate a dither value.
static FASTLED_FORCE_INLINE uint8_t dither(PixelController &pc, uint8_t b)
Calculate a dither value using the per-channel dither data.
FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale0()
non-template alias of advanceAndLoadAndScale<0>()
void enable_dithering(EDitherMode dither)
Toggle dithering enable If dithering is set to enabled, this will re-init the dithering values (init_...
PixelController(const CRGB &d, int len, CRGB &pre_mixed, EDitherMode dither)
Constructor.
static FASTLED_FORCE_INLINE uint8_t scale(PixelController &, uint8_t b, uint8_t scale)
Scale a value.
FASTLED_FORCE_INLINE void advanceData()
Advance the data pointer forward, adjust position counter.
FASTLED_FORCE_INLINE bool has(int n)
Do we have n pixels left to process?
FASTLED_FORCE_INLINE void stepDithering()
Step the dithering forward.
FASTLED_FORCE_INLINE uint8_t stepAdvanceAndLoadAndScale0(int lane, uint8_t scale)
stepDithering() and advanceAndLoadAndScale0()
PixelController(const uint8_t *d, int len, CRGB &pre_mixed, EDitherMode dither, bool advance, uint8_t skip)
Constructor.
PixelController(const PixelController &other)
Copy constructor.
FASTLED_FORCE_INLINE uint8_t loadAndScale1()
non-template alias of loadAndScale<1>()
static FASTLED_FORCE_INLINE uint8_t advanceAndLoadAndScale(PixelController &pc, int lane)
A version of loadAndScale() that advances the output data pointer.
FASTLED_FORCE_INLINE uint8_t getScale1()
non-template alias of getscale<1>()
static FASTLED_FORCE_INLINE uint8_t loadByte(PixelController &pc, int lane)
Read a byte of LED data for parallel output.
FASTLED_FORCE_INLINE uint8_t loadAndScale2()
non-template alias of loadAndScale<2>()
PixelController(const CRGB *d, int len, CRGB &pre_mixed, EDitherMode dither)
Constructor.
Definition rgbw.h:25