diff options
Diffstat (limited to 'colorutils.h')
-rw-r--r-- | colorutils.h | 513 |
1 files changed, 437 insertions, 76 deletions
diff --git a/colorutils.h b/colorutils.h index 9034c1ee..209e6410 100644 --- a/colorutils.h +++ b/colorutils.h @@ -1,27 +1,36 @@ #ifndef __INC_COLORUTILS_H #define __INC_COLORUTILS_H -#include <avr/pgmspace.h> +///@file colorutils.h +/// functions for color fill, paletters, blending, and more #include "pixeltypes.h" +#include "fastled_progmem.h" +FASTLED_NAMESPACE_BEGIN +///@defgroup Colorutils Color utility functions +///A variety of functions for working with color, palletes, and leds +///@{ -// fill_solid - fill a range of LEDs with a solid color -// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200)); - +/// fill_solid - fill a range of LEDs with a solid color +/// Example: fill_solid( leds, NUM_LEDS, CRGB(50,0,200)); 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)); void fill_solid( struct CHSV* targetArray, int numToFill, const struct CHSV& hsvColor); - -// fill_rainbow - fill a range of LEDs with a rainbow of colors, at -// full saturation and full value (brightness) + +/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at +/// full saturation and full value (brightness) void fill_rainbow( struct CRGB * pFirstLED, int numToFill, uint8_t initialhue, uint8_t deltahue = 5); - + +/// fill_rainbow - fill a range of LEDs with a rainbow of colors, at +/// full saturation and full value (brightness) void fill_rainbow( struct CHSV * targetArray, int numToFill, uint8_t initialhue, uint8_t deltahue = 5); @@ -43,11 +52,11 @@ void fill_rainbow( struct CHSV * targetArray, int numToFill, // // fill_gradient can write the gradient colors EITHER // (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette) -// OR +// OR // (2) into an array of CHSVs (e.g. an HSV Palette). // -// In the case of writing into a CRGB array, the gradient is -// computed in HSV space, and then HSV values are converted to RGB +// In the case of writing into a CRGB array, the gradient is +// computed in HSV space, and then HSV values are converted to RGB // as they're written into the RGB array. typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradientDirectionCode; @@ -56,6 +65,30 @@ typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradi #define saccum87 int16_t +/// fill_gradient - fill an array of colors with a smooth HSV gradient +/// between two specified HSV colors. +/// Since 'hue' is a value around a color wheel, +/// there are always two ways to sweep from one hue +/// to another. +/// This function lets you specify which way you want +/// the hue gradient to sweep around the color wheel: +/// +/// FORWARD_HUES: hue always goes clockwise +/// BACKWARD_HUES: hue always goes counter-clockwise +/// SHORTEST_HUES: hue goes whichever way is shortest +/// LONGEST_HUES: hue goes whichever way is longest +/// +/// The default is SHORTEST_HUES, as this is nearly +/// always what is wanted. +/// +/// fill_gradient can write the gradient colors EITHER +/// (1) into an array of CRGBs (e.g., into leds[] array, or an RGB Palette) +/// OR +/// (2) into an array of CHSVs (e.g. an HSV Palette). +/// +/// In the case of writing into a CRGB array, the gradient is +/// computed in HSV space, and then HSV values are converted to RGB +/// as they're written into the RGB array. template <typename T> void fill_gradient( T* targetArray, uint16_t startpos, CHSV startcolor, @@ -66,12 +99,12 @@ void fill_gradient( T* targetArray, if( endpos < startpos ) { uint16_t t = endpos; CHSV tc = endcolor; - startpos = t; - startcolor = tc; endcolor = startcolor; endpos = startpos; + startpos = t; + startcolor = tc; } - + // If we're fading toward black (val=0) or white (sat=0), // then set the endhue to the starthue. // This lets us ramp smoothly to black or white, regardless @@ -79,7 +112,7 @@ void fill_gradient( T* targetArray, if( endcolor.value == 0 || endcolor.saturation == 0) { endcolor.hue = startcolor.hue; } - + // Similarly, if we're fading in from black (val=0) or white (sat=0) // then set the starthue to the endhue. // This lets us ramp smoothly up from black or white, regardless @@ -87,30 +120,30 @@ void fill_gradient( T* targetArray, if( startcolor.value == 0 || startcolor.saturation == 0) { startcolor.hue = endcolor.hue; } - + saccum87 huedistance87; saccum87 satdistance87; saccum87 valdistance87; - + satdistance87 = (endcolor.sat - startcolor.sat) << 7; valdistance87 = (endcolor.val - startcolor.val) << 7; - + uint8_t huedelta8 = endcolor.hue - startcolor.hue; - + if( directionCode == SHORTEST_HUES ) { directionCode = FORWARD_HUES; if( huedelta8 > 127) { directionCode = BACKWARD_HUES; } } - + if( directionCode == LONGEST_HUES ) { directionCode = FORWARD_HUES; if( huedelta8 < 128) { directionCode = BACKWARD_HUES; } } - + if( directionCode == FORWARD_HUES) { huedistance87 = huedelta8 << 7; } @@ -119,18 +152,18 @@ void fill_gradient( T* targetArray, huedistance87 = (uint8_t)(256 - huedelta8) << 7; huedistance87 = -huedistance87; } - + uint16_t pixeldistance = endpos - startpos; int16_t divisor = pixeldistance ? pixeldistance : 1; - + saccum87 huedelta87 = huedistance87 / divisor; saccum87 satdelta87 = satdistance87 / divisor; saccum87 valdelta87 = valdistance87 / divisor; - + huedelta87 *= 2; satdelta87 *= 2; valdelta87 *= 2; - + accum88 hue88 = startcolor.hue << 8; accum88 sat88 = startcolor.sat << 8; accum88 val88 = startcolor.val << 8; @@ -146,7 +179,7 @@ void fill_gradient( T* targetArray, // Convenience functions to fill an array of colors with a // two-color, three-color, or four-color gradient template <typename T> -void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, +void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, TGradientDirectionCode directionCode = SHORTEST_HUES ) { uint16_t last = numLeds - 1; @@ -154,8 +187,8 @@ void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV } template <typename T> -void fill_gradient( T* targetArray, uint16_t numLeds, - const CHSV& c1, const CHSV& c2, const CHSV& c3, +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, TGradientDirectionCode directionCode = SHORTEST_HUES ) { uint16_t half = (numLeds / 2); @@ -165,8 +198,8 @@ void fill_gradient( T* targetArray, uint16_t numLeds, } template <typename T> -void fill_gradient( T* targetArray, uint16_t numLeds, - const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4, +void fill_gradient( T* targetArray, uint16_t numLeds, + const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4, TGradientDirectionCode directionCode = SHORTEST_HUES ) { uint16_t onethird = (numLeds / 3); @@ -219,6 +252,19 @@ void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); // way down to black even if 'scale' is not zero. void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale); +// fadeUsingColor - scale down the brightness of an array of pixels, +// as though it were seen through a transparent +// filter with the specified color. +// For example, if the colormask is +// CRGB( 200, 100, 50) +// then the pixels' red will be faded to 200/256ths, +// their green to 100/256ths, and their blue to 50/256ths. +// This particular example give a 'hot fade' look, +// with white fading to yellow, then red, then black. +// You can also use colormasks like CRGB::Blue to +// zero out the red and green elements, leaving blue +// (largely) the same. +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask); // Pixel blending @@ -256,6 +302,27 @@ void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOver TGradientDirectionCode directionCode = SHORTEST_HUES); +// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. +// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. +// +// 0 = no spread at all +// 64 = moderate spreading +// 172 = maximum smooth, even spreading +// +// 173..255 = wider spreading, but increasing flicker +// +// Total light is NOT entirely conserved, so many repeated +// calls to 'blur' will also result in the light fading, +// eventually all the way to black; this is by design so that +// it can be used to (slowly) clear the LEDs to black. +void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount); +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + +// blurRows: perform a blur1d on every row of a rectangular matrix +void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); +// blurColumns: perform a blur1d on each column of a rectangular matrix +void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); + // CRGB HeatColor( uint8_t temperature) // @@ -329,12 +396,31 @@ class CHSVPalette16; class CHSVPalette256; typedef uint32_t TProgmemRGBPalette16[16]; typedef uint32_t TProgmemHSVPalette16[16]; -#define TProgmemPalette16 TProgmemRGBPalette16 +#define TProgmemPalette16 TProgmemRGBPalette16 + +typedef const uint8_t TProgmemRGBGradientPalette_byte ; +typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; +typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr; +typedef union { + struct { + uint8_t index; + uint8_t r; + uint8_t g; + uint8_t b; + }; + uint32_t dword; + uint8_t bytes[4]; +} TRGBGradientPaletteEntryUnion; + +typedef uint8_t TDynamicRGBGradientPalette_byte ; +typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes; +typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; // Convert a 16-entry palette to a 256-entry palette void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256); void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256); + class CHSVPalette16 { public: CHSV entries[16]; @@ -349,7 +435,7 @@ public: entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; }; - + CHSVPalette16( const CHSVPalette16& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); @@ -359,11 +445,11 @@ public: memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } - + CHSVPalette16( const TProgmemHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { - CRGB xyz = pgm_read_dword_near( rhs + i); + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; entries[i].val = xyz.blue; @@ -372,14 +458,14 @@ public: CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { - CRGB xyz = pgm_read_dword_near( rhs + i); + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); entries[i].hue = xyz.red; entries[i].sat = xyz.green; entries[i].val = xyz.blue; } return *this; } - + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; @@ -388,7 +474,7 @@ public: { return entries[x]; } - + inline CHSV& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; @@ -397,12 +483,12 @@ public: { return entries[(uint8_t)x]; } - + operator CHSV*() { return &(entries[0]); } - + CHSVPalette16( const CHSV& c1) { fill_solid( &(entries[0]), 16, c1); @@ -419,7 +505,7 @@ public: { fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); } - + }; class CHSVPalette256 { @@ -435,7 +521,7 @@ public: c08,c09,c10,c11,c12,c13,c14,c15); *this = p16; }; - + CHSVPalette256( const CHSVPalette256& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); @@ -445,7 +531,7 @@ public: memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } - + CHSVPalette256( const CHSVPalette16& rhs16) { UpscalePalette( rhs16, *this); @@ -455,7 +541,7 @@ public: UpscalePalette( rhs16, *this); return *this; } - + CHSVPalette256( const TProgmemRGBPalette16& rhs) { CHSVPalette16 p16(rhs); @@ -467,7 +553,7 @@ public: *this = p16; return *this; } - + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; @@ -476,7 +562,7 @@ public: { return entries[x]; } - + inline CHSV& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; @@ -490,7 +576,7 @@ public: { return &(entries[0]); } - + CHSVPalette256( const CHSV& c1) { fill_solid( &(entries[0]), 256, c1); @@ -523,7 +609,7 @@ public: entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; }; - + CRGBPalette16( const CRGBPalette16& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); @@ -533,13 +619,13 @@ public: memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } - + CRGBPalette16( const CHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } - } + } CRGBPalette16& operator=( const CHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { @@ -551,17 +637,17 @@ public: CRGBPalette16( const TProgmemRGBPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { - entries[i] = pgm_read_dword_near( rhs + i); + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } } CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) { for( uint8_t i = 0; i < 16; i++) { - entries[i] = pgm_read_dword_near( rhs + i); + entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } return *this; } - + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; @@ -570,7 +656,7 @@ public: { return entries[x]; } - + inline CRGB& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; @@ -579,12 +665,12 @@ public: { return entries[(uint8_t)x]; } - + operator CRGB*() { return &(entries[0]); } - + CRGBPalette16( const CHSV& c1) { fill_solid( &(entries[0]), 16, c1); @@ -601,7 +687,7 @@ public: { fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); } - + CRGBPalette16( const CRGB& c1) { fill_solid( &(entries[0]), 16, c1); @@ -618,7 +704,119 @@ public: { fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4); } - + + + // Gradient palettes are loaded into CRGB16Palettes in such a way + // that, if possible, every color represented in the gradient palette + // is also represented in the CRGBPalette16. + // For example, consider a gradient palette that is all black except + // for a single, one-element-wide (1/256th!) spike of red in the middle: + // 0, 0,0,0 + // 124, 0,0,0 + // 125, 255,0,0 // one 1/256th-palette-wide red stripe + // 126, 0,0,0 + // 255, 0,0,0 + // A naive conversion of this 256-element palette to a 16-element palette + // might accidentally completely eliminate the red spike, rendering the + // palette completely black. + // However, the conversions provided here would attempt to include a + // the red stripe in the output, more-or-less as faithfully as possible. + // So in this case, the resulting CRGBPalette16 palette would have a red + // stripe in the middle which was 1/16th of a palette wide -- the + // narrowest possible in a CRGBPalette16. + // This means that the relative width of stripes in a CRGBPalette16 + // will be, by definition, different from the widths in the gradient + // palette. This code attempts to preserve "all the colors", rather than + // the exact stripe widths at the expense of dropping some colors. + CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint8_t count = 0; + do { + u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint8_t count = 0; + do { + u = *(ent + count); + count++;; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t iend8 = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if( count < 16) { + if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if( iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } }; @@ -635,7 +833,7 @@ public: c08,c09,c10,c11,c12,c13,c14,c15); *this = p16; }; - + CRGBPalette256( const CRGBPalette256& rhs) { memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); @@ -645,7 +843,7 @@ public: memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } - + CRGBPalette256( const CHSVPalette256& rhs) { for( int i = 0; i < 256; i++) { @@ -669,7 +867,7 @@ public: UpscalePalette( rhs16, *this); return *this; } - + CRGBPalette256( const TProgmemRGBPalette16& rhs) { CRGBPalette16 p16(rhs); @@ -681,7 +879,7 @@ public: *this = p16; return *this; } - + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; @@ -690,7 +888,7 @@ public: { return entries[x]; } - + inline CRGB& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; @@ -704,7 +902,7 @@ public: { return &(entries[0]); } - + CRGBPalette256( const CHSV& c1) { fill_solid( &(entries[0]), 256, c1); @@ -721,7 +919,7 @@ public: { fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); } - + CRGBPalette256( const CRGB& c1) { fill_solid( &(entries[0]), 256, c1); @@ -738,18 +936,65 @@ public: { fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4); } - + + CRGBPalette256( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + CRGBPalette256& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + progent++; + u.dword = FL_PGM_READ_DWORD_NEAR( progent); + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + CRGBPalette256& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + while( indexstart < 255) { + ent++; + u = *ent; + int indexend = u.index; + CRGB rgbend( u.r, u.g, u.b); + fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } }; -typedef enum { NOBLEND=0, BLEND=1 } TBlendType; +typedef enum { NOBLEND=0, LINEARBLEND=1 } TBlendType; CRGB ColorFromPalette( const CRGBPalette16& pal, + uint8_t index, + uint8_t brightness=255, + TBlendType blendType=LINEARBLEND); + +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness=255, - TBlendType blendType=BLEND); + TBlendType blendType=LINEARBLEND); CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, @@ -759,7 +1004,7 @@ CRGB ColorFromPalette( const CRGBPalette256& pal, CHSV ColorFromPalette( const CHSVPalette16& pal, uint8_t index, uint8_t brightness=255, - TBlendType blendType=BLEND); + TBlendType blendType=LINEARBLEND); CHSV ColorFromPalette( const CHSVPalette256& pal, uint8_t index, @@ -768,7 +1013,7 @@ CHSV ColorFromPalette( const CHSVPalette256& pal, // Fill a range of LEDs with a sequece of entryies from a palette -template <typename PALETTE> +template <typename PALETTE> void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, const PALETTE& pal, uint8_t brightness, TBlendType blendType) { @@ -780,13 +1025,13 @@ void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, } template <typename PALETTE> -void map_data_into_colors_through_palette( - uint8_t *dataArray, uint16_t dataCount, - CRGB* targetColorArray, - const PALETTE& pal, - uint8_t brightness=255, - uint8_t opacity=255, - TBlendType blendType=BLEND) +void map_data_into_colors_through_palette( + uint8_t *dataArray, uint16_t dataCount, + CRGB* targetColorArray, + const PALETTE& pal, + uint8_t brightness=255, + uint8_t opacity=255, + TBlendType blendType=LINEARBLEND) { for( uint16_t i = 0; i < dataCount; i++) { uint8_t d = dataArray[i]; @@ -801,4 +1046,120 @@ void map_data_into_colors_through_palette( } } +// nblendPaletteTowardPalette: +// Alter one palette by making it slightly more like +// a 'target palette', used for palette cross-fades. +// +// It does this by comparing each of the R, G, and B channels +// of each entry in the current palette to the corresponding +// entry in the target palette and making small adjustments: +// If the Red channel is too low, it will be increased. +// If the Red channel is too high, it will be slightly reduced. +// ... and likewise for Green and Blue channels. +// +// Additionally, there are two significant visual improvements +// to this algorithm implemented here. First is this: +// When increasing a channel, it is stepped up by ONE. +// When decreasing a channel, it is stepped down by TWO. +// Due to the way the eye perceives light, and the way colors +// are represented in RGB, this produces a more uniform apparent +// brightness when cross-fading between most palette colors. +// +// The second visual tweak is limiting the number of changes +// that will be made to the palette at once. If all the palette +// entries are changed at once, it can give a muddled appearance. +// However, if only a few palette entries are changed at once, +// you get a visually smoother transition: in the middle of the +// cross-fade your current palette will actually contain some +// colors from the old palette, a few blended colors, and some +// colors from the new palette. +// The maximum number of possible palette changes per call +// is 48 (sixteen color entries time three channels each). +// The default 'maximim number of changes' here is 12, meaning +// that only approximately a quarter of the palette entries +// will be changed per call. +void nblendPaletteTowardPalette( CRGBPalette16& currentPalette, + CRGBPalette16& targetPalette, + uint8_t maxChanges=24); + + + + +// You can also define a static RGB palette very compactly in terms of a series +// of connected color gradients. +// For example, if you want the first 3/4ths of the palette to be a slow +// gradient ramping from black to red, and then the remaining 1/4 of the +// palette to be a quicker ramp to white, you specify just three points: the +// starting black point (at index 0), the red midpoint (at index 192), +// and the final white point (at index 255). It looks like this: +// +// index: 0 192 255 +// |----------r-r-r-rrrrrrrrRrRrRrRrRRRR-|-RRWRWWRWWW-| +// color: (0,0,0) (255,0,0) (255,255,255) +// +// Here's how you'd define that gradient palette: +// +// DEFINE_GRADIENT_PALETTE( black_to_red_to_white_p ) { +// 0, 0, 0, 0, /* at index 0, black(0,0,0) */ +// 192, 255, 0, 0, /* at index 192, red(255,0,0) */ +// 255, 255,255,255 /* at index 255, white(255,255,255) */ +// }; +// +// This format is designed for compact storage. The example palette here +// takes up just 12 bytes of PROGMEM (flash) storage, and zero bytes +// of SRAM when not currently in use. +// +// To use one of these gradient palettes, simply assign it into a +// CRGBPalette16 or a CRGBPalette256, like this: +// +// CRGBPalette16 pal = black_to_red_to_white_p; +// +// When the assignment is made, the gradients are expanded out into +// either 16 or 256 palette entries, depending on the kind of palette +// object they're assigned to. +// +// IMPORTANT NOTES & CAVEATS: +// +// - The last 'index' position MUST BE 255! Failure to end with +// index 255 will result in program hangs or crashes. +// +// - At this point, these gradient palette definitions MUST BE +// stored in PROGMEM on AVR-based Arduinos. If you use the +// DEFINE_GRADIENT_PALETTE macro, this is taken care of automatically. +// + +#define DEFINE_GRADIENT_PALETTE(X) \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM = + +#define DECLARE_GRADIENT_PALETTE(X) \ + extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM + + +// Functions to apply gamma adjustments, either: +// - a single gamma adjustment to a single scalar value, +// - a single gamma adjustment to each channel of a CRGB color, or +// - different gamma adjustments for each channel of a CRFB color. +// +// Note that the gamma is specified as a traditional floating point value +// e.g., "2.5", and as such these functions should not be called in +// your innermost pixel loops, or in animations that are extremely +// low on program storage space. Nevertheless, if you need these +// functions, here they are. +// +// Furthermore, bear in mind that CRGB leds have only eight bits +// per channel of color resolution, and that very small, subtle shadings +// may not be visible. +uint8_t applyGamma_video( uint8_t brightness, float gamma); +CRGB applyGamma_video( const CRGB& orig, float gamma); +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB); +// The "n" versions below modify their arguments in-place. +CRGB& napplyGamma_video( CRGB& rgb, float gamma); +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB); +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma); +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB); + + +FASTLED_NAMESPACE_END + +///@} #endif |