GCC Code Coverage Report


Directory: ./
File: overlays/wled/colorutils.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 73 601 12.1%
Functions: 7 53 13.2%
Branches: 25 318 7.9%

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