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