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