FastLED 3.9.15
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>) && !defined(__AVR__)
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
26__attribute__((weak)) uint16_t XY(uint8_t x, uint8_t 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, const 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, const 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
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
CRGB leds[NUM_LEDS]
Definition Apa102.ino:11
XYMap xymap(WIDTH, HEIGHT, SERPENTINE)
CFastLED FastLED
Global LED strip management instance.
Definition FastLED.cpp:58
central include file for FastLED, defines the CFastLED class/object
uint32_t x[NUM_LAYERS]
Definition Fire2023.ino:80
uint32_t y[NUM_LAYERS]
Definition Fire2023.ino:81
XYMap xyMap(HEIGHT, WIDTH, SERPENTINE)
UISlider brightness("Brightness", 255, 0, 255, 1)
UISlider scale("Scale", 4,.1, 4,.1)
unsigned int xy(unsigned int x, unsigned int y)
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.
CRGB entries[16]
the color entries that make up the palette
RGB color palette with 16 discrete values.
RGB color palette with 256 discrete values.
RGB color palette with 32 discrete values.
static XYMap constructWithUserFunction(uint16_t width, uint16_t height, XYFunction xyFunction, uint16_t offset=0)
Definition xymap.cpp:27
uint16_t XY(uint8_t x, uint8_t y)
uint8_t lsrX4(uint8_t dividend)
Helper function to divide a number by 16, aka four logical shift right (LSR)'s.
#define assert(x)
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
@ 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
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.
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.
@ NOBLEND
No interpolation between palette entries.
@ LINEARBLEND_NOWRAP
Linear interpolation between palette entries, but no wrap-around.
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.
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
Definition namespace.h:22
uint16_t xy_legacy_wrapper(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
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
CRGB & nscale8(uint8_t scaledown)
Scale down a RGB to N/256ths of its current brightness, using "plain math" dimming rules.
Definition crgb.cpp:72
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:499
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:54
#define FASTLED_UNUSED(x)
Definition unused.h:3