FastLED 3.9.7
Loading...
Searching...
No Matches
colorutils.cpp
Go to the documentation of this file.
1#define FASTLED_INTERNAL
2#define __PROG_TYPES_COMPAT__
3
6
7#include <stdint.h>
8#include <math.h>
9
10
11#include "FastLED.h"
12#include "fl/xymap.h"
13
14using namespace fl;
15
17
18
19
20void fill_solid( struct CRGB * targetArray, int numToFill,
21 const struct CRGB& color)
22{
23 for( int i = 0; i < numToFill; ++i) {
24 targetArray[i] = color;
25 }
26}
27
28void fill_solid( struct CHSV * targetArray, int numToFill,
29 const struct CHSV& color)
30{
31 for( int i = 0; i < numToFill; ++i) {
32 targetArray[i] = color;
33 }
34}
35
36
37// void fill_solid( struct CRGB* targetArray, int numToFill,
38// const struct CHSV& hsvColor)
39// {
40// fill_solid<CRGB>( targetArray, numToFill, (CRGB) hsvColor);
41// }
42
43void fill_rainbow( struct CRGB * targetArray, int numToFill,
44 uint8_t initialhue,
45 uint8_t deltahue )
46{
47 CHSV hsv;
48 hsv.hue = initialhue;
49 hsv.val = 255;
50 hsv.sat = 240;
51 for( int i = 0; i < numToFill; ++i) {
52 targetArray[i] = hsv;
53 hsv.hue += deltahue;
54 }
55}
56
57void fill_rainbow( struct CHSV * targetArray, int numToFill,
58 uint8_t initialhue,
59 uint8_t deltahue )
60{
61 CHSV hsv;
62 hsv.hue = initialhue;
63 hsv.val = 255;
64 hsv.sat = 240;
65 for( int i = 0; i < numToFill; ++i) {
66 targetArray[i] = hsv;
67 hsv.hue += deltahue;
68 }
69}
70
71
72void fill_rainbow_circular(struct CRGB* targetArray, int numToFill, uint8_t initialhue, bool reversed)
73{
74 if (numToFill == 0) return; // avoiding div/0
75
76 CHSV hsv;
77 hsv.hue = initialhue;
78 hsv.val = 255;
79 hsv.sat = 240;
80
81 const uint16_t hueChange = 65535 / (uint16_t)numToFill; // hue change for each LED, * 256 for precision (256 * 256 - 1)
82 uint16_t hueOffset = 0; // offset for hue value, with precision (*256)
83
84 for (int i = 0; i < numToFill; ++i) {
85 targetArray[i] = hsv;
86 if (reversed) hueOffset -= hueChange;
87 else hueOffset += hueChange;
88 hsv.hue = initialhue + (uint8_t)(hueOffset >> 8); // assign new hue with precise offset (as 8-bit)
89 }
90}
91
92void fill_rainbow_circular(struct CHSV* targetArray, int numToFill, uint8_t initialhue, bool reversed)
93{
94 if (numToFill == 0) return; // avoiding div/0
95
96 CHSV hsv;
97 hsv.hue = initialhue;
98 hsv.val = 255;
99 hsv.sat = 240;
100
101 const uint16_t hueChange = 65535 / (uint16_t) numToFill; // hue change for each LED, * 256 for precision (256 * 256 - 1)
102 uint16_t hueOffset = 0; // offset for hue value, with precision (*256)
103
104 for (int i = 0; i < numToFill; ++i) {
105 targetArray[i] = hsv;
106 if (reversed) hueOffset -= hueChange;
107 else hueOffset += hueChange;
108 hsv.hue = initialhue + (uint8_t)(hueOffset >> 8); // assign new hue with precise offset (as 8-bit)
109 }
110}
111
112
114 uint16_t startpos, CRGB startcolor,
115 uint16_t endpos, CRGB endcolor )
116{
117 // if the points are in the wrong order, straighten them
118 if( endpos < startpos ) {
119 uint16_t t = endpos;
120 CRGB tc = endcolor;
121 endcolor = startcolor;
122 endpos = startpos;
123 startpos = t;
124 startcolor = tc;
125 }
126
127 saccum87 rdistance87;
128 saccum87 gdistance87;
129 saccum87 bdistance87;
130
131 rdistance87 = (endcolor.r - startcolor.r) << 7;
132 gdistance87 = (endcolor.g - startcolor.g) << 7;
133 bdistance87 = (endcolor.b - startcolor.b) << 7;
134
135 uint16_t pixeldistance = endpos - startpos;
136 int16_t divisor = pixeldistance ? pixeldistance : 1;
137
138 saccum87 rdelta87 = rdistance87 / divisor;
139 saccum87 gdelta87 = gdistance87 / divisor;
140 saccum87 bdelta87 = bdistance87 / divisor;
141
142 rdelta87 *= 2;
143 gdelta87 *= 2;
144 bdelta87 *= 2;
145
146 accum88 r88 = startcolor.r << 8;
147 accum88 g88 = startcolor.g << 8;
148 accum88 b88 = startcolor.b << 8;
149 for( uint16_t i = startpos; i <= endpos; ++i) {
150 leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8);
151 r88 += rdelta87;
152 g88 += gdelta87;
153 b88 += bdelta87;
154 }
155}
156
157#if 0
158void fill_gradient( const CHSV& c1, const CHSV& c2)
159{
160 fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2);
161}
162
163void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3)
164{
165 fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3);
166}
167
168void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4)
169{
170 fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4);
171}
172
173void fill_gradient_RGB( const CRGB& c1, const CRGB& c2)
174{
175 fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2);
176}
177
178void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3)
179{
180 fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3);
181}
182
183void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
184{
185 fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4);
186}
187#endif
188
189
190
191
192void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2)
193{
194 uint16_t last = numLeds - 1;
195 fill_gradient_RGB( leds, 0, c1, last, c2);
196}
197
198
199void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3)
200{
201 uint16_t half = (numLeds / 2);
202 uint16_t last = numLeds - 1;
203 fill_gradient_RGB( leds, 0, c1, half, c2);
204 fill_gradient_RGB( leds, half, c2, last, c3);
205}
206
207void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4)
208{
209 uint16_t onethird = (numLeds / 3);
210 uint16_t twothirds = ((numLeds * 2) / 3);
211 uint16_t last = numLeds - 1;
212 fill_gradient_RGB( leds, 0, c1, onethird, c2);
213 fill_gradient_RGB( leds, onethird, c2, twothirds, c3);
214 fill_gradient_RGB( leds, twothirds, c3, last, c4);
215}
216
217
218
219
220void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale)
221{
222 for( uint16_t i = 0; i < num_leds; ++i) {
223 leds[i].nscale8_video( scale);
224 }
225}
226
227void fade_video(CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
228{
229 nscale8_video( leds, num_leds, 255 - fadeBy);
230}
231
232void fadeLightBy(CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
233{
234 nscale8_video( leds, num_leds, 255 - fadeBy);
235}
236
237
238void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
239{
240 nscale8( leds, num_leds, 255 - fadeBy);
241}
242
243void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy)
244{
245 nscale8( leds, num_leds, 255 - fadeBy);
246}
247
248
249void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale)
250{
251 for( uint16_t i = 0; i < num_leds; ++i) {
252 leds[i].nscale8( scale);
253 }
254}
255
256void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask)
257{
258 uint8_t fr, fg, fb;
259 fr = colormask.r;
260 fg = colormask.g;
261 fb = colormask.b;
262
263 for( uint16_t i = 0; i < numLeds; ++i) {
264 leds[i].r = scale8_LEAVING_R1_DIRTY( leds[i].r, fr);
265 leds[i].g = scale8_LEAVING_R1_DIRTY( leds[i].g, fg);
266 leds[i].b = scale8 ( leds[i].b, fb);
267 }
268}
269
270
271CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay )
272{
273 if( amountOfOverlay == 0) {
274 return existing;
275 }
276
277 if( amountOfOverlay == 255) {
278 existing = overlay;
279 return existing;
280 }
281
282#if 0
283 // Old blend method which unfortunately had some rounding errors
284 fract8 amountOfKeep = 255 - amountOfOverlay;
285
286 existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep)
287 + scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay);
288 existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep)
289 + scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay);
290 existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep)
291 + scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay);
292
293 cleanup_R1();
294#else
295 // Corrected blend method, with no loss-of-precision rounding errors
296 existing.red = blend8( existing.red, overlay.red, amountOfOverlay);
297 existing.green = blend8( existing.green, overlay.green, amountOfOverlay);
298 existing.blue = blend8( existing.blue, overlay.blue, amountOfOverlay);
299#endif
300
301 return existing;
302}
303
304
305
306void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay)
307{
308 for( uint16_t i = count; i; --i) {
309 nblend( *existing, *overlay, amountOfOverlay);
310 ++existing;
311 ++overlay;
312 }
313}
314
315CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 )
316{
317 CRGB nu(p1);
318 nblend( nu, p2, amountOfP2);
319 return nu;
320}
321
322CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 )
323{
324 for( uint16_t i = 0; i < count; ++i) {
325 dest[i] = blend(src1[i], src2[i], amountOfsrc2);
326 }
327 return dest;
328}
329
330
331
332CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode)
333{
334 if( amountOfOverlay == 0) {
335 return existing;
336 }
337
338 if( amountOfOverlay == 255) {
339 existing = overlay;
340 return existing;
341 }
342
343 fract8 amountOfKeep = 255 - amountOfOverlay;
344
345 uint8_t huedelta8 = overlay.hue - existing.hue;
346
347 if( directionCode == SHORTEST_HUES ) {
348 directionCode = FORWARD_HUES;
349 if( huedelta8 > 127) {
350 directionCode = BACKWARD_HUES;
351 }
352 }
353
354 if( directionCode == LONGEST_HUES ) {
355 directionCode = FORWARD_HUES;
356 if( huedelta8 < 128) {
357 directionCode = BACKWARD_HUES;
358 }
359 }
360
361 if( directionCode == FORWARD_HUES) {
362 existing.hue = existing.hue + scale8( huedelta8, amountOfOverlay);
363 }
364 else /* directionCode == BACKWARD_HUES */
365 {
366 huedelta8 = -huedelta8;
367 existing.hue = existing.hue - scale8( huedelta8, amountOfOverlay);
368 }
369
370 existing.sat = scale8_LEAVING_R1_DIRTY( existing.sat, amountOfKeep)
371 + scale8_LEAVING_R1_DIRTY( overlay.sat, amountOfOverlay);
372 existing.val = scale8_LEAVING_R1_DIRTY( existing.val, amountOfKeep)
373 + scale8_LEAVING_R1_DIRTY( overlay.val, amountOfOverlay);
374
375 cleanup_R1();
376
377 return existing;
378}
379
380
381
382void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode )
383{
384 if(existing == overlay) return;
385 for( uint16_t i = count; i; --i) {
386 nblend( *existing, *overlay, amountOfOverlay, directionCode);
387 ++existing;
388 ++overlay;
389 }
390}
391
392CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode )
393{
394 CHSV nu(p1);
395 nblend( nu, p2, amountOfP2, directionCode);
396 return nu;
397}
398
399CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode )
400{
401 for( uint16_t i = 0; i < count; ++i) {
402 dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode);
403 }
404 return dest;
405}
406
407
408
409// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
410// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
411//
412// 0 = no spread at all
413// 64 = moderate spreading
414// 172 = maximum smooth, even spreading
415//
416// 173..255 = wider spreading, but increasing flicker
417//
418// Total light is NOT entirely conserved, so many repeated
419// calls to 'blur' will also result in the light fading,
420// eventually all the way to black; this is by design so that
421// it can be used to (slowly) clear the LEDs to black.
422void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount)
423{
424 uint8_t keep = 255 - blur_amount;
425 uint8_t seep = blur_amount >> 1;
426 CRGB carryover = CRGB::Black;
427 for( uint16_t i = 0; i < numLeds; ++i) {
428 CRGB cur = leds[i];
429 CRGB part = cur;
430 part.nscale8( seep);
431 cur.nscale8( keep);
432 cur += carryover;
433 if( i) leds[i-1] += part;
434 leds[i] = cur;
435 carryover = part;
436 }
437}
438
439void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap& xymap)
440{
441 blurRows(leds, width, height, blur_amount, xymap);
442 blurColumns(leds, width, height, blur_amount, xymap);
443}
444
445void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap& xyMap)
446{
447
448/* for( uint8_t row = 0; row < height; row++) {
449 CRGB* rowbase = leds + (row * width);
450 blur1d( rowbase, width, blur_amount);
451 }
452*/
453 // blur rows same as columns, for irregular matrix
454 uint8_t keep = 255 - blur_amount;
455 uint8_t seep = blur_amount >> 1;
456 for( uint8_t row = 0; row < height; row++) {
457 CRGB carryover = CRGB::Black;
458 for( uint8_t i = 0; i < width; i++) {
459 CRGB cur = leds[xyMap.mapToIndex(i,row)];
460 CRGB part = cur;
461 part.nscale8( seep);
462 cur.nscale8( keep);
463 cur += carryover;
464 if( i) leds[xyMap.mapToIndex(i-1,row)] += part;
465 leds[xyMap.mapToIndex(i,row)] = cur;
466 carryover = part;
467 }
468 }
469}
470
471// blurColumns: perform a blur1d on each column of a rectangular matrix
472void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap& xyMap)
473{
474 // blur columns
475 uint8_t keep = 255 - blur_amount;
476 uint8_t seep = blur_amount >> 1;
477 for( uint8_t col = 0; col < width; ++col) {
478 CRGB carryover = CRGB::Black;
479 for( uint8_t i = 0; i < height; ++i) {
480 CRGB cur = leds[xyMap.mapToIndex(col,i)];
481 CRGB part = cur;
482 part.nscale8( seep);
483 cur.nscale8( keep);
484 cur += carryover;
485 if( i) leds[xyMap.mapToIndex(col,i-1)] += part;
486 leds[xyMap.mapToIndex(col,i)] = cur;
487 carryover = part;
488 }
489 }
490}
491
492
493
494// CRGB HeatColor( uint8_t temperature)
495//
496// Approximates a 'black body radiation' spectrum for
497// a given 'heat' level. This is useful for animations of 'fire'.
498// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
499// This is NOT a chromatically correct 'black body radiation'
500// spectrum, but it's surprisingly close, and it's fast and small.
501//
502// On AVR/Arduino, this typically takes around 70 bytes of program memory,
503// versus 768 bytes for a full 256-entry RGB lookup table.
504
505CRGB HeatColor( uint8_t temperature)
506{
507 CRGB heatcolor;
508
509 // Scale 'heat' down from 0-255 to 0-191,
510 // which can then be easily divided into three
511 // equal 'thirds' of 64 units each.
512 uint8_t t192 = scale8_video( temperature, 191);
513
514 // calculate a value that ramps up from
515 // zero to 255 in each 'third' of the scale.
516 uint8_t heatramp = t192 & 0x3F; // 0..63
517 heatramp <<= 2; // scale up to 0..252
518
519 // now figure out which third of the spectrum we're in:
520 if( t192 & 0x80) {
521 // we're in the hottest third
522 heatcolor.r = 255; // full red
523 heatcolor.g = 255; // full green
524 heatcolor.b = heatramp; // ramp up blue
525
526 } else if( t192 & 0x40 ) {
527 // we're in the middle third
528 heatcolor.r = 255; // full red
529 heatcolor.g = heatramp; // ramp up green
530 heatcolor.b = 0; // no blue
531
532 } else {
533 // we're in the coolest third
534 heatcolor.r = heatramp; // ramp up red
535 heatcolor.g = 0; // no green
536 heatcolor.b = 0; // no blue
537 }
538
539 return heatcolor;
540}
541
542
547inline uint8_t lsrX4( uint8_t dividend) __attribute__((always_inline));
548inline uint8_t lsrX4( uint8_t dividend)
549{
550#if defined(__AVR__)
551 dividend /= 2;
552 dividend /= 2;
553 dividend /= 2;
554 dividend /= 2;
555#else
556 dividend >>= 4;
557#endif
558 return dividend;
559}
560
561CRGB ColorFromPaletteExtended(const CRGBPalette32& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
562 // Extract the five most significant bits of the index as a palette index.
563 uint8_t index_5bit = (index >> 11);
564 // Calculate the 8-bit offset from the palette index.
565 uint8_t offset = (uint8_t)(index >> 3);
566 // Get the palette entry from the 5-bit index
567 const CRGB* entry = &(pal[0]) + index_5bit;
568 uint8_t red1 = entry->red;
569 uint8_t green1 = entry->green;
570 uint8_t blue1 = entry->blue;
571
572 uint8_t blend = offset && (blendType != NOBLEND);
573 if (blend) {
574 if (index_5bit == 31) {
575 entry = &(pal[0]);
576 } else {
577 entry++;
578 }
579
580 // Calculate the scaling factor and scaled values for the lower palette value.
581 uint8_t f1 = 255 - offset;
582 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
583 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
584 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
585
586 // Calculate the scaled values for the neighbouring palette value.
587 uint8_t red2 = entry->red;
588 uint8_t green2 = entry->green;
589 uint8_t blue2 = entry->blue;
590 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
591 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
592 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
593 cleanup_R1();
594
595 // These sums can't overflow, so no qadd8 needed.
596 red1 += red2;
597 green1 += green2;
598 blue1 += blue2;
599 }
600 if (brightness != 255) {
601 nscale8x3_video(red1, green1, blue1, brightness);
602 }
603 return CRGB(red1, green1, blue1);
604}
605
606CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
607{
608 if ( blendType == LINEARBLEND_NOWRAP) {
609 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping
610 }
611
612 // hi4 = index >> 4;
613 uint8_t hi4 = lsrX4(index);
614 uint8_t lo4 = index & 0x0F;
615
616 // const CRGB* entry = &(pal[0]) + hi4;
617 // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value,
618 // instead of the two byte 'int' that avr-gcc defaults to.
619 // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB;
620 uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB);
621 // We then add that to a base array pointer.
622 const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB);
623
624 uint8_t blend = lo4 && (blendType != NOBLEND);
625
626 uint8_t red1 = entry->red;
627 uint8_t green1 = entry->green;
628 uint8_t blue1 = entry->blue;
629
630
631 if( blend ) {
632
633 if( hi4 == 15 ) {
634 entry = &(pal[0]);
635 } else {
636 ++entry;
637 }
638
639 uint8_t f2 = lo4 << 4;
640 uint8_t f1 = 255 - f2;
641
642 // rgb1.nscale8(f1);
643 uint8_t red2 = entry->red;
644 red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
645 red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
646 red1 += red2;
647
648 uint8_t green2 = entry->green;
649 green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
650 green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
651 green1 += green2;
652
653 uint8_t blue2 = entry->blue;
654 blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
655 blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
656 blue1 += blue2;
657
658 cleanup_R1();
659 }
660
661 if( brightness != 255) {
662 if( brightness ) {
663 ++brightness; // adjust for rounding
664 // Now, since brightness is nonzero, we don't need the full scale8_video logic;
665 // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs.
666 if( red1 ) {
667 red1 = scale8_LEAVING_R1_DIRTY( red1, brightness);
668#if !(FASTLED_SCALE8_FIXED==1)
669 ++red1;
670#endif
671 }
672 if( green1 ) {
673 green1 = scale8_LEAVING_R1_DIRTY( green1, brightness);
674#if !(FASTLED_SCALE8_FIXED==1)
675 ++green1;
676#endif
677 }
678 if( blue1 ) {
679 blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness);
680#if !(FASTLED_SCALE8_FIXED==1)
681 ++blue1;
682#endif
683 }
684 cleanup_R1();
685 } else {
686 red1 = 0;
687 green1 = 0;
688 blue1 = 0;
689 }
690 }
691
692 return CRGB( red1, green1, blue1);
693}
694
695CRGB ColorFromPaletteExtended(const CRGBPalette16& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
696 // Extract the four most significant bits of the index as a palette index.
697 uint8_t index_4bit = index >> 12;
698 // Calculate the 8-bit offset from the palette index.
699 uint8_t offset = (uint8_t)(index >> 4);
700 // Get the palette entry from the 4-bit index
701 const CRGB* entry = &(pal[0]) + index_4bit;
702 uint8_t red1 = entry->red;
703 uint8_t green1 = entry->green;
704 uint8_t blue1 = entry->blue;
705
706 uint8_t blend = offset && (blendType != NOBLEND);
707 if (blend) {
708 if (index_4bit == 15) {
709 entry = &(pal[0]);
710 } else {
711 entry++;
712 }
713
714 // Calculate the scaling factor and scaled values for the lower palette value.
715 uint8_t f1 = 255 - offset;
716 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
717 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
718 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
719
720 // Calculate the scaled values for the neighbouring palette value.
721 uint8_t red2 = entry->red;
722 uint8_t green2 = entry->green;
723 uint8_t blue2 = entry->blue;
724 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
725 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
726 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
727 cleanup_R1();
728
729 // These sums can't overflow, so no qadd8 needed.
730 red1 += red2;
731 green1 += green2;
732 blue1 += blue2;
733 }
734 if (brightness != 255) {
735 // nscale8x3_video(red1, green1, blue1, brightness);
736 nscale8x3(red1, green1, blue1, brightness);
737 }
738 return CRGB(red1, green1, blue1);
739}
740
741CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
742{
743 if ( blendType == LINEARBLEND_NOWRAP) {
744 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping
745 }
746
747 // hi4 = index >> 4;
748 uint8_t hi4 = lsrX4(index);
749 uint8_t lo4 = index & 0x0F;
750
751 CRGB entry(FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 ));
752
753
754 uint8_t red1 = entry.red;
755 uint8_t green1 = entry.green;
756 uint8_t blue1 = entry.blue;
757
758 uint8_t blend = lo4 && (blendType != NOBLEND);
759
760 if( blend ) {
761
762 if( hi4 == 15 ) {
763 entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) );
764 } else {
765 entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi4 );
766 }
767
768 uint8_t f2 = lo4 << 4;
769 uint8_t f1 = 255 - f2;
770
771 uint8_t red2 = entry.red;
772 red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
773 red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
774 red1 += red2;
775
776 uint8_t green2 = entry.green;
777 green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
778 green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
779 green1 += green2;
780
781 uint8_t blue2 = entry.blue;
782 blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
783 blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
784 blue1 += blue2;
785
786 cleanup_R1();
787 }
788
789 if( brightness != 255) {
790 if( brightness ) {
791 ++brightness; // adjust for rounding
792 // Now, since brightness is nonzero, we don't need the full scale8_video logic;
793 // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs.
794 if( red1 ) {
795 red1 = scale8_LEAVING_R1_DIRTY( red1, brightness);
796#if !(FASTLED_SCALE8_FIXED==1)
797 ++red1;
798#endif
799 }
800 if( green1 ) {
801 green1 = scale8_LEAVING_R1_DIRTY( green1, brightness);
802#if !(FASTLED_SCALE8_FIXED==1)
803 ++green1;
804#endif
805 }
806 if( blue1 ) {
807 blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness);
808#if !(FASTLED_SCALE8_FIXED==1)
809 ++blue1;
810#endif
811 }
812 cleanup_R1();
813 } else {
814 red1 = 0;
815 green1 = 0;
816 blue1 = 0;
817 }
818 }
819
820 return CRGB( red1, green1, blue1);
821}
822
823
824CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
825{
826 if ( blendType == LINEARBLEND_NOWRAP) {
827 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping
828 }
829
830 uint8_t hi5 = index;
831#if defined(__AVR__)
832 hi5 /= 2;
833 hi5 /= 2;
834 hi5 /= 2;
835#else
836 hi5 >>= 3;
837#endif
838 uint8_t lo3 = index & 0x07;
839
840 // const CRGB* entry = &(pal[0]) + hi5;
841 // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value,
842 // instead of the two byte 'int' that avr-gcc defaults to.
843 // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB;
844 uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB);
845 // We then add that to a base array pointer.
846 const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCRGB);
847
848 uint8_t red1 = entry->red;
849 uint8_t green1 = entry->green;
850 uint8_t blue1 = entry->blue;
851
852 uint8_t blend = lo3 && (blendType != NOBLEND);
853
854 if( blend ) {
855
856 if( hi5 == 31 ) {
857 entry = &(pal[0]);
858 } else {
859 ++entry;
860 }
861
862 uint8_t f2 = lo3 << 5;
863 uint8_t f1 = 255 - f2;
864
865 uint8_t red2 = entry->red;
866 red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
867 red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
868 red1 += red2;
869
870 uint8_t green2 = entry->green;
871 green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
872 green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
873 green1 += green2;
874
875 uint8_t blue2 = entry->blue;
876 blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
877 blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
878 blue1 += blue2;
879
880 cleanup_R1();
881
882 }
883
884 if( brightness != 255) {
885 if( brightness ) {
886 ++brightness; // adjust for rounding
887 // Now, since brightness is nonzero, we don't need the full scale8_video logic;
888 // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs.
889 if( red1 ) {
890 red1 = scale8_LEAVING_R1_DIRTY( red1, brightness);
891#if !(FASTLED_SCALE8_FIXED==1)
892 ++red1;
893#endif
894 }
895 if( green1 ) {
896 green1 = scale8_LEAVING_R1_DIRTY( green1, brightness);
897#if !(FASTLED_SCALE8_FIXED==1)
898 ++green1;
899#endif
900 }
901 if( blue1 ) {
902 blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness);
903#if !(FASTLED_SCALE8_FIXED==1)
904 ++blue1;
905#endif
906 }
907 cleanup_R1();
908 } else {
909 red1 = 0;
910 green1 = 0;
911 blue1 = 0;
912 }
913 }
914
915 return CRGB( red1, green1, blue1);
916}
917
918
919CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
920{
921 if ( blendType == LINEARBLEND_NOWRAP) {
922 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping
923 }
924
925 uint8_t hi5 = index;
926#if defined(__AVR__)
927 hi5 /= 2;
928 hi5 /= 2;
929 hi5 /= 2;
930#else
931 hi5 >>= 3;
932#endif
933 uint8_t lo3 = index & 0x07;
934
935 CRGB entry(FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi5));
936
937 uint8_t red1 = entry.red;
938 uint8_t green1 = entry.green;
939 uint8_t blue1 = entry.blue;
940
941 uint8_t blend = lo3 && (blendType != NOBLEND);
942
943 if( blend ) {
944
945 if( hi5 == 31 ) {
946 entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) );
947 } else {
948 entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi5 );
949 }
950
951 uint8_t f2 = lo3 << 5;
952 uint8_t f1 = 255 - f2;
953
954 uint8_t red2 = entry.red;
955 red1 = scale8_LEAVING_R1_DIRTY( red1, f1);
956 red2 = scale8_LEAVING_R1_DIRTY( red2, f2);
957 red1 += red2;
958
959 uint8_t green2 = entry.green;
960 green1 = scale8_LEAVING_R1_DIRTY( green1, f1);
961 green2 = scale8_LEAVING_R1_DIRTY( green2, f2);
962 green1 += green2;
963
964 uint8_t blue2 = entry.blue;
965 blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1);
966 blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2);
967 blue1 += blue2;
968
969 cleanup_R1();
970 }
971
972 if( brightness != 255) {
973 if( brightness ) {
974 ++brightness; // adjust for rounding
975 // Now, since brightness is nonzero, we don't need the full scale8_video logic;
976 // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs.
977 if( red1 ) {
978 red1 = scale8_LEAVING_R1_DIRTY( red1, brightness);
979#if !(FASTLED_SCALE8_FIXED==1)
980 ++red1;
981#endif
982 }
983 if( green1 ) {
984 green1 = scale8_LEAVING_R1_DIRTY( green1, brightness);
985#if !(FASTLED_SCALE8_FIXED==1)
986 ++green1;
987#endif
988 }
989 if( blue1 ) {
990 blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness);
991#if !(FASTLED_SCALE8_FIXED==1)
992 ++blue1;
993#endif
994 }
995 cleanup_R1();
996 } else {
997 red1 = 0;
998 green1 = 0;
999 blue1 = 0;
1000 }
1001 }
1002
1003 return CRGB( red1, green1, blue1);
1004}
1005
1006
1007
1008CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness, TBlendType)
1009{
1010 const CRGB* entry = &(pal[0]) + index;
1011
1012 uint8_t red = entry->red;
1013 uint8_t green = entry->green;
1014 uint8_t blue = entry->blue;
1015
1016 if( brightness != 255) {
1017 ++brightness; // adjust for rounding
1018 red = scale8_video_LEAVING_R1_DIRTY( red, brightness);
1019 green = scale8_video_LEAVING_R1_DIRTY( green, brightness);
1020 blue = scale8_video_LEAVING_R1_DIRTY( blue, brightness);
1021 cleanup_R1();
1022 }
1023
1024 return CRGB( red, green, blue);
1025}
1026
1027CRGB ColorFromPaletteExtended(const CRGBPalette256& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
1028 // Extract the eight most significant bits of the index as a palette index.
1029 uint8_t index_8bit = index >> 8;
1030 // Calculate the 8-bit offset from the palette index.
1031 uint8_t offset = index & 0xff;
1032 // Get the palette entry from the 8-bit index
1033 const CRGB* entry = &(pal[0]) + index_8bit;
1034 uint8_t red1 = entry->red;
1035 uint8_t green1 = entry->green;
1036 uint8_t blue1 = entry->blue;
1037
1038 uint8_t blend = offset && (blendType != NOBLEND);
1039 if (blend) {
1040 if (index_8bit == 255) {
1041 entry = &(pal[0]);
1042 } else {
1043 entry++;
1044 }
1045
1046 // Calculate the scaling factor and scaled values for the lower palette value.
1047 uint8_t f1 = 255 - offset;
1048 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
1049 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
1050 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
1051
1052 // Calculate the scaled values for the neighbouring palette value.
1053 uint8_t red2 = entry->red;
1054 uint8_t green2 = entry->green;
1055 uint8_t blue2 = entry->blue;
1056 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
1057 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
1058 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
1059 cleanup_R1();
1060
1061 // These sums can't overflow, so no qadd8 needed.
1062 red1 += red2;
1063 green1 += green2;
1064 blue1 += blue2;
1065 }
1066 if (brightness != 255) {
1067 // nscale8x3_video(red1, green1, blue1, brightness);
1068 nscale8x3(red1, green1, blue1, brightness);
1069 }
1070 return CRGB(red1, green1, blue1);
1071}
1072
1073
1074
1075CHSV ColorFromPalette( const CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
1076{
1077 if ( blendType == LINEARBLEND_NOWRAP) {
1078 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of values, remap to avoid wrapping
1079 }
1080
1081 // hi4 = index >> 4;
1082 uint8_t hi4 = lsrX4(index);
1083 uint8_t lo4 = index & 0x0F;
1084
1085 // CRGB rgb1 = pal[ hi4];
1086 const CHSV* entry = &(pal[0]) + hi4;
1087
1088 uint8_t hue1 = entry->hue;
1089 uint8_t sat1 = entry->sat;
1090 uint8_t val1 = entry->val;
1091
1092 uint8_t blend = lo4 && (blendType != NOBLEND);
1093
1094 if( blend ) {
1095
1096 if( hi4 == 15 ) {
1097 entry = &(pal[0]);
1098 } else {
1099 ++entry;
1100 }
1101
1102 uint8_t f2 = lo4 << 4;
1103 uint8_t f1 = 255 - f2;
1104
1105 uint8_t hue2 = entry->hue;
1106 uint8_t sat2 = entry->sat;
1107 uint8_t val2 = entry->val;
1108
1109 // Now some special casing for blending to or from
1110 // either black or white. Black and white don't have
1111 // proper 'hue' of their own, so when ramping from
1112 // something else to/from black/white, we set the 'hue'
1113 // of the black/white color to be the same as the hue
1114 // of the other color, so that you get the expected
1115 // brightness or saturation ramp, with hue staying
1116 // constant:
1117
1118 // If we are starting from white (sat=0)
1119 // or black (val=0), adopt the target hue.
1120 if( sat1 == 0 || val1 == 0) {
1121 hue1 = hue2;
1122 }
1123
1124 // If we are ending at white (sat=0)
1125 // or black (val=0), adopt the starting hue.
1126 if( sat2 == 0 || val2 == 0) {
1127 hue2 = hue1;
1128 }
1129
1130
1131 sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1);
1132 val1 = scale8_LEAVING_R1_DIRTY( val1, f1);
1133
1134 sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2);
1135 val2 = scale8_LEAVING_R1_DIRTY( val2, f2);
1136
1137 // cleanup_R1();
1138
1139 // These sums can't overflow, so no qadd8 needed.
1140 sat1 += sat2;
1141 val1 += val2;
1142
1143 uint8_t deltaHue = (uint8_t)(hue2 - hue1);
1144 if( deltaHue & 0x80 ) {
1145 // go backwards
1146 hue1 -= scale8( 256 - deltaHue, f2);
1147 } else {
1148 // go forwards
1149 hue1 += scale8( deltaHue, f2);
1150 }
1151
1152 cleanup_R1();
1153 }
1154
1155 if( brightness != 255) {
1156 val1 = scale8_video( val1, brightness);
1157 }
1158
1159 return CHSV( hue1, sat1, val1);
1160}
1161
1162
1163CHSV ColorFromPalette( const CHSVPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType)
1164{
1165 if ( blendType == LINEARBLEND_NOWRAP) {
1166 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of values, remap to avoid wrapping
1167 }
1168
1169 uint8_t hi5 = index;
1170#if defined(__AVR__)
1171 hi5 /= 2;
1172 hi5 /= 2;
1173 hi5 /= 2;
1174#else
1175 hi5 >>= 3;
1176#endif
1177 uint8_t lo3 = index & 0x07;
1178
1179 uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV);
1180 const CHSV* entry = (CHSV*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCHSV);
1181
1182 uint8_t hue1 = entry->hue;
1183 uint8_t sat1 = entry->sat;
1184 uint8_t val1 = entry->val;
1185
1186 uint8_t blend = lo3 && (blendType != NOBLEND);
1187
1188 if( blend ) {
1189
1190 if( hi5 == 31 ) {
1191 entry = &(pal[0]);
1192 } else {
1193 ++entry;
1194 }
1195
1196 uint8_t f2 = lo3 << 5;
1197 uint8_t f1 = 255 - f2;
1198
1199 uint8_t hue2 = entry->hue;
1200 uint8_t sat2 = entry->sat;
1201 uint8_t val2 = entry->val;
1202
1203 // Now some special casing for blending to or from
1204 // either black or white. Black and white don't have
1205 // proper 'hue' of their own, so when ramping from
1206 // something else to/from black/white, we set the 'hue'
1207 // of the black/white color to be the same as the hue
1208 // of the other color, so that you get the expected
1209 // brightness or saturation ramp, with hue staying
1210 // constant:
1211
1212 // If we are starting from white (sat=0)
1213 // or black (val=0), adopt the target hue.
1214 if( sat1 == 0 || val1 == 0) {
1215 hue1 = hue2;
1216 }
1217
1218 // If we are ending at white (sat=0)
1219 // or black (val=0), adopt the starting hue.
1220 if( sat2 == 0 || val2 == 0) {
1221 hue2 = hue1;
1222 }
1223
1224
1225 sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1);
1226 val1 = scale8_LEAVING_R1_DIRTY( val1, f1);
1227
1228 sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2);
1229 val2 = scale8_LEAVING_R1_DIRTY( val2, f2);
1230
1231 // cleanup_R1();
1232
1233 // These sums can't overflow, so no qadd8 needed.
1234 sat1 += sat2;
1235 val1 += val2;
1236
1237 uint8_t deltaHue = (uint8_t)(hue2 - hue1);
1238 if( deltaHue & 0x80 ) {
1239 // go backwards
1240 hue1 -= scale8( 256 - deltaHue, f2);
1241 } else {
1242 // go forwards
1243 hue1 += scale8( deltaHue, f2);
1244 }
1245
1246 cleanup_R1();
1247 }
1248
1249 if( brightness != 255) {
1250 val1 = scale8_video( val1, brightness);
1251 }
1252
1253 return CHSV( hue1, sat1, val1);
1254}
1255
1256CHSV ColorFromPalette( const CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType)
1257{
1258 CHSV hsv = *( &(pal[0]) + index );
1259
1260 if( brightness != 255) {
1261 hsv.value = scale8_video( hsv.value, brightness);
1262 }
1263
1264 return hsv;
1265}
1266
1267
1268void UpscalePalette(const class CRGBPalette16& srcpal16, class CRGBPalette256& destpal256)
1269{
1270 for( int i = 0; i < 256; ++i) {
1271 destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i);
1272 }
1273}
1274
1275void UpscalePalette(const class CHSVPalette16& srcpal16, class CHSVPalette256& destpal256)
1276{
1277 for( int i = 0; i < 256; ++i) {
1278 destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i);
1279 }
1280}
1281
1282
1283void UpscalePalette(const class CRGBPalette16& srcpal16, class CRGBPalette32& destpal32)
1284{
1285 for( uint8_t i = 0; i < 16; ++i) {
1286 uint8_t j = i * 2;
1287 destpal32[j+0] = srcpal16[i];
1288 destpal32[j+1] = srcpal16[i];
1289 }
1290}
1291
1292void UpscalePalette(const class CHSVPalette16& srcpal16, class CHSVPalette32& destpal32)
1293{
1294 for( uint8_t i = 0; i < 16; ++i) {
1295 uint8_t j = i * 2;
1296 destpal32[j+0] = srcpal16[i];
1297 destpal32[j+1] = srcpal16[i];
1298 }
1299}
1300
1301void UpscalePalette(const class CRGBPalette32& srcpal32, class CRGBPalette256& destpal256)
1302{
1303 for( int i = 0; i < 256; ++i) {
1304 destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i);
1305 }
1306}
1307
1308void UpscalePalette(const class CHSVPalette32& srcpal32, class CHSVPalette256& destpal256)
1309{
1310 for( int i = 0; i < 256; ++i) {
1311 destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i);
1312 }
1313}
1314
1315
1316
1317#if 0
1318// replaced by PartyColors_p
1319void SetupPartyColors(CRGBPalette16& pal)
1320{
1321 fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES);
1322 fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES);
1323}
1324#endif
1325
1326
1327void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges)
1328{
1329 uint8_t* p1;
1330 uint8_t* p2;
1331 uint8_t changes = 0;
1332
1333 p1 = (uint8_t*)current.entries;
1334 p2 = (uint8_t*)target.entries;
1335
1336 const uint8_t totalChannels = sizeof(CRGBPalette16);
1337 for( uint8_t i = 0; i < totalChannels; ++i) {
1338 // if the values are equal, no changes are needed
1339 if( p1[i] == p2[i] ) { continue; }
1340
1341 // if the current value is less than the target, increase it by one
1342 if( p1[i] < p2[i] ) { ++p1[i]; ++changes; }
1343
1344 // if the current value is greater than the target,
1345 // increase it by one (or two if it's still greater).
1346 if( p1[i] > p2[i] ) {
1347 --p1[i]; ++changes;
1348 if( p1[i] > p2[i] ) { --p1[i]; }
1349 }
1350
1351 // if we've hit the maximum number of changes, exit
1352 if( changes >= maxChanges) { break; }
1353 }
1354}
1355
1356
1357uint8_t applyGamma_video( uint8_t brightness, float gamma)
1358{
1359 float orig;
1360 float adj;
1361 orig = (float)(brightness) / (255.0);
1362 adj = pow( orig, gamma) * (255.0);
1363 uint8_t result = (uint8_t)(adj);
1364 if( (brightness > 0) && (result == 0)) {
1365 result = 1; // never gamma-adjust a positive number down to zero
1366 }
1367 return result;
1368}
1369
1370CRGB applyGamma_video( const CRGB& orig, float gamma)
1371{
1372 CRGB adj;
1373 adj.r = applyGamma_video( orig.r, gamma);
1374 adj.g = applyGamma_video( orig.g, gamma);
1375 adj.b = applyGamma_video( orig.b, gamma);
1376 return adj;
1377}
1378
1379CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB)
1380{
1381 CRGB adj;
1382 adj.r = applyGamma_video( orig.r, gammaR);
1383 adj.g = applyGamma_video( orig.g, gammaG);
1384 adj.b = applyGamma_video( orig.b, gammaB);
1385 return adj;
1386}
1387
1388CRGB& napplyGamma_video( CRGB& rgb, float gamma)
1389{
1390 rgb = applyGamma_video( rgb, gamma);
1391 return rgb;
1392}
1393
1394CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB)
1395{
1396 rgb = applyGamma_video( rgb, gammaR, gammaG, gammaB);
1397 return rgb;
1398}
1399
1400void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma)
1401{
1402 for( uint16_t i = 0; i < count; ++i) {
1403 rgbarray[i] = applyGamma_video( rgbarray[i], gamma);
1404 }
1405}
1406
1407void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB)
1408{
1409 for( uint16_t i = 0; i < count; ++i) {
1410 rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB);
1411 }
1412}
1413
1414
CFastLED FastLED
Global LED strip management instance.
Definition FastLED.cpp:45
central include file for FastLED, defines the CFastLED class/object
HSV color palette with 16 discrete values.
Definition colorutils.h:720
HSV color palette with 256 discrete values.
Definition colorutils.h:867
HSV color palette with 32 discrete values.
RGB color palette with 16 discrete values.
Definition colorutils.h:997
CRGB entries[16]
the color entries that make up the palette
Definition colorutils.h:999
RGB color palette with 256 discrete values.
RGB color palette with 32 discrete values.
uint8_t lsrX4(uint8_t dividend)
Helper function to divide a number by 16, aka four logical shift right (LSR)'s.
uint32_t TProgmemRGBPalette32[32]
CRGBPalette32 entries stored in PROGMEM memory.
Definition colorutils.h:80
uint32_t TProgmemRGBPalette16[16]
CRGBPalette16 entries stored in PROGMEM memory.
Definition colorutils.h:76
#define FL_PGM_READ_DWORD_NEAR(x)
Read a double word (32-bit) from PROGMEM memory.
CRGB & nblend(CRGB &existing, const CRGB &overlay, fract8 amountOfOverlay)
Destructively modifies one color, blending in a given fraction of an overlay color.
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
Computes a new color blended some fraction of the way between two other colors.
void blurRows(CRGB *leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap &xyMap)
Perform a blur1d() on every row of a rectangular matrix.
void blurColumns(CRGB *leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap &xyMap)
Perform a blur1d() on every column of a rectangular matrix.
void blur1d(CRGB *leds, uint16_t numLeds, fract8 blur_amount)
One-dimensional blur filter.
void blur2d(CRGB *leds, uint8_t width, uint8_t height, fract8 blur_amount, const XYMap &xymap)
Two-dimensional blur filter.
void fadeToBlackBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
Reduce the brightness of an array of pixels all at once.
void nscale8_video(CRGB *leds, uint16_t num_leds, uint8_t scale)
Scale the brightness of an array of pixels all at once.
void fade_raw(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
Reduce the brightness of an array of pixels all at once.
void fadeLightBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
Reduce the brightness of an array of pixels all at once.
void nscale8(CRGB *leds, uint16_t num_leds, uint8_t scale)
Scale the brightness of an array of pixels all at once.
void fadeUsingColor(CRGB *leds, uint16_t numLeds, const CRGB &colormask)
Reduce the brightness of an array of pixels as thought it were seen through a transparent filter with...
void fade_video(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
Reduce the brightness of an array of pixels all at once.
TGradientDirectionCode
Hue direction for calculating fill gradients.
Definition colorutils.h:157
void fill_rainbow(struct CRGB *targetArray, int numToFill, uint8_t initialhue, uint8_t deltahue)
Fill a range of LEDs with a rainbow of colors.
void fill_gradient(T *targetArray, uint16_t startpos, CHSV startcolor, uint16_t endpos, CHSV endcolor, TGradientDirectionCode directionCode=SHORTEST_HUES)
Fill a range of LEDs with a smooth HSV gradient between two HSV colors.
Definition colorutils.h:186
#define saccum87
ANSI: signed short _Accum.
Definition colorutils.h:167
void fill_gradient_RGB(CRGB *leds, uint16_t startpos, CRGB startcolor, uint16_t endpos, CRGB endcolor)
Fill a range of LEDs with a smooth RGB gradient between two RGB colors.
void fill_rainbow_circular(struct CRGB *targetArray, int numToFill, uint8_t initialhue, bool reversed)
Fill a range of LEDs with a rainbow of colors, so that the hues are continuous between the end of the...
void fill_solid(struct CRGB *targetArray, int numToFill, const struct CRGB &color)
Fill a range of LEDs with a solid color.
CRGB HeatColor(uint8_t temperature)
Approximates a "black body radiation" spectrum for a given "heat" level.
@ LONGEST_HUES
Hue goes whichever way is longest.
Definition colorutils.h:161
@ FORWARD_HUES
Hue always goes clockwise around the color wheel.
Definition colorutils.h:158
@ SHORTEST_HUES
Hue goes whichever way is shortest.
Definition colorutils.h:160
@ BACKWARD_HUES
Hue always goes counter-clockwise around the color wheel.
Definition colorutils.h:159
uint16_t accum88
ANSI: unsigned short _Accum. 8 bits int, 8 bits fraction.
Definition types.h:58
uint8_t fract8
ANSI: unsigned short _Fract.
Definition types.h:36
CRGB & napplyGamma_video(CRGB &rgb, float gamma)
Destructively applies a gamma adjustment to a color.
uint8_t applyGamma_video(uint8_t brightness, float gamma)
Applies a gamma adjustment to a color channel.
LIB8STATIC uint8_t map8(uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
Map from one full-range 8-bit value into a narrower range of 8-bit values, possibly a range of hues.
Definition lib8tion.h:559
LIB8STATIC uint8_t blend8(uint8_t a, uint8_t b, uint8_t amountOfB)
Blend a variable proportion (0-255) of one byte to another.
Definition math8.h:667
void nblendPaletteTowardPalette(CRGBPalette16 &current, CRGBPalette16 &target, uint8_t maxChanges)
Alter one palette by making it slightly more like a "target palette".
TBlendType
Color interpolation options for palette.
CRGB ColorFromPaletteExtended(const CRGBPalette32 &pal, uint16_t index, uint8_t brightness, TBlendType blendType)
Same as ColorFromPalette, but higher precision.
CRGB ColorFromPalette(const CRGBPalette16 &pal, uint8_t index, uint8_t brightness, TBlendType blendType)
Get a color from a palette.
@ NOBLEND
No interpolation between palette entries.
@ LINEARBLEND_NOWRAP
Linear interpolation between palette entries, but no wrap-around.
void UpscalePalette(const class CRGBPalette16 &srcpal16, class CRGBPalette256 &destpal256)
Convert a 16-entry palette to a 256-entry palette.
@ HUE_ORANGE
Orange (45°)
Definition chsv.h:99
@ HUE_BLUE
Blue (225°)
Definition chsv.h:103
@ HUE_YELLOW
Yellow (90°)
Definition chsv.h:100
@ HUE_PURPLE
Purple (270°)
Definition chsv.h:104
LIB8STATIC_ALWAYS_INLINE void cleanup_R1()
Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls.
Definition scale8.h:333
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY(uint8_t i, fract8 scale)
This version of scale8() does not clean up the R1 register on AVR.
Definition scale8.h:170
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY(uint8_t i, fract8 scale)
This version of scale8_video() does not clean up the R1 register on AVR.
Definition scale8.h:262
LIB8STATIC void nscale8x3(uint8_t &r, uint8_t &g, uint8_t &b, fract8 scale)
Scale three one-byte values by a fourth one, which is treated as the numerator of a fraction whose de...
Definition scale8.h:357
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video(uint8_t i, fract8 scale)
The "video" version of scale8() guarantees that the output will be only be zero if one or both of the...
Definition scale8.h:117
LIB8STATIC void nscale8x3_video(uint8_t &r, uint8_t &g, uint8_t &b, fract8 scale)
Scale three one-byte values by a fourth one, which is treated as the numerator of a fraction whose de...
Definition scale8.h:391
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:34
#define FASTLED_NAMESPACE_END
End of the FastLED namespace.
Definition namespace.h:16
#define FASTLED_NAMESPACE_BEGIN
Start of the FastLED namespace.
Definition namespace.h:14
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16
Representation of an HSV pixel (hue, saturation, value (aka brightness)).
Definition chsv.h:16
uint8_t hue
Color hue.
Definition chsv.h:23
uint8_t sat
Color saturation.
Definition chsv.h:30
uint8_t value
Color value (brightness).
Definition chsv.h:36
uint8_t val
Color value (brightness).
Definition chsv.h:37
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:54
FASTLED_FORCE_INLINE CRGB & nscale8_video(uint8_t scaledown)
Scale down a RGB to N/256ths of it's current brightness using "video" dimming rules.
Definition crgb.hpp:78
uint8_t r
Red channel value.
Definition crgb.h:58
FASTLED_FORCE_INLINE CRGB & nscale8(uint8_t scaledown)
Scale down a RGB to N/256ths of its current brightness, using "plain math" dimming rules.
Definition crgb.hpp:111
uint8_t g
Green channel value.
Definition crgb.h:62
uint8_t red
Red channel value.
Definition crgb.h:59
uint8_t blue
Blue channel value.
Definition crgb.h:67
uint8_t b
Blue channel value.
Definition crgb.h:66
uint8_t green
Green channel value.
Definition crgb.h:63
@ Black
Definition crgb.h:496