diff options
Diffstat (limited to 'Библиотеки/FastLED-master/colorutils.cpp')
-rw-r--r-- | Библиотеки/FastLED-master/colorutils.cpp | 1197 |
1 files changed, 1197 insertions, 0 deletions
diff --git a/Библиотеки/FastLED-master/colorutils.cpp b/Библиотеки/FastLED-master/colorutils.cpp new file mode 100644 index 0000000..8df7557 --- /dev/null +++ b/Библиотеки/FastLED-master/colorutils.cpp @@ -0,0 +1,1197 @@ +#define FASTLED_INTERNAL +#define __PROG_TYPES_COMPAT__ + +#include <stdint.h> + +#include "FastLED.h" + +FASTLED_NAMESPACE_BEGIN + + + +void fill_solid( struct CRGB * leds, int numToFill, + const struct CRGB& color) +{ + for( int i = 0; i < numToFill; i++) { + leds[i] = color; + } +} + +void fill_solid( struct CHSV * targetArray, int numToFill, + const struct CHSV& hsvColor) +{ + for( int i = 0; i < numToFill; i++) { + targetArray[i] = hsvColor; + } +} + + +// void fill_solid( struct CRGB* targetArray, int numToFill, +// const struct CHSV& hsvColor) +// { +// fill_solid<CRGB>( targetArray, numToFill, (CRGB) hsvColor); +// } + +void fill_rainbow( struct CRGB * pFirstLED, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; i++) { + pFirstLED[i] = hsv; + hsv.hue += deltahue; + } +} + +void fill_rainbow( struct CHSV * targetArray, int numToFill, + uint8_t initialhue, + uint8_t deltahue ) +{ + CHSV hsv; + hsv.hue = initialhue; + hsv.val = 255; + hsv.sat = 240; + for( int i = 0; i < numToFill; i++) { + targetArray[i] = hsv; + hsv.hue += deltahue; + } +} + + +void fill_gradient_RGB( CRGB* leds, + uint16_t startpos, CRGB startcolor, + uint16_t endpos, CRGB endcolor ) +{ + // if the points are in the wrong order, straighten them + if( endpos < startpos ) { + uint16_t t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + + saccum87 rdistance87; + saccum87 gdistance87; + saccum87 bdistance87; + + rdistance87 = (endcolor.r - startcolor.r) << 7; + gdistance87 = (endcolor.g - startcolor.g) << 7; + bdistance87 = (endcolor.b - startcolor.b) << 7; + + uint16_t pixeldistance = endpos - startpos; + int16_t divisor = pixeldistance ? pixeldistance : 1; + + saccum87 rdelta87 = rdistance87 / divisor; + saccum87 gdelta87 = gdistance87 / divisor; + saccum87 bdelta87 = bdistance87 / divisor; + + rdelta87 *= 2; + gdelta87 *= 2; + bdelta87 *= 2; + + accum88 r88 = startcolor.r << 8; + accum88 g88 = startcolor.g << 8; + accum88 b88 = startcolor.b << 8; + for( uint16_t i = startpos; i <= endpos; i++) { + leds[i] = CRGB( r88 >> 8, g88 >> 8, b88 >> 8); + r88 += rdelta87; + g88 += gdelta87; + b88 += bdelta87; + } +} + +#if 0 +void fill_gradient( const CHSV& c1, const CHSV& c2) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) +{ + fill_gradient( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3); +} + +void fill_gradient_RGB( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + fill_gradient_RGB( FastLED[0].leds(), FastLED[0].size(), c1, c2, c3, c4); +} +#endif + + + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2) +{ + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, last, c2); +} + + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3) +{ + uint16_t half = (numLeds / 2); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, half, c2); + fill_gradient_RGB( leds, half, c2, last, c3); +} + +void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) +{ + uint16_t onethird = (numLeds / 3); + uint16_t twothirds = ((numLeds * 2) / 3); + uint16_t last = numLeds - 1; + fill_gradient_RGB( leds, 0, c1, onethird, c2); + fill_gradient_RGB( leds, onethird, c2, twothirds, c3); + fill_gradient_RGB( leds, twothirds, c3, last, c4); +} + + + + +void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; i++) { + leds[i].nscale8_video( scale); + } +} + +void fade_video(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + +void fadeLightBy(CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8_video( leds, num_leds, 255 - fadeBy); +} + + +void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy) +{ + nscale8( leds, num_leds, 255 - fadeBy); +} + +void nscale8_raw( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + nscale8( leds, num_leds, scale); +} + +void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale) +{ + for( uint16_t i = 0; i < num_leds; i++) { + leds[i].nscale8( scale); + } +} + +void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask) +{ + uint8_t fr, fg, fb; + fr = colormask.r; + fg = colormask.g; + fb = colormask.b; + + for( uint16_t i = 0; i < numLeds; i++) { + leds[i].r = scale8_LEAVING_R1_DIRTY( leds[i].r, fr); + leds[i].g = scale8_LEAVING_R1_DIRTY( leds[i].g, fg); + leds[i].b = scale8 ( leds[i].b, fb); + } +} + + +CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + +#if 0 + // Old blend method which unfortunately had some rounding errors + fract8 amountOfKeep = 255 - amountOfOverlay; + + existing.red = scale8_LEAVING_R1_DIRTY( existing.red, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.red, amountOfOverlay); + existing.green = scale8_LEAVING_R1_DIRTY( existing.green, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.green, amountOfOverlay); + existing.blue = scale8_LEAVING_R1_DIRTY( existing.blue, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.blue, amountOfOverlay); + + cleanup_R1(); +#else + // Corrected blend method, with no loss-of-precision rounding errors + existing.red = blend8( existing.red, overlay.red, amountOfOverlay); + existing.green = blend8( existing.green, overlay.green, amountOfOverlay); + existing.blue = blend8( existing.blue, overlay.blue, amountOfOverlay); +#endif + + return existing; +} + + + +void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay) +{ + for( uint16_t i = count; i; i--) { + nblend( *existing, *overlay, amountOfOverlay); + existing++; + overlay++; + } +} + +CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ) +{ + CRGB nu(p1); + nblend( nu, p2, amountOfP2); + return nu; +} + +CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, uint16_t count, fract8 amountOfsrc2 ) +{ + for( uint16_t i = 0; i < count; i++) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2); + } + return dest; +} + + + +CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, TGradientDirectionCode directionCode) +{ + if( amountOfOverlay == 0) { + return existing; + } + + if( amountOfOverlay == 255) { + existing = overlay; + return existing; + } + + fract8 amountOfKeep = 255 - amountOfOverlay; + + uint8_t huedelta8 = overlay.hue - existing.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) { + existing.hue = existing.hue + scale8( huedelta8, amountOfOverlay); + } + else /* directionCode == BACKWARD_HUES */ + { + huedelta8 = -huedelta8; + existing.hue = existing.hue - scale8( huedelta8, amountOfOverlay); + } + + existing.sat = scale8_LEAVING_R1_DIRTY( existing.sat, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.sat, amountOfOverlay); + existing.val = scale8_LEAVING_R1_DIRTY( existing.val, amountOfKeep) + + scale8_LEAVING_R1_DIRTY( overlay.val, amountOfOverlay); + + cleanup_R1(); + + return existing; +} + + + +void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, TGradientDirectionCode directionCode ) +{ + if(existing == overlay) return; + for( uint16_t i = count; i; i--) { + nblend( *existing, *overlay, amountOfOverlay, directionCode); + existing++; + overlay++; + } +} + +CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, TGradientDirectionCode directionCode ) +{ + CHSV nu(p1); + nblend( nu, p2, amountOfP2, directionCode); + return nu; +} + +CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, uint16_t count, fract8 amountOfsrc2, TGradientDirectionCode directionCode ) +{ + for( uint16_t i = 0; i < count; i++) { + dest[i] = blend(src1[i], src2[i], amountOfsrc2, directionCode); + } + return dest; +} + + + +// Forward declaration of the function "XY" which must be provided by +// the application for use in two-dimensional filter functions. +uint16_t XY( uint8_t, uint8_t);// __attribute__ ((weak)); + + +// 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) +{ + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + CRGB carryover = CRGB::Black; + for( uint16_t i = 0; i < numLeds; i++) { + CRGB cur = leds[i]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[i-1] += part; + leds[i] = cur; + carryover = part; + } +} + +void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount) +{ + blurRows(leds, width, height, blur_amount); + blurColumns(leds, width, height, 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) +{ + for( uint8_t row = 0; row < height; row++) { + CRGB* rowbase = leds + (row * width); + blur1d( rowbase, width, 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) +{ + // blur columns + uint8_t keep = 255 - blur_amount; + uint8_t seep = blur_amount >> 1; + for( uint8_t col = 0; col < width; col++) { + CRGB carryover = CRGB::Black; + for( uint8_t i = 0; i < height; i++) { + CRGB cur = leds[XY(col,i)]; + CRGB part = cur; + part.nscale8( seep); + cur.nscale8( keep); + cur += carryover; + if( i) leds[XY(col,i-1)] += part; + leds[XY(col,i)] = cur; + carryover = part; + } + } +} + + + +// 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. +// +// On AVR/Arduino, this typically takes around 70 bytes of program memory, +// versus 768 bytes for a full 256-entry RGB lookup table. + +CRGB HeatColor( uint8_t temperature) +{ + CRGB heatcolor; + + // Scale 'heat' down from 0-255 to 0-191, + // which can then be easily divided into three + // equal 'thirds' of 64 units each. + uint8_t t192 = scale8_video( temperature, 191); + + // calculate a value that ramps up from + // zero to 255 in each 'third' of the scale. + uint8_t heatramp = t192 & 0x3F; // 0..63 + heatramp <<= 2; // scale up to 0..252 + + // now figure out which third of the spectrum we're in: + if( t192 & 0x80) { + // we're in the hottest third + heatcolor.r = 255; // full red + heatcolor.g = 255; // full green + heatcolor.b = heatramp; // ramp up blue + + } else if( t192 & 0x40 ) { + // we're in the middle third + heatcolor.r = 255; // full red + heatcolor.g = heatramp; // ramp up green + heatcolor.b = 0; // no blue + + } else { + // we're in the coolest third + heatcolor.r = heatramp; // ramp up red + heatcolor.g = 0; // no green + heatcolor.b = 0; // no blue + } + + return heatcolor; +} + + +// lsrX4: helper function to divide a number by 16, aka four LSR's. +// On avr-gcc, "u8 >> 4" generates a loop, which is big, and slow. +// merely forcing it to be four /=2's causes avr-gcc to emit +// a SWAP instruction followed by an AND 0x0F, which is faster, and smaller. +inline uint8_t lsrX4( uint8_t dividend) __attribute__((always_inline)); +inline uint8_t lsrX4( uint8_t dividend) +{ +#if defined(__AVR__) + dividend /= 2; + dividend /= 2; + dividend /= 2; + dividend /= 2; +#else + dividend >>= 4; +#endif + return dividend; +} + + +CRGB ColorFromPalette( const CRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // const CRGB* entry = &(pal[0]) + hi4; + // since hi4 is always 0..15, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi4 X sizeof(CRGB), giving hi4XsizeofCRGB; + uint8_t hi4XsizeofCRGB = hi4 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); + + uint8_t blend = lo4 && (blendType != NOBLEND); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + // rgb1.nscale8(f1); + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + +CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi4 ); + + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi4 ); + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const CRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + // const CRGB* entry = &(pal[0]) + hi5; + // since hi5 is always 0..31, hi4 * sizeof(CRGB) can be a single-byte value, + // instead of the two byte 'int' that avr-gcc defaults to. + // So, we multiply hi5 X sizeof(CRGB), giving hi5XsizeofCRGB; + uint8_t hi5XsizeofCRGB = hi5 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCRGB); + + uint8_t red1 = entry->red; + uint8_t green1 = entry->green; + uint8_t blue1 = entry->blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry->red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry->green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry->blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + +CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + CRGB entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) + hi5); + + uint8_t red1 = entry.red; + uint8_t green1 = entry.green; + uint8_t blue1 = entry.blue; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[0]) ); + } else { + entry = FL_PGM_READ_DWORD_NEAR( &(pal[1]) + hi5 ); + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t red2 = entry.red; + red1 = scale8_LEAVING_R1_DIRTY( red1, f1); + red2 = scale8_LEAVING_R1_DIRTY( red2, f2); + red1 += red2; + + uint8_t green2 = entry.green; + green1 = scale8_LEAVING_R1_DIRTY( green1, f1); + green2 = scale8_LEAVING_R1_DIRTY( green2, f2); + green1 += green2; + + uint8_t blue2 = entry.blue; + blue1 = scale8_LEAVING_R1_DIRTY( blue1, f1); + blue2 = scale8_LEAVING_R1_DIRTY( blue2, f2); + blue1 += blue2; + + cleanup_R1(); + } + + if( brightness != 255) { + if( brightness ) { + brightness++; // adjust for rounding + // Now, since brightness is nonzero, we don't need the full scale8_video logic; + // we can just to scale8 and then add one (unless scale8 fixed) to all nonzero inputs. + if( red1 ) { + red1 = scale8_LEAVING_R1_DIRTY( red1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + red1++; +#endif + } + if( green1 ) { + green1 = scale8_LEAVING_R1_DIRTY( green1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + green1++; +#endif + } + if( blue1 ) { + blue1 = scale8_LEAVING_R1_DIRTY( blue1, brightness); +#if !(FASTLED_SCALE8_FIXED==1) + blue1++; +#endif + } + cleanup_R1(); + } else { + red1 = 0; + green1 = 0; + blue1 = 0; + } + } + + return CRGB( red1, green1, blue1); +} + + + +CRGB ColorFromPalette( const CRGBPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + const CRGB* entry = &(pal[0]) + index; + + uint8_t red = entry->red; + uint8_t green = entry->green; + uint8_t blue = entry->blue; + + if( brightness != 255) { + brightness++; // adjust for rounding + red = scale8_video_LEAVING_R1_DIRTY( red, brightness); + green = scale8_video_LEAVING_R1_DIRTY( green, brightness); + blue = scale8_video_LEAVING_R1_DIRTY( blue, brightness); + cleanup_R1(); + } + + return CRGB( red, green, blue); +} + + +CHSV ColorFromPalette( const struct CHSVPalette16& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + // hi4 = index >> 4; + uint8_t hi4 = lsrX4(index); + uint8_t lo4 = index & 0x0F; + + // CRGB rgb1 = pal[ hi4]; + const CHSV* entry = &(pal[0]) + hi4; + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo4 && (blendType != NOBLEND); + + if( blend ) { + + if( hi4 == 15 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo4 << 4; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 255 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + + +CHSV ColorFromPalette( const struct CHSVPalette32& pal, uint8_t index, uint8_t brightness, TBlendType blendType) +{ + uint8_t hi5 = index; +#if defined(__AVR__) + hi5 /= 2; + hi5 /= 2; + hi5 /= 2; +#else + hi5 >>= 3; +#endif + uint8_t lo3 = index & 0x07; + + uint8_t hi5XsizeofCHSV = hi5 * sizeof(CHSV); + const CHSV* entry = (CHSV*)( (uint8_t*)(&(pal[0])) + hi5XsizeofCHSV); + + uint8_t hue1 = entry->hue; + uint8_t sat1 = entry->sat; + uint8_t val1 = entry->val; + + uint8_t blend = lo3 && (blendType != NOBLEND); + + if( blend ) { + + if( hi5 == 31 ) { + entry = &(pal[0]); + } else { + entry++; + } + + uint8_t f2 = lo3 << 5; + uint8_t f1 = 255 - f2; + + uint8_t hue2 = entry->hue; + uint8_t sat2 = entry->sat; + uint8_t val2 = entry->val; + + // Now some special casing for blending to or from + // either black or white. Black and white don't have + // proper 'hue' of their own, so when ramping from + // something else to/from black/white, we set the 'hue' + // of the black/white color to be the same as the hue + // of the other color, so that you get the expected + // brightness or saturation ramp, with hue staying + // constant: + + // If we are starting from white (sat=0) + // or black (val=0), adopt the target hue. + if( sat1 == 0 || val1 == 0) { + hue1 = hue2; + } + + // If we are ending at white (sat=0) + // or black (val=0), adopt the starting hue. + if( sat2 == 0 || val2 == 0) { + hue2 = hue1; + } + + + sat1 = scale8_LEAVING_R1_DIRTY( sat1, f1); + val1 = scale8_LEAVING_R1_DIRTY( val1, f1); + + sat2 = scale8_LEAVING_R1_DIRTY( sat2, f2); + val2 = scale8_LEAVING_R1_DIRTY( val2, f2); + + // cleanup_R1(); + + // These sums can't overflow, so no qadd8 needed. + sat1 += sat2; + val1 += val2; + + uint8_t deltaHue = (uint8_t)(hue2 - hue1); + if( deltaHue & 0x80 ) { + // go backwards + hue1 -= scale8( 255 - deltaHue, f2); + } else { + // go forwards + hue1 += scale8( deltaHue, f2); + } + + cleanup_R1(); + } + + if( brightness != 255) { + val1 = scale8_video( val1, brightness); + } + + return CHSV( hue1, sat1, val1); +} + +CHSV ColorFromPalette( const struct CHSVPalette256& pal, uint8_t index, uint8_t brightness, TBlendType) +{ + CHSV hsv = *( &(pal[0]) + index ); + + if( brightness != 255) { + hsv.value = scale8_video( hsv.value, brightness); + } + + return hsv; +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal16, i); + } +} + + +void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32) +{ + for( uint8_t i = 0; i < 16; i++) { + uint8_t j = i * 2; + destpal32[j+0] = srcpal16[i]; + destpal32[j+1] = srcpal16[i]; + } +} + +void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + +void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256) +{ + for( int i = 0; i < 256; i++) { + destpal256[(uint8_t)(i)] = ColorFromPalette( srcpal32, i); + } +} + + + +#if 0 +// replaced by PartyColors_p +void SetupPartyColors(CRGBPalette16& pal) +{ + fill_gradient( pal, 0, CHSV( HUE_PURPLE,255,255), 7, CHSV(HUE_YELLOW - 18,255,255), FORWARD_HUES); + fill_gradient( pal, 8, CHSV( HUE_ORANGE,255,255), 15, CHSV(HUE_BLUE + 18,255,255), BACKWARD_HUES); +} +#endif + + +void nblendPaletteTowardPalette( CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges) +{ + uint8_t* p1; + uint8_t* p2; + uint8_t changes = 0; + + p1 = (uint8_t*)current.entries; + p2 = (uint8_t*)target.entries; + + const uint8_t totalChannels = sizeof(CRGBPalette16); + for( uint8_t i = 0; i < totalChannels; i++) { + // if the values are equal, no changes are needed + if( p1[i] == p2[i] ) { continue; } + + // if the current value is less than the target, increase it by one + if( p1[i] < p2[i] ) { p1[i]++; changes++; } + + // if the current value is greater than the target, + // increase it by one (or two if it's still greater). + if( p1[i] > p2[i] ) { + p1[i]--; changes++; + if( p1[i] > p2[i] ) { p1[i]--; } + } + + // if we've hit the maximum number of changes, exit + if( changes >= maxChanges) { break; } + } +} + + +uint8_t applyGamma_video( uint8_t brightness, float gamma) +{ + float orig; + float adj; + orig = (float)(brightness) / (255.0); + adj = pow( orig, gamma) * (255.0); + uint8_t result = (uint8_t)(adj); + if( (brightness > 0) && (result == 0)) { + result = 1; // never gamma-adjust a positive number down to zero + } + return result; +} + +CRGB applyGamma_video( const CRGB& orig, float gamma) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gamma); + adj.g = applyGamma_video( orig.g, gamma); + adj.b = applyGamma_video( orig.b, gamma); + return adj; +} + +CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB) +{ + CRGB adj; + adj.r = applyGamma_video( orig.r, gammaR); + adj.g = applyGamma_video( orig.g, gammaG); + adj.b = applyGamma_video( orig.b, gammaB); + return adj; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gamma) +{ + rgb = applyGamma_video( rgb, gamma); + return rgb; +} + +CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB) +{ + rgb = applyGamma_video( rgb, gammaR, gammaG, gammaB); + return rgb; +} + +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma) +{ + for( uint16_t i = 0; i < count; i++) { + rgbarray[i] = applyGamma_video( rgbarray[i], gamma); + } +} + +void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB) +{ + for( uint16_t i = 0; i < count; i++) { + rgbarray[i] = applyGamma_video( rgbarray[i], gammaR, gammaG, gammaB); + } +} + + +FASTLED_NAMESPACE_END |