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
7
8#include <math.h>
9#include <stdint.h>
10
11#include "FastLED.h"
12#include "fl/assert.h"
13#include "fl/colorutils.h"
14#include "fl/unused.h"
15#include "fl/xymap.h"
16
17namespace fl {
18
19CRGB &nblend(CRGB &existing, const CRGB &overlay, fract8 amountOfOverlay) {
20 if (amountOfOverlay == 0) {
21 return existing;
22 }
23
24 if (amountOfOverlay == 255) {
25 existing = overlay;
26 return existing;
27 }
28
29#if 0
30 // Old blend method which unfortunately had some rounding errors
31 fract8 amountOfKeep = 255 - amountOfOverlay;
32
33 existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep)
34 + scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay);
35 existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep)
36 + scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay);
37 existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep)
38 + scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay);
39
40 cleanup_R1();
41#else
42 // Corrected blend method, with no loss-of-precision rounding errors
43 existing.red = blend8(existing.red, overlay.red, amountOfOverlay);
44 existing.green = blend8(existing.green, overlay.green, amountOfOverlay);
45 existing.blue = blend8(existing.blue, overlay.blue, amountOfOverlay);
46#endif
47
48 return existing;
49}
50
51void nblend(CRGB *existing, const CRGB *overlay, uint16_t count,
52 fract8 amountOfOverlay) {
53 for (uint16_t i = count; i; --i) {
54 nblend(*existing, *overlay, amountOfOverlay);
55 ++existing;
56 ++overlay;
57 }
58}
59
60CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2) {
61 CRGB nu(p1);
62 nblend(nu, p2, amountOfP2);
63 return nu;
64}
65
66CRGB *blend(const CRGB *src1, const CRGB *src2, CRGB *dest, uint16_t count,
67 fract8 amountOfsrc2) {
68 for (uint16_t i = 0; i < count; ++i) {
69 dest[i] = blend(src1[i], src2[i], amountOfsrc2);
70 }
71 return dest;
72}
73
74CHSV &nblend(CHSV &existing, const CHSV &overlay, fract8 amountOfOverlay,
75 TGradientDirectionCode directionCode) {
76 if (amountOfOverlay == 0) {
77 return existing;
78 }
79
80 if (amountOfOverlay == 255) {
81 existing = overlay;
82 return existing;
83 }
84
85 fract8 amountOfKeep = 255 - amountOfOverlay;
86
87 uint8_t huedelta8 = overlay.hue - existing.hue;
88
89 if (directionCode == SHORTEST_HUES) {
90 directionCode = FORWARD_HUES;
91 if (huedelta8 > 127) {
92 directionCode = BACKWARD_HUES;
93 }
94 }
95
96 if (directionCode == LONGEST_HUES) {
97 directionCode = FORWARD_HUES;
98 if (huedelta8 < 128) {
99 directionCode = BACKWARD_HUES;
100 }
101 }
102
103 if (directionCode == FORWARD_HUES) {
104 existing.hue = existing.hue + scale8(huedelta8, amountOfOverlay);
105 } else /* directionCode == BACKWARD_HUES */
106 {
107 huedelta8 = -huedelta8;
108 existing.hue = existing.hue - scale8(huedelta8, amountOfOverlay);
109 }
110
111 existing.sat = scale8_LEAVING_R1_DIRTY(existing.sat, amountOfKeep) +
112 scale8_LEAVING_R1_DIRTY(overlay.sat, amountOfOverlay);
113 existing.val = scale8_LEAVING_R1_DIRTY(existing.val, amountOfKeep) +
114 scale8_LEAVING_R1_DIRTY(overlay.val, amountOfOverlay);
115
116 cleanup_R1();
117
118 return existing;
119}
120
121void nblend(CHSV *existing, const CHSV *overlay, uint16_t count,
122 fract8 amountOfOverlay, TGradientDirectionCode directionCode) {
123 if (existing == overlay)
124 return;
125 for (uint16_t i = count; i; --i) {
126 nblend(*existing, *overlay, amountOfOverlay, directionCode);
127 ++existing;
128 ++overlay;
129 }
130}
131
132CHSV blend(const CHSV &p1, const CHSV &p2, fract8 amountOfP2,
133 TGradientDirectionCode directionCode) {
134 CHSV nu(p1);
135 nblend(nu, p2, amountOfP2, directionCode);
136 return nu;
137}
138
139CHSV *blend(const CHSV *src1, const CHSV *src2, CHSV *dest, uint16_t count,
140 fract8 amountOfsrc2, TGradientDirectionCode directionCode) {
141 for (uint16_t i = 0; i < count; ++i) {
142 dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode);
143 }
144 return dest;
145}
146
147void nscale8_video(CRGB *leds, uint16_t num_leds, uint8_t scale) {
148 for (uint16_t i = 0; i < num_leds; ++i) {
149 leds[i].nscale8_video(scale);
150 }
151}
152
153void fade_video(CRGB *leds, uint16_t num_leds, uint8_t fadeBy) {
154 nscale8_video(leds, num_leds, 255 - fadeBy);
155}
156
157void fadeLightBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy) {
158 nscale8_video(leds, num_leds, 255 - fadeBy);
159}
160
161void fadeToBlackBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy) {
162 nscale8(leds, num_leds, 255 - fadeBy);
163}
164
165void fade_raw(CRGB *leds, uint16_t num_leds, uint8_t fadeBy) {
166 nscale8(leds, num_leds, 255 - fadeBy);
167}
168
169void nscale8(CRGB *leds, uint16_t num_leds, uint8_t scale) {
170 for (uint16_t i = 0; i < num_leds; ++i) {
171 leds[i].nscale8(scale);
172 }
173}
174
175void fadeUsingColor(CRGB *leds, uint16_t numLeds, const CRGB &colormask) {
176 uint8_t fr, fg, fb;
177 fr = colormask.r;
178 fg = colormask.g;
179 fb = colormask.b;
180
181 for (uint16_t i = 0; i < numLeds; ++i) {
182 leds[i].r = scale8_LEAVING_R1_DIRTY(leds[i].r, fr);
183 leds[i].g = scale8_LEAVING_R1_DIRTY(leds[i].g, fg);
184 leds[i].b = scale8(leds[i].b, fb);
185 }
186}
187
188// CRGB HeatColor( uint8_t temperature)
189//
190// Approximates a 'black body radiation' spectrum for
191// a given 'heat' level. This is useful for animations of 'fire'.
192// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
193// This is NOT a chromatically correct 'black body radiation'
194// spectrum, but it's surprisingly close, and it's fast and small.
195//
196// On AVR/Arduino, this typically takes around 70 bytes of program memory,
197// versus 768 bytes for a full 256-entry RGB lookup table.
198
199CRGB HeatColor(uint8_t temperature) {
200 CRGB heatcolor;
201
202 // Scale 'heat' down from 0-255 to 0-191,
203 // which can then be easily divided into three
204 // equal 'thirds' of 64 units each.
205 uint8_t t192 = scale8_video(temperature, 191);
206
207 // calculate a value that ramps up from
208 // zero to 255 in each 'third' of the scale.
209 uint8_t heatramp = t192 & 0x3F; // 0..63
210 heatramp <<= 2; // scale up to 0..252
211
212 // now figure out which third of the spectrum we're in:
213 if (t192 & 0x80) {
214 // we're in the hottest third
215 heatcolor.r = 255; // full red
216 heatcolor.g = 255; // full green
217 heatcolor.b = heatramp; // ramp up blue
218
219 } else if (t192 & 0x40) {
220 // we're in the middle third
221 heatcolor.r = 255; // full red
222 heatcolor.g = heatramp; // ramp up green
223 heatcolor.b = 0; // no blue
224
225 } else {
226 // we're in the coolest third
227 heatcolor.r = heatramp; // ramp up red
228 heatcolor.g = 0; // no green
229 heatcolor.b = 0; // no blue
230 }
231
232 return heatcolor;
233}
234
239inline uint8_t lsrX4(uint8_t dividend) __attribute__((always_inline));
240inline uint8_t lsrX4(uint8_t dividend) {
241#if defined(__AVR__)
242 dividend /= 2;
243 dividend /= 2;
244 dividend /= 2;
245 dividend /= 2;
246#else
247 dividend >>= 4;
248#endif
249 return dividend;
250}
251
252CRGB ColorFromPaletteExtended(const CRGBPalette32 &pal, uint16_t index,
253 uint8_t brightness, TBlendType blendType) {
254 // Extract the five most significant bits of the index as a palette index.
255 uint8_t index_5bit = (index >> 11);
256 // Calculate the 8-bit offset from the palette index.
257 uint8_t offset = (uint8_t)(index >> 3);
258 // Get the palette entry from the 5-bit index
259 const CRGB *entry = &(pal[0]) + index_5bit;
260 uint8_t red1 = entry->red;
261 uint8_t green1 = entry->green;
262 uint8_t blue1 = entry->blue;
263
264 uint8_t blend = offset && (blendType != NOBLEND);
265 if (blend) {
266 if (index_5bit == 31) {
267 entry = &(pal[0]);
268 } else {
269 entry++;
270 }
271
272 // Calculate the scaling factor and scaled values for the lower palette
273 // value.
274 uint8_t f1 = 255 - offset;
275 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
276 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
277 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
278
279 // Calculate the scaled values for the neighbouring palette value.
280 uint8_t red2 = entry->red;
281 uint8_t green2 = entry->green;
282 uint8_t blue2 = entry->blue;
283 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
284 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
285 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
286 cleanup_R1();
287
288 // These sums can't overflow, so no qadd8 needed.
289 red1 += red2;
290 green1 += green2;
291 blue1 += blue2;
292 }
293 if (brightness != 255) {
294 nscale8x3_video(red1, green1, blue1, brightness);
295 }
296 return CRGB(red1, green1, blue1);
297}
298
299CRGB ColorFromPalette(const CRGBPalette16 &pal, uint8_t index,
300 uint8_t brightness, TBlendType blendType) {
301 if (blendType == LINEARBLEND_NOWRAP) {
302 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of
303 // values, remap to avoid wrapping
304 }
305
306 // hi4 = index >> 4;
307 uint8_t hi4 = lsrX4(index);
308 uint8_t lo4 = index & 0x0F;
309
310 // const CRGB* entry = &(pal[0]) + hi4;
311 // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value,
312 // instead of the two byte 'int' that avr-gcc defaults to.
313 // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB;
314 uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB);
315 // We then add that to a base array pointer.
316 const CRGB *entry = (CRGB *)((uint8_t *)(&(pal[0])) + hi4XsizeofCRGB);
317
318 uint8_t blend = lo4 && (blendType != NOBLEND);
319
320 uint8_t red1 = entry->red;
321 uint8_t green1 = entry->green;
322 uint8_t blue1 = entry->blue;
323
324 if (blend) {
325
326 if (hi4 == 15) {
327 entry = &(pal[0]);
328 } else {
329 ++entry;
330 }
331
332 uint8_t f2 = lo4 << 4;
333 uint8_t f1 = 255 - f2;
334
335 // rgb1.nscale8(f1);
336 uint8_t red2 = entry->red;
337 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
338 red2 = scale8_LEAVING_R1_DIRTY(red2, f2);
339 red1 += red2;
340
341 uint8_t green2 = entry->green;
342 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
343 green2 = scale8_LEAVING_R1_DIRTY(green2, f2);
344 green1 += green2;
345
346 uint8_t blue2 = entry->blue;
347 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
348 blue2 = scale8_LEAVING_R1_DIRTY(blue2, f2);
349 blue1 += blue2;
350
351 cleanup_R1();
352 }
353
354 if (brightness != 255) {
355 if (brightness) {
356 ++brightness; // adjust for rounding
357 // Now, since brightness is nonzero, we don't need the full
358 // scale8_video logic; we can just to scale8 and then add one
359 // (unless scale8 fixed) to all nonzero inputs.
360 if (red1) {
362#if !(FASTLED_SCALE8_FIXED == 1)
363 ++red1;
364#endif
365 }
366 if (green1) {
367 green1 = scale8_LEAVING_R1_DIRTY(green1, brightness);
368#if !(FASTLED_SCALE8_FIXED == 1)
369 ++green1;
370#endif
371 }
372 if (blue1) {
373 blue1 = scale8_LEAVING_R1_DIRTY(blue1, brightness);
374#if !(FASTLED_SCALE8_FIXED == 1)
375 ++blue1;
376#endif
377 }
378 cleanup_R1();
379 } else {
380 red1 = 0;
381 green1 = 0;
382 blue1 = 0;
383 }
384 }
385
386 return CRGB(red1, green1, blue1);
387}
388
389CRGB ColorFromPaletteExtended(const CRGBPalette16 &pal, uint16_t index,
390 uint8_t brightness, TBlendType blendType) {
391 // Extract the four most significant bits of the index as a palette index.
392 uint8_t index_4bit = index >> 12;
393 // Calculate the 8-bit offset from the palette index.
394 uint8_t offset = (uint8_t)(index >> 4);
395 // Get the palette entry from the 4-bit index
396 const CRGB *entry = &(pal[0]) + index_4bit;
397 uint8_t red1 = entry->red;
398 uint8_t green1 = entry->green;
399 uint8_t blue1 = entry->blue;
400
401 uint8_t blend = offset && (blendType != NOBLEND);
402 if (blend) {
403 if (index_4bit == 15) {
404 entry = &(pal[0]);
405 } else {
406 entry++;
407 }
408
409 // Calculate the scaling factor and scaled values for the lower palette
410 // value.
411 uint8_t f1 = 255 - offset;
412 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
413 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
414 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
415
416 // Calculate the scaled values for the neighbouring palette value.
417 uint8_t red2 = entry->red;
418 uint8_t green2 = entry->green;
419 uint8_t blue2 = entry->blue;
420 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
421 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
422 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
423 cleanup_R1();
424
425 // These sums can't overflow, so no qadd8 needed.
426 red1 += red2;
427 green1 += green2;
428 blue1 += blue2;
429 }
430 if (brightness != 255) {
431 // nscale8x3_video(red1, green1, blue1, brightness);
432 nscale8x3(red1, green1, blue1, brightness);
433 }
434 return CRGB(red1, green1, blue1);
435}
436
438 uint8_t brightness, TBlendType blendType) {
439 if (blendType == LINEARBLEND_NOWRAP) {
440 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of
441 // values, remap to avoid wrapping
442 }
443
444 // hi4 = index >> 4;
445 uint8_t hi4 = lsrX4(index);
446 uint8_t lo4 = index & 0x0F;
447
448 CRGB entry(FL_PGM_READ_DWORD_NEAR(&(pal[0]) + hi4));
449
450 uint8_t red1 = entry.red;
451 uint8_t green1 = entry.green;
452 uint8_t blue1 = entry.blue;
453
454 uint8_t blend = lo4 && (blendType != NOBLEND);
455
456 if (blend) {
457
458 if (hi4 == 15) {
459 entry = FL_PGM_READ_DWORD_NEAR(&(pal[0]));
460 } else {
461 entry = FL_PGM_READ_DWORD_NEAR(&(pal[1]) + hi4);
462 }
463
464 uint8_t f2 = lo4 << 4;
465 uint8_t f1 = 255 - f2;
466
467 uint8_t red2 = entry.red;
468 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
469 red2 = scale8_LEAVING_R1_DIRTY(red2, f2);
470 red1 += red2;
471
472 uint8_t green2 = entry.green;
473 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
474 green2 = scale8_LEAVING_R1_DIRTY(green2, f2);
475 green1 += green2;
476
477 uint8_t blue2 = entry.blue;
478 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
479 blue2 = scale8_LEAVING_R1_DIRTY(blue2, f2);
480 blue1 += blue2;
481
482 cleanup_R1();
483 }
484
485 if (brightness != 255) {
486 if (brightness) {
487 ++brightness; // adjust for rounding
488 // Now, since brightness is nonzero, we don't need the full
489 // scale8_video logic; we can just to scale8 and then add one
490 // (unless scale8 fixed) to all nonzero inputs.
491 if (red1) {
493#if !(FASTLED_SCALE8_FIXED == 1)
494 ++red1;
495#endif
496 }
497 if (green1) {
498 green1 = scale8_LEAVING_R1_DIRTY(green1, brightness);
499#if !(FASTLED_SCALE8_FIXED == 1)
500 ++green1;
501#endif
502 }
503 if (blue1) {
504 blue1 = scale8_LEAVING_R1_DIRTY(blue1, brightness);
505#if !(FASTLED_SCALE8_FIXED == 1)
506 ++blue1;
507#endif
508 }
509 cleanup_R1();
510 } else {
511 red1 = 0;
512 green1 = 0;
513 blue1 = 0;
514 }
515 }
516
517 return CRGB(red1, green1, blue1);
518}
519
520CRGB ColorFromPalette(const CRGBPalette32 &pal, uint8_t index,
521 uint8_t brightness, TBlendType blendType) {
522 if (blendType == LINEARBLEND_NOWRAP) {
523 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of
524 // values, remap to avoid wrapping
525 }
526
527 uint8_t hi5 = index;
528#if defined(__AVR__)
529 hi5 /= 2;
530 hi5 /= 2;
531 hi5 /= 2;
532#else
533 hi5 >>= 3;
534#endif
535 uint8_t lo3 = index & 0x07;
536
537 // const CRGB* entry = &(pal[0]) + hi5;
538 // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value,
539 // instead of the two byte 'int' that avr-gcc defaults to.
540 // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB;
541 uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB);
542 // We then add that to a base array pointer.
543 const CRGB *entry = (CRGB *)((uint8_t *)(&(pal[0])) + hi5XsizeofCRGB);
544
545 uint8_t red1 = entry->red;
546 uint8_t green1 = entry->green;
547 uint8_t blue1 = entry->blue;
548
549 uint8_t blend = lo3 && (blendType != NOBLEND);
550
551 if (blend) {
552
553 if (hi5 == 31) {
554 entry = &(pal[0]);
555 } else {
556 ++entry;
557 }
558
559 uint8_t f2 = lo3 << 5;
560 uint8_t f1 = 255 - f2;
561
562 uint8_t red2 = entry->red;
563 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
564 red2 = scale8_LEAVING_R1_DIRTY(red2, f2);
565 red1 += red2;
566
567 uint8_t green2 = entry->green;
568 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
569 green2 = scale8_LEAVING_R1_DIRTY(green2, f2);
570 green1 += green2;
571
572 uint8_t blue2 = entry->blue;
573 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
574 blue2 = scale8_LEAVING_R1_DIRTY(blue2, f2);
575 blue1 += blue2;
576
577 cleanup_R1();
578 }
579
580 if (brightness != 255) {
581 if (brightness) {
582 ++brightness; // adjust for rounding
583 // Now, since brightness is nonzero, we don't need the full
584 // scale8_video logic; we can just to scale8 and then add one
585 // (unless scale8 fixed) to all nonzero inputs.
586 if (red1) {
588#if !(FASTLED_SCALE8_FIXED == 1)
589 ++red1;
590#endif
591 }
592 if (green1) {
593 green1 = scale8_LEAVING_R1_DIRTY(green1, brightness);
594#if !(FASTLED_SCALE8_FIXED == 1)
595 ++green1;
596#endif
597 }
598 if (blue1) {
599 blue1 = scale8_LEAVING_R1_DIRTY(blue1, brightness);
600#if !(FASTLED_SCALE8_FIXED == 1)
601 ++blue1;
602#endif
603 }
604 cleanup_R1();
605 } else {
606 red1 = 0;
607 green1 = 0;
608 blue1 = 0;
609 }
610 }
611
612 return CRGB(red1, green1, blue1);
613}
614
616 uint8_t brightness, TBlendType blendType) {
617 if (blendType == LINEARBLEND_NOWRAP) {
618 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of
619 // values, remap to avoid wrapping
620 }
621
622 uint8_t hi5 = index;
623#if defined(__AVR__)
624 hi5 /= 2;
625 hi5 /= 2;
626 hi5 /= 2;
627#else
628 hi5 >>= 3;
629#endif
630 uint8_t lo3 = index & 0x07;
631
632 CRGB entry(FL_PGM_READ_DWORD_NEAR(&(pal[0]) + hi5));
633
634 uint8_t red1 = entry.red;
635 uint8_t green1 = entry.green;
636 uint8_t blue1 = entry.blue;
637
638 uint8_t blend = lo3 && (blendType != NOBLEND);
639
640 if (blend) {
641
642 if (hi5 == 31) {
643 entry = FL_PGM_READ_DWORD_NEAR(&(pal[0]));
644 } else {
645 entry = FL_PGM_READ_DWORD_NEAR(&(pal[1]) + hi5);
646 }
647
648 uint8_t f2 = lo3 << 5;
649 uint8_t f1 = 255 - f2;
650
651 uint8_t red2 = entry.red;
652 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
653 red2 = scale8_LEAVING_R1_DIRTY(red2, f2);
654 red1 += red2;
655
656 uint8_t green2 = entry.green;
657 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
658 green2 = scale8_LEAVING_R1_DIRTY(green2, f2);
659 green1 += green2;
660
661 uint8_t blue2 = entry.blue;
662 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
663 blue2 = scale8_LEAVING_R1_DIRTY(blue2, f2);
664 blue1 += blue2;
665
666 cleanup_R1();
667 }
668
669 if (brightness != 255) {
670 if (brightness) {
671 ++brightness; // adjust for rounding
672 // Now, since brightness is nonzero, we don't need the full
673 // scale8_video logic; we can just to scale8 and then add one
674 // (unless scale8 fixed) to all nonzero inputs.
675 if (red1) {
677#if !(FASTLED_SCALE8_FIXED == 1)
678 ++red1;
679#endif
680 }
681 if (green1) {
682 green1 = scale8_LEAVING_R1_DIRTY(green1, brightness);
683#if !(FASTLED_SCALE8_FIXED == 1)
684 ++green1;
685#endif
686 }
687 if (blue1) {
688 blue1 = scale8_LEAVING_R1_DIRTY(blue1, brightness);
689#if !(FASTLED_SCALE8_FIXED == 1)
690 ++blue1;
691#endif
692 }
693 cleanup_R1();
694 } else {
695 red1 = 0;
696 green1 = 0;
697 blue1 = 0;
698 }
699 }
700
701 return CRGB(red1, green1, blue1);
702}
703
704CRGB ColorFromPalette(const CRGBPalette256 &pal, uint8_t index,
705 uint8_t brightness, TBlendType) {
706 const CRGB *entry = &(pal[0]) + index;
707
708 uint8_t red = entry->red;
709 uint8_t green = entry->green;
710 uint8_t blue = entry->blue;
711
712 if (brightness != 255) {
713 ++brightness; // adjust for rounding
717 cleanup_R1();
718 }
719
720 return CRGB(red, green, blue);
721}
722
723CRGB ColorFromPaletteExtended(const CRGBPalette256 &pal, uint16_t index,
724 uint8_t brightness, TBlendType blendType) {
725 // Extract the eight most significant bits of the index as a palette index.
726 uint8_t index_8bit = index >> 8;
727 // Calculate the 8-bit offset from the palette index.
728 uint8_t offset = index & 0xff;
729 // Get the palette entry from the 8-bit index
730 const CRGB *entry = &(pal[0]) + index_8bit;
731 uint8_t red1 = entry->red;
732 uint8_t green1 = entry->green;
733 uint8_t blue1 = entry->blue;
734
735 uint8_t blend = offset && (blendType != NOBLEND);
736 if (blend) {
737 if (index_8bit == 255) {
738 entry = &(pal[0]);
739 } else {
740 entry++;
741 }
742
743 // Calculate the scaling factor and scaled values for the lower palette
744 // value.
745 uint8_t f1 = 255 - offset;
746 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
747 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
748 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
749
750 // Calculate the scaled values for the neighbouring palette value.
751 uint8_t red2 = entry->red;
752 uint8_t green2 = entry->green;
753 uint8_t blue2 = entry->blue;
754 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
755 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
756 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
757 cleanup_R1();
758
759 // These sums can't overflow, so no qadd8 needed.
760 red1 += red2;
761 green1 += green2;
762 blue1 += blue2;
763 }
764 if (brightness != 255) {
765 // nscale8x3_video(red1, green1, blue1, brightness);
766 nscale8x3(red1, green1, blue1, brightness);
767 }
768 return CRGB(red1, green1, blue1);
769}
770
771CHSV ColorFromPalette(const CHSVPalette16 &pal, uint8_t index,
772 uint8_t brightness, TBlendType blendType) {
773 if (blendType == LINEARBLEND_NOWRAP) {
774 index = map8(index, 0, 239); // Blend range is affected by lo4 blend of
775 // values, remap to avoid wrapping
776 }
777
778 // hi4 = index >> 4;
779 uint8_t hi4 = lsrX4(index);
780 uint8_t lo4 = index & 0x0F;
781
782 // CRGB rgb1 = pal[ hi4];
783 const CHSV *entry = &(pal[0]) + hi4;
784
785 uint8_t hue1 = entry->hue;
786 uint8_t sat1 = entry->sat;
787 uint8_t val1 = entry->val;
788
789 uint8_t blend = lo4 && (blendType != NOBLEND);
790
791 if (blend) {
792
793 if (hi4 == 15) {
794 entry = &(pal[0]);
795 } else {
796 ++entry;
797 }
798
799 uint8_t f2 = lo4 << 4;
800 uint8_t f1 = 255 - f2;
801
802 uint8_t hue2 = entry->hue;
803 uint8_t sat2 = entry->sat;
804 uint8_t val2 = entry->val;
805
806 // Now some special casing for blending to or from
807 // either black or white. Black and white don't have
808 // proper 'hue' of their own, so when ramping from
809 // something else to/from black/white, we set the 'hue'
810 // of the black/white color to be the same as the hue
811 // of the other color, so that you get the expected
812 // brightness or saturation ramp, with hue staying
813 // constant:
814
815 // If we are starting from white (sat=0)
816 // or black (val=0), adopt the target hue.
817 if (sat1 == 0 || val1 == 0) {
818 hue1 = hue2;
819 }
820
821 // If we are ending at white (sat=0)
822 // or black (val=0), adopt the starting hue.
823 if (sat2 == 0 || val2 == 0) {
824 hue2 = hue1;
825 }
826
827 sat1 = scale8_LEAVING_R1_DIRTY(sat1, f1);
828 val1 = scale8_LEAVING_R1_DIRTY(val1, f1);
829
830 sat2 = scale8_LEAVING_R1_DIRTY(sat2, f2);
831 val2 = scale8_LEAVING_R1_DIRTY(val2, f2);
832
833 // cleanup_R1();
834
835 // These sums can't overflow, so no qadd8 needed.
836 sat1 += sat2;
837 val1 += val2;
838
839 uint8_t deltaHue = (uint8_t)(hue2 - hue1);
840 if (deltaHue & 0x80) {
841 // go backwards
842 hue1 -= scale8(256 - deltaHue, f2);
843 } else {
844 // go forwards
845 hue1 += scale8(deltaHue, f2);
846 }
847
848 cleanup_R1();
849 }
850
851 if (brightness != 255) {
852 val1 = scale8_video(val1, brightness);
853 }
854
855 return CHSV(hue1, sat1, val1);
856}
857
858CHSV ColorFromPalette(const CHSVPalette32 &pal, uint8_t index,
859 uint8_t brightness, TBlendType blendType) {
860 if (blendType == LINEARBLEND_NOWRAP) {
861 index = map8(index, 0, 247); // Blend range is affected by lo3 blend of
862 // values, remap to avoid wrapping
863 }
864
865 uint8_t hi5 = index;
866#if defined(__AVR__)
867 hi5 /= 2;
868 hi5 /= 2;
869 hi5 /= 2;
870#else
871 hi5 >>= 3;
872#endif
873 uint8_t lo3 = index & 0x07;
874
875 uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV);
876 const CHSV *entry = (CHSV *)((uint8_t *)(&(pal[0])) + hi5XsizeofCHSV);
877
878 uint8_t hue1 = entry->hue;
879 uint8_t sat1 = entry->sat;
880 uint8_t val1 = entry->val;
881
882 uint8_t blend = lo3 && (blendType != NOBLEND);
883
884 if (blend) {
885
886 if (hi5 == 31) {
887 entry = &(pal[0]);
888 } else {
889 ++entry;
890 }
891
892 uint8_t f2 = lo3 << 5;
893 uint8_t f1 = 255 - f2;
894
895 uint8_t hue2 = entry->hue;
896 uint8_t sat2 = entry->sat;
897 uint8_t val2 = entry->val;
898
899 // Now some special casing for blending to or from
900 // either black or white. Black and white don't have
901 // proper 'hue' of their own, so when ramping from
902 // something else to/from black/white, we set the 'hue'
903 // of the black/white color to be the same as the hue
904 // of the other color, so that you get the expected
905 // brightness or saturation ramp, with hue staying
906 // constant:
907
908 // If we are starting from white (sat=0)
909 // or black (val=0), adopt the target hue.
910 if (sat1 == 0 || val1 == 0) {
911 hue1 = hue2;
912 }
913
914 // If we are ending at white (sat=0)
915 // or black (val=0), adopt the starting hue.
916 if (sat2 == 0 || val2 == 0) {
917 hue2 = hue1;
918 }
919
920 sat1 = scale8_LEAVING_R1_DIRTY(sat1, f1);
921 val1 = scale8_LEAVING_R1_DIRTY(val1, f1);
922
923 sat2 = scale8_LEAVING_R1_DIRTY(sat2, f2);
924 val2 = scale8_LEAVING_R1_DIRTY(val2, f2);
925
926 // cleanup_R1();
927
928 // These sums can't overflow, so no qadd8 needed.
929 sat1 += sat2;
930 val1 += val2;
931
932 uint8_t deltaHue = (uint8_t)(hue2 - hue1);
933 if (deltaHue & 0x80) {
934 // go backwards
935 hue1 -= scale8(256 - deltaHue, f2);
936 } else {
937 // go forwards
938 hue1 += scale8(deltaHue, f2);
939 }
940
941 cleanup_R1();
942 }
943
944 if (brightness != 255) {
945 val1 = scale8_video(val1, brightness);
946 }
947
948 return CHSV(hue1, sat1, val1);
949}
950
951CHSV ColorFromPalette(const CHSVPalette256 &pal, uint8_t index,
952 uint8_t brightness, TBlendType) {
953 CHSV hsv = *(&(pal[0]) + index);
954
955 if (brightness != 255) {
956 hsv.value = scale8_video(hsv.value, brightness);
957 }
958
959 return hsv;
960}
961
962void UpscalePalette(const class CRGBPalette16 &srcpal16,
963 class CRGBPalette256 &destpal256) {
964 for (int i = 0; i < 256; ++i) {
965 destpal256[(uint8_t)(i)] = ColorFromPalette(srcpal16, i);
966 }
967}
968
969void UpscalePalette(const class CHSVPalette16 &srcpal16,
970 class CHSVPalette256 &destpal256) {
971 for (int i = 0; i < 256; ++i) {
972 destpal256[(uint8_t)(i)] = ColorFromPalette(srcpal16, i);
973 }
974}
975
976void UpscalePalette(const class CRGBPalette16 &srcpal16,
977 class CRGBPalette32 &destpal32) {
978 for (uint8_t i = 0; i < 16; ++i) {
979 uint8_t j = i * 2;
980 destpal32[j + 0] = srcpal16[i];
981 destpal32[j + 1] = srcpal16[i];
982 }
983}
984
985void UpscalePalette(const class CHSVPalette16 &srcpal16,
986 class CHSVPalette32 &destpal32) {
987 for (uint8_t i = 0; i < 16; ++i) {
988 uint8_t j = i * 2;
989 destpal32[j + 0] = srcpal16[i];
990 destpal32[j + 1] = srcpal16[i];
991 }
992}
993
994void UpscalePalette(const class CRGBPalette32 &srcpal32,
995 class CRGBPalette256 &destpal256) {
996 for (int i = 0; i < 256; ++i) {
997 destpal256[(uint8_t)(i)] = ColorFromPalette(srcpal32, i);
998 }
999}
1000
1001void UpscalePalette(const class CHSVPalette32 &srcpal32,
1002 class CHSVPalette256 &destpal256) {
1003 for (int i = 0; i < 256; ++i) {
1004 destpal256[(uint8_t)(i)] = ColorFromPalette(srcpal32, i);
1005 }
1006}
1007
1008#if 0
1009// replaced by PartyColors_p
1010void SetupPartyColors(CRGBPalette16& pal)
1011{
1012 fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES);
1013 fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES);
1014}
1015#endif
1016
1017void nblendPaletteTowardPalette(CRGBPalette16 &current, CRGBPalette16 &target,
1018 uint8_t maxChanges) {
1019 uint8_t *p1;
1020 uint8_t *p2;
1021 uint8_t changes = 0;
1022
1023 p1 = (uint8_t *)current.entries;
1024 p2 = (uint8_t *)target.entries;
1025
1026 const uint8_t totalChannels = sizeof(CRGBPalette16);
1027 for (uint8_t i = 0; i < totalChannels; ++i) {
1028 // if the values are equal, no changes are needed
1029 if (p1[i] == p2[i]) {
1030 continue;
1031 }
1032
1033 // if the current value is less than the target, increase it by one
1034 if (p1[i] < p2[i]) {
1035 ++p1[i];
1036 ++changes;
1037 }
1038
1039 // if the current value is greater than the target,
1040 // increase it by one (or two if it's still greater).
1041 if (p1[i] > p2[i]) {
1042 --p1[i];
1043 ++changes;
1044 if (p1[i] > p2[i]) {
1045 --p1[i];
1046 }
1047 }
1048
1049 // if we've hit the maximum number of changes, exit
1050 if (changes >= maxChanges) {
1051 break;
1052 }
1053 }
1054}
1055
1056uint8_t applyGamma_video(uint8_t brightness, float gamma) {
1057 float orig;
1058 float adj;
1059 orig = (float)(brightness) / (255.0);
1060 adj = pow(orig, gamma) * (255.0);
1061 uint8_t result = (uint8_t)(adj);
1062 if ((brightness > 0) && (result == 0)) {
1063 result = 1; // never gamma-adjust a positive number down to zero
1064 }
1065 return result;
1066}
1067
1068CRGB applyGamma_video(const CRGB &orig, float gamma) {
1069 CRGB adj;
1070 adj.r = applyGamma_video(orig.r, gamma);
1071 adj.g = applyGamma_video(orig.g, gamma);
1072 adj.b = applyGamma_video(orig.b, gamma);
1073 return adj;
1074}
1075
1076CRGB applyGamma_video(const CRGB &orig, float gammaR, float gammaG,
1077 float gammaB) {
1078 CRGB adj;
1079 adj.r = applyGamma_video(orig.r, gammaR);
1080 adj.g = applyGamma_video(orig.g, gammaG);
1081 adj.b = applyGamma_video(orig.b, gammaB);
1082 return adj;
1083}
1084
1085CRGB &napplyGamma_video(CRGB &rgb, float gamma) {
1086 rgb = applyGamma_video(rgb, gamma);
1087 return rgb;
1088}
1089
1090CRGB &napplyGamma_video(CRGB &rgb, float gammaR, float gammaG, float gammaB) {
1091 rgb = applyGamma_video(rgb, gammaR, gammaG, gammaB);
1092 return rgb;
1093}
1094
1095void napplyGamma_video(CRGB *rgbarray, uint16_t count, float gamma) {
1096 for (uint16_t i = 0; i < count; ++i) {
1097 rgbarray[i] = applyGamma_video(rgbarray[i], gamma);
1098 }
1099}
1100
1101void napplyGamma_video(CRGB *rgbarray, uint16_t count, float gammaR,
1102 float gammaG, float gammaB) {
1103 for (uint16_t i = 0; i < count; ++i) {
1104 rgbarray[i] = applyGamma_video(rgbarray[i], gammaR, gammaG, gammaB);
1105 }
1106}
1107
1108} // namespace fl
CRGB leds[NUM_LEDS]
Definition Apa102.ino:11
UISlider scale("Scale", 1.0f, 0.0f, 1.0f, 0.01f)
central include file for FastLED, defines the CFastLED class/object
UISlider brightness("Brightness", 255, 0, 255, 1)
uint32_t TProgmemRGBPalette32[32]
CRGBPalette32 entries stored in PROGMEM memory.
uint32_t TProgmemRGBPalette16[16]
CRGBPalette16 entries stored in PROGMEM memory.
UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
#define FL_PGM_READ_DWORD_NEAR(x)
Read a double word (32-bit) from PROGMEM memory.
Utility functions for color fill, palettes, blending, and more.
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 fill.h:77
uint8_t fract8
ANSI: unsigned short _Fract.
Definition types.h:36
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:541
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
@ 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:339
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:176
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:268
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:363
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:123
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:397
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:40
void fadeUsingColor(CRGB *leds, uint16_t numLeds, const CRGB &colormask)
TGradientDirectionCode
Hue direction for calculating fill gradients.
@ SHORTEST_HUES
Hue goes whichever way is shortest.
@ LONGEST_HUES
Hue goes whichever way is longest.
@ FORWARD_HUES
Hue always goes clockwise around the color wheel.
@ BACKWARD_HUES
Hue always goes counter-clockwise around the color wheel.
void UpscalePalette(const class CRGBPalette16 &srcpal16, class CRGBPalette256 &destpal256)
CRGB ColorFromPaletteExtended(const CRGBPalette32 &pal, uint16_t index, uint8_t brightness, TBlendType blendType)
uint8_t applyGamma_video(uint8_t brightness, float gamma)
CRGB ColorFromPalette(const CRGBPalette16 &pal, uint8_t index, uint8_t brightness, TBlendType blendType)
CRGB HeatColor(uint8_t temperature)
uint8_t lsrX4(uint8_t dividend)
Helper function to divide a number by 16, aka four logical shift right (LSR)'s.
void fadeToBlackBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
void nblendPaletteTowardPalette(CRGBPalette16 &current, CRGBPalette16 &target, uint8_t maxChanges)
CRGB & nblend(CRGB &existing, const CRGB &overlay, fract8 amountOfOverlay)
void fade_raw(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
void fadeLightBy(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
void nscale8(CRGB *leds, uint16_t num_leds, uint8_t scale)
CRGB & napplyGamma_video(CRGB &rgb, float gamma)
void fade_video(CRGB *leds, uint16_t num_leds, uint8_t fadeBy)
void nscale8_video(CRGB *leds, uint16_t num_leds, uint8_t scale)
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
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:55