#ifndef __INC_COLORUTILS_H #define __INC_COLORUTILS_H ///@file colorutils.h /// functions for color fill, paletters, blending, and more #include "FastLED.h" #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)); 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) 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); /// fill_rainbow_circular - fill a range of LEDs with a rainbow of colors, at /// full saturation and full value (brightness), /// so that the hues are continuous between the end /// of the strip and the beginning void fill_rainbow_circular(struct CRGB* targetArray, int numToFill, uint8_t initialhue, bool reversed=false); /// fill_rainbow_circular - fill a range of LEDs with a rainbow of colors, at /// full saturation and full value (brightness), /// so that the hues are continuous between the end /// of the strip and the beginning void fill_rainbow_circular(struct CHSV* targetArray, int numToFill, uint8_t initialhue, bool reversed=false); // 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. typedef enum { FORWARD_HUES, BACKWARD_HUES, SHORTEST_HUES, LONGEST_HUES } TGradientDirectionCode; #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 void fill_gradient( T* targetArray, uint16_t startpos, CHSV startcolor, uint16_t endpos, CHSV endcolor, TGradientDirectionCode directionCode = SHORTEST_HUES ) { // if the points are in the wrong order, straighten them if( endpos < startpos ) { uint16_t t = endpos; CHSV tc = endcolor; 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 // of what 'hue' was set in the endcolor (since it doesn't matter) 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 // of what 'hue' was set in the startcolor (since it doesn't matter) 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; } else /* directionCode == BACKWARD_HUES */ { 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; for( uint16_t i = startpos; i <= endpos; ++i) { targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8); hue88 += huedelta87; sat88 += satdelta87; val88 += valdelta87; } } // Convenience functions to fill an array of colors with a // two-color, three-color, or four-color gradient template void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, TGradientDirectionCode directionCode = SHORTEST_HUES ) { uint16_t last = numLeds - 1; fill_gradient( targetArray, 0, c1, last, c2, directionCode); } template 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); uint16_t last = numLeds - 1; fill_gradient( targetArray, 0, c1, half, c2, directionCode); fill_gradient( targetArray, half, c2, last, c3, directionCode); } template 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); uint16_t twothirds = ((numLeds * 2) / 3); uint16_t last = numLeds - 1; fill_gradient( targetArray, 0, c1, onethird, c2, directionCode); fill_gradient( targetArray, onethird, c2, twothirds, c3, directionCode); fill_gradient( targetArray, twothirds, c3, last, c4, directionCode); } // convenience synonym #define fill_gradient_HSV fill_gradient // fill_gradient_RGB - fill a range of LEDs with a smooth RGB gradient // between two specified RGB colors. // Unlike HSV, there is no 'color wheel' in RGB space, // and therefore there's only one 'direction' for the // gradient to go, and no 'direction code' is needed. void fill_gradient_RGB( CRGB* leds, uint16_t startpos, CRGB startcolor, uint16_t endpos, CRGB endcolor ); void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2); void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3); void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4); // fadeLightBy and fade_video - reduce the brightness of an array // of pixels all at once. Guaranteed // to never fade all the way to black. // (The two names are synonyms.) void fadeLightBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); void fade_video( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); // nscale8_video - scale down the brightness of an array of pixels // all at once. Guaranteed to never scale a pixel // all the way down to black, unless 'scale' is zero. void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale); // fadeToBlackBy and fade_raw - reduce the brightness of an array // of pixels all at once. These // functions will eventually fade all // the way to black. // (The two names are synonyms.) void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); // nscale8 - scale down the brightness of an array of pixels // all at once. This function can scale pixels all the // 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 // // blend - computes a new color blended some fraction of the way // between two other colors. CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ); CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode = SHORTEST_HUES ); // blend - computes a new color blended array of colors, each // a given fraction of the way between corresponding // elements of two source arrays of colors. // Useful for blending palettes. CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 ); CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode = SHORTEST_HUES ); // nblend - destructively modifies one color, blending // in a given fraction of an overlay color CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ); CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode = SHORTEST_HUES ); // nblend - destructively blends a given fraction of // a new color array into an existing color array void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay); void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, 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) // // Approximates a 'black body radiation' spectrum for // a given 'heat' level. This is useful for animations of 'fire'. // Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). // This is NOT a chromatically correct 'black body radiation' // spectrum, but it's surprisingly close, and it's fast and small. CRGB HeatColor( uint8_t temperature); // Palettes // // RGB Palettes map an 8-bit value (0..255) to an RGB color. // // You can create any color palette you wish; a couple of starters // are provided: Forest, Clouds, Lava, Ocean, Rainbow, and Rainbow Stripes. // // Palettes come in the traditional 256-entry variety, which take // up 768 bytes of RAM, and lightweight 16-entry varieties. The 16-entry // variety automatically interpolates between its entries to produce // a full 256-element color map, but at a cost of only 48 bytes or RAM. // // Basic operation is like this: (example shows the 16-entry variety) // 1. Declare your palette storage: // CRGBPalette16 myPalette; // // 2. Fill myPalette with your own 16 colors, or with a preset color scheme. // You can specify your 16 colors a variety of ways: // CRGBPalette16 myPalette( // CRGB::Black, // CRGB::Black, // CRGB::Red, // CRGB::Yellow, // CRGB::Green, // CRGB::Blue, // CRGB::Purple, // CRGB::Black, // // 0x100000, // 0x200000, // 0x400000, // 0x800000, // // CHSV( 30,255,255), // CHSV( 50,255,255), // CHSV( 70,255,255), // CHSV( 90,255,255) // ); // // Or you can initiaize your palette with a preset color scheme: // myPalette = RainbowStripesColors_p; // // 3. Any time you want to set a pixel to a color from your palette, use // "ColorFromPalette(...)" as shown: // // uint8_t index = /* any value 0..255 */; // leds[i] = ColorFromPalette( myPalette, index); // // Even though your palette has only 16 explicily defined entries, you // can use an 'index' from 0..255. The 16 explicit palette entries will // be spread evenly across the 0..255 range, and the intermedate values // will be RGB-interpolated between adjacent explicit entries. // // It's easier to use than it sounds. // class CRGBPalette16; class CRGBPalette32; class CRGBPalette256; class CHSVPalette16; class CHSVPalette32; class CHSVPalette256; typedef uint32_t TProgmemRGBPalette16[16]; typedef uint32_t TProgmemHSVPalette16[16]; #define TProgmemPalette16 TProgmemRGBPalette16 typedef uint32_t TProgmemRGBPalette32[32]; typedef uint32_t TProgmemHSVPalette32[32]; #define TProgmemPalette32 TProgmemRGBPalette32 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); // Convert a 16-entry palette to a 32-entry palette void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32); void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32); // Convert a 32-entry palette to a 256-entry palette void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256); void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256); class CHSVPalette16 { public: CHSV entries[16]; CHSVPalette16() {}; CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) { entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; 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( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette16& operator=( const CHSVPalette16& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CHSVPalette16( const TProgmemHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; ++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; } } CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; ++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]; } inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CHSV& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CHSV& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CHSV*() { return &(entries[0]); } bool operator==( const CHSVPalette16 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CHSVPalette16 rhs) { return !( *this == rhs); } CHSVPalette16( const CHSV& c1) { fill_solid( &(entries[0]), 16, c1); } CHSVPalette16( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 16, c1, c2); } CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 16, c1, c2, c3); } CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); } }; class CHSVPalette256 { public: CHSV entries[256]; CHSVPalette256() {}; CHSVPalette256( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) { CHSVPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, c08,c09,c10,c11,c12,c13,c14,c15); *this = p16; }; CHSVPalette256( const CHSVPalette256& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette256& operator=( const CHSVPalette256& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CHSVPalette256( const CHSVPalette16& rhs16) { UpscalePalette( rhs16, *this); } CHSVPalette256& operator=( const CHSVPalette16& rhs16) { UpscalePalette( rhs16, *this); return *this; } CHSVPalette256( const TProgmemRGBPalette16& rhs) { CHSVPalette16 p16(rhs); *this = p16; } CHSVPalette256& operator=( const TProgmemRGBPalette16& rhs) { CHSVPalette16 p16(rhs); *this = p16; return *this; } inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; } inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CHSV& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CHSV& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CHSV*() { return &(entries[0]); } bool operator==( const CHSVPalette256 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint16_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CHSVPalette256 rhs) { return !( *this == rhs); } CHSVPalette256( const CHSV& c1) { fill_solid( &(entries[0]), 256, c1); } CHSVPalette256( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 256, c1, c2); } CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 256, c1, c2, c3); } CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); } }; class CRGBPalette16 { public: CRGB entries[16]; CRGBPalette16() {}; CRGBPalette16( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) { entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; 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( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette16( const CRGB rhs[16]) { memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette16& operator=( const CRGBPalette16& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette16& operator=( const CRGB rhs[16]) { memmove8( (void *) &(entries[0]), &(rhs[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( const CHSV rhs[16]) { for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette16& operator=( const CHSVPalette16& rhs) { for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette16& operator=( const CHSV rhs[16]) { for( uint8_t i = 0; i < 16; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette16( const TProgmemRGBPalette16& rhs) { for( uint8_t i = 0; i < 16; ++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] = FL_PGM_READ_DWORD_NEAR( rhs + i); } return *this; } bool operator==( const CRGBPalette16 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CRGBPalette16 rhs) { return !( *this == rhs); } inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; } inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CRGB& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CRGB& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CRGB*() { return &(entries[0]); } CRGBPalette16( const CHSV& c1) { fill_solid( &(entries[0]), 16, c1); } CRGBPalette16( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 16, c1, c2); } CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 16, c1, c2, c3); } CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); } CRGBPalette16( const CRGB& c1) { fill_solid( &(entries[0]), 16, c1); } CRGBPalette16( const CRGB& c1, const CRGB& c2) { fill_gradient_RGB( &(entries[0]), 16, c1, c2); } CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) { fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3); } CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) { 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 uint16_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 uint16_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; } }; class CHSVPalette32 { public: CHSV entries[32]; CHSVPalette32() {}; CHSVPalette32( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) { for( uint8_t i = 0; i < 2; ++i) { entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; } }; CHSVPalette32( const CHSVPalette32& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CHSVPalette32& operator=( const CHSVPalette32& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CHSVPalette32( const TProgmemHSVPalette32& rhs) { for( uint8_t i = 0; i < 32; ++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; } } CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) { for( uint8_t i = 0; i < 32; ++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]; } inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CHSV& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CHSV& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CHSV*() { return &(entries[0]); } bool operator==( const CHSVPalette32 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CHSVPalette32 rhs) { return !( *this == rhs); } CHSVPalette32( const CHSV& c1) { fill_solid( &(entries[0]), 32, c1); } CHSVPalette32( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 32, c1, c2); } CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 32, c1, c2, c3); } CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); } }; class CRGBPalette32 { public: CRGB entries[32]; CRGBPalette32() {}; CRGBPalette32( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) { for( uint8_t i = 0; i < 2; ++i) { entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; } }; CRGBPalette32( const CRGBPalette32& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette32( const CRGB rhs[32]) { memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette32& operator=( const CRGBPalette32& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette32& operator=( const CRGB rhs[32]) { memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); return *this; } CRGBPalette32( const CHSVPalette32& rhs) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } CRGBPalette32( const CHSV rhs[32]) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette32& operator=( const CHSVPalette32& rhs) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette32& operator=( const CHSV rhs[32]) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette32( const TProgmemRGBPalette32& rhs) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } } CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) { for( uint8_t i = 0; i < 32; ++i) { entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); } return *this; } bool operator==( const CRGBPalette32 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint8_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CRGBPalette32 rhs) { return !( *this == rhs); } inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; } inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CRGB& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CRGB& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CRGB*() { return &(entries[0]); } CRGBPalette32( const CHSV& c1) { fill_solid( &(entries[0]), 32, c1); } CRGBPalette32( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 32, c1, c2); } CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 32, c1, c2, c3); } CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); } CRGBPalette32( const CRGB& c1) { fill_solid( &(entries[0]), 32, c1); } CRGBPalette32( const CRGB& c1, const CRGB& c2) { fill_gradient_RGB( &(entries[0]), 32, c1, c2); } CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3) { fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3); } CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) { fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3, c4); } CRGBPalette32( const CRGBPalette16& rhs16) { UpscalePalette( rhs16, *this); } CRGBPalette32& operator=( const CRGBPalette16& rhs16) { UpscalePalette( rhs16, *this); return *this; } CRGBPalette32( const TProgmemRGBPalette16& rhs) { CRGBPalette16 p16(rhs); *this = p16; } CRGBPalette32& operator=( const TProgmemRGBPalette16& rhs) { CRGBPalette16 p16(rhs); *this = p16; return *this; } // 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 CRGBPalette32. // 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 CRGBPalette32 palette would have a red // stripe in the middle which was 1/16th of a palette wide -- the // narrowest possible in a CRGBPalette32. // This means that the relative width of stripes in a CRGBPalette32 // 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. CRGBPalette32( TProgmemRGBGradientPalette_bytes progpal ) { *this = progpal; } CRGBPalette32& operator=( TProgmemRGBGradientPalette_bytes progpal ) { TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); TRGBGradientPaletteEntryUnion u; // Count entries uint16_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 / 8; iend8 = indexend / 8; if( count < 16) { if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { 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; } CRGBPalette32& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) { TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); TRGBGradientPaletteEntryUnion u; // Count entries uint16_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 / 8; iend8 = indexend / 8; if( count < 16) { if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { 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; } }; class CRGBPalette256 { public: CRGB entries[256]; CRGBPalette256() {}; CRGBPalette256( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) { CRGBPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, c08,c09,c10,c11,c12,c13,c14,c15); *this = p16; }; CRGBPalette256( const CRGBPalette256& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); } CRGBPalette256( const CRGB rhs[256]) { memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); } CRGBPalette256& operator=( const CRGBPalette256& rhs) { memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); return *this; } CRGBPalette256& operator=( const CRGB rhs[256]) { memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); return *this; } CRGBPalette256( const CHSVPalette256& rhs) { for( int i = 0; i < 256; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } } CRGBPalette256( const CHSV rhs[256]) { for( int i = 0; i < 256; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } } CRGBPalette256& operator=( const CHSVPalette256& rhs) { for( int i = 0; i < 256; ++i) { entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette256& operator=( const CHSV rhs[256]) { for( int i = 0; i < 256; ++i) { entries[i] = rhs[i]; // implicit HSV-to-RGB conversion } return *this; } CRGBPalette256( const CRGBPalette16& rhs16) { UpscalePalette( rhs16, *this); } CRGBPalette256& operator=( const CRGBPalette16& rhs16) { UpscalePalette( rhs16, *this); return *this; } CRGBPalette256( const TProgmemRGBPalette16& rhs) { CRGBPalette16 p16(rhs); *this = p16; } CRGBPalette256& operator=( const TProgmemRGBPalette16& rhs) { CRGBPalette16 p16(rhs); *this = p16; return *this; } bool operator==( const CRGBPalette256 rhs) { const uint8_t* p = (const uint8_t*)(&(this->entries[0])); const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); if( p == q) return true; for( uint16_t i = 0; i < (sizeof( entries)); ++i) { if( *p != *q) return false; ++p; ++q; } return true; } bool operator!=( const CRGBPalette256 rhs) { return !( *this == rhs); } inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) { return entries[x]; } inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) { return entries[x]; } inline CRGB& operator[] (int x) __attribute__((always_inline)) { return entries[(uint8_t)x]; } inline const CRGB& operator[] (int x) const __attribute__((always_inline)) { return entries[(uint8_t)x]; } operator CRGB*() { return &(entries[0]); } CRGBPalette256( const CHSV& c1) { fill_solid( &(entries[0]), 256, c1); } CRGBPalette256( const CHSV& c1, const CHSV& c2) { fill_gradient( &(entries[0]), 256, c1, c2); } CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) { fill_gradient( &(entries[0]), 256, c1, c2, c3); } CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) { fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); } CRGBPalette256( const CRGB& c1) { fill_solid( &(entries[0]), 256, c1); } CRGBPalette256( const CRGB& c1, const CRGB& c2) { fill_gradient_RGB( &(entries[0]), 256, c1, c2); } CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3) { fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3); } CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) { 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, 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=LINEARBLEND); CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=NOBLEND ); CHSV ColorFromPalette( const CHSVPalette16& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=LINEARBLEND); CHSV ColorFromPalette( const CHSVPalette256& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=NOBLEND ); CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=LINEARBLEND); CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=LINEARBLEND); CHSV ColorFromPalette( const CHSVPalette32& pal, uint8_t index, uint8_t brightness=255, TBlendType blendType=LINEARBLEND); // Fill a range of LEDs with a sequence of entries from a palette template void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND) { uint8_t colorIndex = startIndex; for( uint16_t i = 0; i < N; ++i) { L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType); colorIndex += incIndex; } } // Fill a range of LEDs with a sequence of entries from a palette, so that // the entire palette smoothly covers the range of LEDs template void fill_palette_circular(CRGB* L, uint16_t N, uint8_t startIndex, const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND, bool reversed=false) { if (N == 0) return; // avoiding div/0 const uint16_t colorChange = 65535 / N; // color change for each LED, * 256 for precision uint16_t colorIndex = ((uint16_t) startIndex) << 8; // offset for color index, with precision (*256) for (uint16_t i = 0; i < N; ++i) { L[i] = ColorFromPalette(pal, (colorIndex >> 8), brightness, blendType); if (reversed) colorIndex -= colorChange; else colorIndex += colorChange; } } template 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]; CRGB rgb = ColorFromPalette( pal, d, brightness, blendType); if( opacity == 255 ) { targetColorArray[i] = rgb; } else { targetColorArray[i].nscale8( 256 - opacity); rgb.nscale8_video( opacity); targetColorArray[i] += rgb; } } } // 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) \ FL_ALIGN_PROGMEM \ extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM = #define DECLARE_GRADIENT_PALETTE(X) \ FL_ALIGN_PROGMEM \ 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