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