1707 lines
58 KiB
C++
1707 lines
58 KiB
C++
#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_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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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 <typename T>
|
|
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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CHSVPalette16& operator=( const CHSVPalette16& rhs)
|
|
{
|
|
memmove8( &(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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CHSVPalette256& operator=( const CHSVPalette256& rhs)
|
|
{
|
|
memmove8( &(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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette16( const CRGB rhs[16])
|
|
{
|
|
memmove8( &(entries[0]), &(rhs[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette16& operator=( const CRGBPalette16& rhs)
|
|
{
|
|
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
return *this;
|
|
}
|
|
CRGBPalette16& operator=( const CRGB rhs[16])
|
|
{
|
|
memmove8( &(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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CHSVPalette32& operator=( const CHSVPalette32& rhs)
|
|
{
|
|
memmove8( &(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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette32( const CRGB rhs[32])
|
|
{
|
|
memmove8( &(entries[0]), &(rhs[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette32& operator=( const CRGBPalette32& rhs)
|
|
{
|
|
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
return *this;
|
|
}
|
|
CRGBPalette32& operator=( const CRGB rhs[32])
|
|
{
|
|
memmove8( &(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( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette256( const CRGB rhs[256])
|
|
{
|
|
memmove8( &(entries[0]), &(rhs[0]), sizeof( entries));
|
|
}
|
|
CRGBPalette256& operator=( const CRGBPalette256& rhs)
|
|
{
|
|
memmove8( &(entries[0]), &(rhs.entries[0]), sizeof( entries));
|
|
return *this;
|
|
}
|
|
CRGBPalette256& operator=( const CRGB rhs[256])
|
|
{
|
|
memmove8( &(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 sequece of entryies from a 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)
|
|
{
|
|
uint8_t colorIndex = startIndex;
|
|
for( uint16_t i = 0; i < N; i++) {
|
|
L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType);
|
|
colorIndex += 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=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
|