| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef __INC_COLORUTILS_H | ||
| 2 | #define __INC_COLORUTILS_H | ||
| 3 | |||
| 4 | /// @file colorutils.h | ||
| 5 | /// Utility functions for color fill, palettes, blending, and more | ||
| 6 | |||
| 7 | #include "FastLED.h" | ||
| 8 | #include "pixeltypes.h" | ||
| 9 | |||
| 10 | FASTLED_NAMESPACE_BEGIN | ||
| 11 | |||
| 12 | /// @defgroup ColorUtils Color Utility Functions | ||
| 13 | /// A variety of functions for working with color, palettes, and leds | ||
| 14 | /// @{ | ||
| 15 | |||
| 16 | /// @defgroup ColorFills Color Fill Functions | ||
| 17 | /// Functions for filling LED arrays with colors and gradients | ||
| 18 | /// @{ | ||
| 19 | |||
| 20 | /// Fill a range of LEDs with a solid color. | ||
| 21 | /// @param targetArray a pointer to the LED array to fill | ||
| 22 | /// @param numToFill the number of LEDs to fill in the array | ||
| 23 | /// @param color the color to fill with | ||
| 24 | void fill_solid( struct CRGB * targetArray, int numToFill, | ||
| 25 | const struct CRGB& color); | ||
| 26 | |||
| 27 | /// @copydoc fill_solid() | ||
| 28 | void fill_solid( struct CHSV* targetArray, int numToFill, | ||
| 29 | const struct CHSV& color); | ||
| 30 | |||
| 31 | |||
| 32 | /// Fill a range of LEDs with a rainbow of colors. | ||
| 33 | /// The colors making up the rainbow are at full saturation and full | ||
| 34 | /// value (brightness). | ||
| 35 | /// @param targetArray a pointer to the LED array to fill | ||
| 36 | /// @param numToFill the number of LEDs to fill in the array | ||
| 37 | /// @param initialhue the starting hue for the rainbow | ||
| 38 | /// @param deltahue how many hue values to advance for each LED | ||
| 39 | void fill_rainbow( struct CRGB * targetArray, int numToFill, | ||
| 40 | uint8_t initialhue, | ||
| 41 | uint8_t deltahue = 5); | ||
| 42 | |||
| 43 | /// @copydoc fill_rainbow() | ||
| 44 | void fill_rainbow( struct CHSV * targetArray, int numToFill, | ||
| 45 | uint8_t initialhue, | ||
| 46 | uint8_t deltahue = 5); | ||
| 47 | |||
| 48 | |||
| 49 | /// Fill a range of LEDs with a rainbow of colors, so that the hues | ||
| 50 | /// are continuous between the end of the strip and the beginning. | ||
| 51 | /// The colors making up the rainbow are at full saturation and full | ||
| 52 | /// value (brightness). | ||
| 53 | /// @param targetArray a pointer to the LED array to fill | ||
| 54 | /// @param numToFill the number of LEDs to fill in the array | ||
| 55 | /// @param initialhue the starting hue for the rainbow | ||
| 56 | /// @param reversed whether to progress through the rainbow hues backwards | ||
| 57 | void fill_rainbow_circular(struct CRGB* targetArray, int numToFill, | ||
| 58 | uint8_t initialhue, bool reversed=false); | ||
| 59 | |||
| 60 | /// @copydoc fill_rainbow_circular() | ||
| 61 | void fill_rainbow_circular(struct CHSV* targetArray, int numToFill, | ||
| 62 | uint8_t initialhue, bool reversed=false); | ||
| 63 | |||
| 64 | |||
| 65 | /// Hue direction for calculating fill gradients. | ||
| 66 | /// Since "hue" is a value around a color wheel, there are always two directions | ||
| 67 | /// to sweep from one hue to another. | ||
| 68 | typedef enum { | ||
| 69 | FORWARD_HUES, ///< Hue always goes clockwise around the color wheel | ||
| 70 | BACKWARD_HUES, ///< Hue always goes counter-clockwise around the color wheel | ||
| 71 | SHORTEST_HUES, ///< Hue goes whichever way is shortest | ||
| 72 | LONGEST_HUES ///< Hue goes whichever way is longest | ||
| 73 | } TGradientDirectionCode; | ||
| 74 | |||
| 75 | |||
| 76 | /// ANSI: signed short _Accum. 8 bits int, 7 bits fraction | ||
| 77 | /// @see accum88 | ||
| 78 | #define saccum87 int16_t | ||
| 79 | |||
| 80 | |||
| 81 | /// Fill a range of LEDs with a smooth HSV gradient between two HSV colors. | ||
| 82 | /// This function can write the gradient colors either: | ||
| 83 | /// | ||
| 84 | /// 1. Into an array of CRGBs (e.g., an leds[] array, or a CRGB palette) | ||
| 85 | /// 2. Into an array of CHSVs (e.g. a CHSV palette). | ||
| 86 | /// | ||
| 87 | /// In the case of writing into a CRGB array, the gradient is | ||
| 88 | /// computed in HSV space, and then HSV values are converted to RGB | ||
| 89 | /// as they're written into the CRGB array. | ||
| 90 | /// @param targetArray a pointer to the color array to fill | ||
| 91 | /// @param startpos the starting position in the array | ||
| 92 | /// @param startcolor the starting color for the gradient | ||
| 93 | /// @param endpos the ending position in the array | ||
| 94 | /// @param endcolor the end color for the gradient | ||
| 95 | /// @param directionCode the direction to travel around the color wheel | ||
| 96 | template <typename T> | ||
| 97 | 14091 | void fill_gradient( T* targetArray, | |
| 98 | uint16_t startpos, CHSV startcolor, | ||
| 99 | uint16_t endpos, CHSV endcolor, | ||
| 100 | TGradientDirectionCode directionCode = SHORTEST_HUES ) | ||
| 101 | { | ||
| 102 | // if the points are in the wrong order, straighten them | ||
| 103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14091 times.
|
14091 | if( endpos < startpos ) { |
| 104 | ✗ | uint16_t t = endpos; | |
| 105 | ✗ | CHSV tc = endcolor; | |
| 106 | ✗ | endcolor = startcolor; | |
| 107 | ✗ | endpos = startpos; | |
| 108 | ✗ | startpos = t; | |
| 109 | ✗ | startcolor = tc; | |
| 110 | } | ||
| 111 | |||
| 112 | // If we're fading toward black (val=0) or white (sat=0), | ||
| 113 | // then set the endhue to the starthue. | ||
| 114 | // This lets us ramp smoothly to black or white, regardless | ||
| 115 | // of what 'hue' was set in the endcolor (since it doesn't matter) | ||
| 116 |
2/4✓ Branch 0 taken 14091 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14091 times.
|
14091 | if( endcolor.value == 0 || endcolor.saturation == 0) { |
| 117 | ✗ | endcolor.hue = startcolor.hue; | |
| 118 | } | ||
| 119 | |||
| 120 | // Similarly, if we're fading in from black (val=0) or white (sat=0) | ||
| 121 | // then set the starthue to the endhue. | ||
| 122 | // This lets us ramp smoothly up from black or white, regardless | ||
| 123 | // of what 'hue' was set in the startcolor (since it doesn't matter) | ||
| 124 |
2/4✓ Branch 0 taken 14091 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14091 times.
|
14091 | if( startcolor.value == 0 || startcolor.saturation == 0) { |
| 125 | ✗ | startcolor.hue = endcolor.hue; | |
| 126 | } | ||
| 127 | |||
| 128 | saccum87 huedistance87; | ||
| 129 | saccum87 satdistance87; | ||
| 130 | saccum87 valdistance87; | ||
| 131 | |||
| 132 | 14091 | satdistance87 = (endcolor.sat - startcolor.sat) << 7; | |
| 133 | 14091 | valdistance87 = (endcolor.val - startcolor.val) << 7; | |
| 134 | |||
| 135 | 14091 | uint8_t huedelta8 = endcolor.hue - startcolor.hue; | |
| 136 | |||
| 137 |
1/2✓ Branch 0 taken 14091 times.
✗ Branch 1 not taken.
|
14091 | if( directionCode == SHORTEST_HUES ) { |
| 138 | 14091 | directionCode = FORWARD_HUES; | |
| 139 |
2/2✓ Branch 0 taken 4896 times.
✓ Branch 1 taken 9195 times.
|
14091 | if( huedelta8 > 127) { |
| 140 | 4896 | directionCode = BACKWARD_HUES; | |
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14091 times.
|
14091 | if( directionCode == LONGEST_HUES ) { |
| 145 | ✗ | directionCode = FORWARD_HUES; | |
| 146 | ✗ | if( huedelta8 < 128) { | |
| 147 | ✗ | directionCode = BACKWARD_HUES; | |
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 |
2/2✓ Branch 0 taken 9195 times.
✓ Branch 1 taken 4896 times.
|
14091 | if( directionCode == FORWARD_HUES) { |
| 152 | 9195 | huedistance87 = huedelta8 << 7; | |
| 153 | } | ||
| 154 | else /* directionCode == BACKWARD_HUES */ | ||
| 155 | { | ||
| 156 | 4896 | huedistance87 = (uint8_t)(256 - huedelta8) << 7; | |
| 157 | 4896 | huedistance87 = -huedistance87; | |
| 158 | } | ||
| 159 | |||
| 160 | 14091 | uint16_t pixeldistance = endpos - startpos; | |
| 161 |
1/2✓ Branch 0 taken 14091 times.
✗ Branch 1 not taken.
|
14091 | int16_t divisor = pixeldistance ? pixeldistance : 1; |
| 162 | |||
| 163 | 14091 | saccum87 huedelta87 = huedistance87 / divisor; | |
| 164 | 14091 | saccum87 satdelta87 = satdistance87 / divisor; | |
| 165 | 14091 | saccum87 valdelta87 = valdistance87 / divisor; | |
| 166 | |||
| 167 | 14091 | huedelta87 *= 2; | |
| 168 | 14091 | satdelta87 *= 2; | |
| 169 | 14091 | valdelta87 *= 2; | |
| 170 | |||
| 171 | 14091 | accum88 hue88 = startcolor.hue << 8; | |
| 172 | 14091 | accum88 sat88 = startcolor.sat << 8; | |
| 173 | 14091 | accum88 val88 = startcolor.val << 8; | |
| 174 |
2/2✓ Branch 0 taken 84546 times.
✓ Branch 1 taken 14091 times.
|
98637 | for( uint16_t i = startpos; i <= endpos; ++i) { |
| 175 |
1/2✓ Branch 0 taken 84546 times.
✗ Branch 1 not taken.
|
85698 | targetArray[i] = CHSV( hue88 >> 8, sat88 >> 8, val88 >> 8); |
| 176 | 84546 | hue88 += huedelta87; | |
| 177 | 84546 | sat88 += satdelta87; | |
| 178 | 84546 | val88 += valdelta87; | |
| 179 | } | ||
| 180 | 14091 | } | |
| 181 | |||
| 182 | |||
| 183 | /// Fill a range of LEDs with a smooth HSV gradient between two HSV colors. | ||
| 184 | /// @see fill_gradient() | ||
| 185 | /// @param targetArray a pointer to the color array to fill | ||
| 186 | /// @param numLeds the number of LEDs to fill | ||
| 187 | /// @param c1 the starting color in the gradient | ||
| 188 | /// @param c2 the end color for the gradient | ||
| 189 | /// @param directionCode the direction to travel around the color wheel | ||
| 190 | template <typename T> | ||
| 191 | void fill_gradient( T* targetArray, uint16_t numLeds, const CHSV& c1, const CHSV& c2, | ||
| 192 | TGradientDirectionCode directionCode = SHORTEST_HUES ) | ||
| 193 | { | ||
| 194 | uint16_t last = numLeds - 1; | ||
| 195 | fill_gradient( targetArray, 0, c1, last, c2, directionCode); | ||
| 196 | } | ||
| 197 | |||
| 198 | /// Fill a range of LEDs with a smooth HSV gradient between three HSV colors. | ||
| 199 | /// @see fill_gradient() | ||
| 200 | /// @param targetArray a pointer to the color array to fill | ||
| 201 | /// @param numLeds the number of LEDs to fill | ||
| 202 | /// @param c1 the starting color in the gradient | ||
| 203 | /// @param c2 the middle color for the gradient | ||
| 204 | /// @param c3 the end color for the gradient | ||
| 205 | /// @param directionCode the direction to travel around the color wheel | ||
| 206 | template <typename T> | ||
| 207 | void fill_gradient( T* targetArray, uint16_t numLeds, | ||
| 208 | const CHSV& c1, const CHSV& c2, const CHSV& c3, | ||
| 209 | TGradientDirectionCode directionCode = SHORTEST_HUES ) | ||
| 210 | { | ||
| 211 | uint16_t half = (numLeds / 2); | ||
| 212 | uint16_t last = numLeds - 1; | ||
| 213 | fill_gradient( targetArray, 0, c1, half, c2, directionCode); | ||
| 214 | fill_gradient( targetArray, half, c2, last, c3, directionCode); | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Fill a range of LEDs with a smooth HSV gradient between four HSV colors. | ||
| 218 | /// @see fill_gradient() | ||
| 219 | /// @param targetArray a pointer to the color array to fill | ||
| 220 | /// @param numLeds the number of LEDs to fill | ||
| 221 | /// @param c1 the starting color in the gradient | ||
| 222 | /// @param c2 the first middle color for the gradient | ||
| 223 | /// @param c3 the second middle color for the gradient | ||
| 224 | /// @param c4 the end color for the gradient | ||
| 225 | /// @param directionCode the direction to travel around the color wheel | ||
| 226 | template <typename T> | ||
| 227 | 4697 | void fill_gradient( T* targetArray, uint16_t numLeds, | |
| 228 | const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4, | ||
| 229 | TGradientDirectionCode directionCode = SHORTEST_HUES ) | ||
| 230 | { | ||
| 231 | 4697 | uint16_t onethird = (numLeds / 3); | |
| 232 | 4697 | uint16_t twothirds = ((numLeds * 2) / 3); | |
| 233 | 4697 | uint16_t last = numLeds - 1; | |
| 234 |
1/2✓ Branch 0 taken 4697 times.
✗ Branch 1 not taken.
|
4697 | fill_gradient( targetArray, 0, c1, onethird, c2, directionCode); |
| 235 |
1/2✓ Branch 0 taken 4697 times.
✗ Branch 1 not taken.
|
4697 | fill_gradient( targetArray, onethird, c2, twothirds, c3, directionCode); |
| 236 |
1/2✓ Branch 0 taken 4697 times.
✗ Branch 1 not taken.
|
4697 | fill_gradient( targetArray, twothirds, c3, last, c4, directionCode); |
| 237 | 4697 | } | |
| 238 | |||
| 239 | /// Convenience synonym | ||
| 240 | #define fill_gradient_HSV fill_gradient | ||
| 241 | |||
| 242 | |||
| 243 | /// Fill a range of LEDs with a smooth RGB gradient between two RGB colors. | ||
| 244 | /// Unlike HSV, there is no "color wheel" in RGB space, and therefore there's only one | ||
| 245 | /// "direction" for the gradient to go. This means there's no TGradientDirectionCode | ||
| 246 | /// parameter for direction. | ||
| 247 | /// @param leds a pointer to the LED array to fill | ||
| 248 | /// @param startpos the starting position in the array | ||
| 249 | /// @param startcolor the starting color for the gradient | ||
| 250 | /// @param endpos the ending position in the array | ||
| 251 | /// @param endcolor the end color for the gradient | ||
| 252 | void fill_gradient_RGB( CRGB* leds, | ||
| 253 | uint16_t startpos, CRGB startcolor, | ||
| 254 | uint16_t endpos, CRGB endcolor ); | ||
| 255 | |||
| 256 | /// Fill a range of LEDs with a smooth RGB gradient between two RGB colors. | ||
| 257 | /// @see fill_gradient_RGB() | ||
| 258 | /// @param leds a pointer to the LED array to fill | ||
| 259 | /// @param numLeds the number of LEDs to fill | ||
| 260 | /// @param c1 the starting color in the gradient | ||
| 261 | /// @param c2 the end color for the gradient | ||
| 262 | void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2); | ||
| 263 | |||
| 264 | /// Fill a range of LEDs with a smooth RGB gradient between three RGB colors. | ||
| 265 | /// @see fill_gradient_RGB() | ||
| 266 | /// @param leds a pointer to the LED array to fill | ||
| 267 | /// @param numLeds the number of LEDs to fill | ||
| 268 | /// @param c1 the starting color in the gradient | ||
| 269 | /// @param c2 the middle color for the gradient | ||
| 270 | /// @param c3 the end color for the gradient | ||
| 271 | void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3); | ||
| 272 | |||
| 273 | /// Fill a range of LEDs with a smooth RGB gradient between four RGB colors. | ||
| 274 | /// @see fill_gradient_RGB() | ||
| 275 | /// @param leds a pointer to the LED array to fill | ||
| 276 | /// @param numLeds the number of LEDs to fill | ||
| 277 | /// @param c1 the starting color in the gradient | ||
| 278 | /// @param c2 the first middle color for the gradient | ||
| 279 | /// @param c3 the second middle color for the gradient | ||
| 280 | /// @param c4 the end color for the gradient | ||
| 281 | void fill_gradient_RGB( CRGB* leds, uint16_t numLeds, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4); | ||
| 282 | |||
| 283 | /// @} ColorFills | ||
| 284 | |||
| 285 | |||
| 286 | /// @defgroup ColorFades Color Fade Functions | ||
| 287 | /// Functions for fading LED arrays | ||
| 288 | /// @{ | ||
| 289 | |||
| 290 | /// Reduce the brightness of an array of pixels all at once. | ||
| 291 | /// Guaranteed to never fade all the way to black. | ||
| 292 | /// @param leds a pointer to the LED array to fade | ||
| 293 | /// @param num_leds the number of LEDs to fade | ||
| 294 | /// @param fadeBy how much to fade each LED | ||
| 295 | void fadeLightBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); | ||
| 296 | |||
| 297 | /// @copydoc fadeLightBy() | ||
| 298 | void fade_video( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); | ||
| 299 | |||
| 300 | /// Scale the brightness of an array of pixels all at once. | ||
| 301 | /// Guaranteed to never fade all the way to black. | ||
| 302 | /// @param leds a pointer to the LED array to scale | ||
| 303 | /// @param num_leds the number of LEDs to scale | ||
| 304 | /// @param scale how much to scale each LED | ||
| 305 | void nscale8_video( CRGB* leds, uint16_t num_leds, uint8_t scale); | ||
| 306 | |||
| 307 | |||
| 308 | /// Reduce the brightness of an array of pixels all at once. | ||
| 309 | /// This function will eventually fade all the way to black. | ||
| 310 | /// @param leds a pointer to the LED array to fade | ||
| 311 | /// @param num_leds the number of LEDs to fade | ||
| 312 | /// @param fadeBy how much to fade each LED | ||
| 313 | void fadeToBlackBy( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); | ||
| 314 | |||
| 315 | /// @copydoc fadeToBlackBy() | ||
| 316 | void fade_raw( CRGB* leds, uint16_t num_leds, uint8_t fadeBy); | ||
| 317 | |||
| 318 | |||
| 319 | /// Scale the brightness of an array of pixels all at once. | ||
| 320 | /// This function will eventually fade all the way to black, even | ||
| 321 | /// if "scale" is not zero. | ||
| 322 | /// @param leds a pointer to the LED array to scale | ||
| 323 | /// @param num_leds the number of LEDs to scale | ||
| 324 | /// @param scale how much to scale each LED | ||
| 325 | void nscale8( CRGB* leds, uint16_t num_leds, uint8_t scale); | ||
| 326 | |||
| 327 | |||
| 328 | /// Reduce the brightness of an array of pixels as thought it were seen through | ||
| 329 | /// a transparent filter with the specified color. | ||
| 330 | /// For example, if the colormask if CRGB(200, 100, 50), then the pixels' red will | ||
| 331 | /// be faded to 200/256ths, their green to 100/256ths, and their blue to 50/256ths. | ||
| 332 | /// This particular example will give a "hot fade" look, with white fading to yellow, | ||
| 333 | /// then red, then black. You can also use colormasks like CRGB::Blue to zero out the | ||
| 334 | /// red and green elements, leaving blue (largely) the same. | ||
| 335 | /// @param leds a pointer to the LED array to fade | ||
| 336 | /// @param numLeds the number of LEDs to fade | ||
| 337 | /// @param colormask the color mask to fade with | ||
| 338 | void fadeUsingColor( CRGB* leds, uint16_t numLeds, const CRGB& colormask); | ||
| 339 | |||
| 340 | /// @} ColorFades | ||
| 341 | |||
| 342 | |||
| 343 | /// @defgroup ColorBlends Color Blending Functions | ||
| 344 | /// Functions for blending colors together | ||
| 345 | /// @{ | ||
| 346 | |||
| 347 | /// Computes a new color blended some fraction of the way between two other colors. | ||
| 348 | /// @param p1 the first color to blend | ||
| 349 | /// @param p2 the second color to blend | ||
| 350 | /// @param amountOfP2 the fraction of p2 to blend into p1 | ||
| 351 | CRGB blend( const CRGB& p1, const CRGB& p2, fract8 amountOfP2 ); | ||
| 352 | |||
| 353 | /// @copydoc blend(const CRGB&, const CRGB&, fract8) | ||
| 354 | /// @param directionCode the direction to travel around the color wheel | ||
| 355 | CHSV blend( const CHSV& p1, const CHSV& p2, fract8 amountOfP2, | ||
| 356 | TGradientDirectionCode directionCode = SHORTEST_HUES ); | ||
| 357 | |||
| 358 | |||
| 359 | /// Computes a new blended array of colors, each some fraction of the way between | ||
| 360 | /// corresponding elements of two source arrays of colors. | ||
| 361 | /// Useful for blending palettes. | ||
| 362 | /// @param src1 the first array of colors to blend | ||
| 363 | /// @param src2 the second array of colors to blend | ||
| 364 | /// @param dest the destination array for the colors | ||
| 365 | /// @param count the number of LEDs to blend | ||
| 366 | /// @param amountOfsrc2 the fraction of src2 to blend into src1 | ||
| 367 | CRGB* blend( const CRGB* src1, const CRGB* src2, CRGB* dest, | ||
| 368 | uint16_t count, fract8 amountOfsrc2 ); | ||
| 369 | |||
| 370 | /// @copydoc blend(const CRGB*, const CRGB*, CRGB*, uint16_t, fract8) | ||
| 371 | /// @param directionCode the direction to travel around the color wheel | ||
| 372 | CHSV* blend( const CHSV* src1, const CHSV* src2, CHSV* dest, | ||
| 373 | uint16_t count, fract8 amountOfsrc2, | ||
| 374 | TGradientDirectionCode directionCode = SHORTEST_HUES ); | ||
| 375 | |||
| 376 | |||
| 377 | /// Destructively modifies one color, blending in a given fraction of an overlay color | ||
| 378 | /// @param existing the color to modify | ||
| 379 | /// @param overlay the color to blend into existing | ||
| 380 | /// @param amountOfOverlay the fraction of overlay to blend into existing | ||
| 381 | CRGB& nblend( CRGB& existing, const CRGB& overlay, fract8 amountOfOverlay ); | ||
| 382 | |||
| 383 | /// @copydoc nblend(CRGB&, const CRGB&, fract8) | ||
| 384 | /// @param directionCode the direction to travel around the color wheel | ||
| 385 | CHSV& nblend( CHSV& existing, const CHSV& overlay, fract8 amountOfOverlay, | ||
| 386 | TGradientDirectionCode directionCode = SHORTEST_HUES ); | ||
| 387 | |||
| 388 | |||
| 389 | /// Destructively blends a given fraction of a color array into an existing color array | ||
| 390 | /// @param existing the color array to modify | ||
| 391 | /// @param overlay the color array to blend into existing | ||
| 392 | /// @param count the number of colors to process | ||
| 393 | /// @param amountOfOverlay the fraction of overlay to blend into existing | ||
| 394 | void nblend( CRGB* existing, CRGB* overlay, uint16_t count, fract8 amountOfOverlay); | ||
| 395 | |||
| 396 | /// @copydoc nblend(CRGB*, CRGB*, uint16_t, fract8) | ||
| 397 | /// @param directionCode the direction to travel around the color wheel | ||
| 398 | void nblend( CHSV* existing, CHSV* overlay, uint16_t count, fract8 amountOfOverlay, | ||
| 399 | TGradientDirectionCode directionCode = SHORTEST_HUES); | ||
| 400 | |||
| 401 | /// @} ColorBlends | ||
| 402 | |||
| 403 | |||
| 404 | /// @defgroup ColorBlurs Color Blurring Functions | ||
| 405 | /// Functions for blurring colors | ||
| 406 | /// @{ | ||
| 407 | |||
| 408 | /// One-dimensional blur filter. | ||
| 409 | /// Spreads light to 2 line neighbors. | ||
| 410 | /// * 0 = no spread at all | ||
| 411 | /// * 64 = moderate spreading | ||
| 412 | /// * 172 = maximum smooth, even spreading | ||
| 413 | /// * 173..255 = wider spreading, but increasing flicker | ||
| 414 | /// | ||
| 415 | /// Total light is NOT entirely conserved, so many repeated | ||
| 416 | /// calls to 'blur' will also result in the light fading, | ||
| 417 | /// eventually all the way to black; this is by design so that | ||
| 418 | /// it can be used to (slowly) clear the LEDs to black. | ||
| 419 | /// @param leds a pointer to the LED array to blur | ||
| 420 | /// @param numLeds the number of LEDs to blur | ||
| 421 | /// @param blur_amount the amount of blur to apply | ||
| 422 | void blur1d( CRGB* leds, uint16_t numLeds, fract8 blur_amount); | ||
| 423 | |||
| 424 | /// Two-dimensional blur filter. | ||
| 425 | /// Spreads light to 8 XY neighbors. | ||
| 426 | /// * 0 = no spread at all | ||
| 427 | /// * 64 = moderate spreading | ||
| 428 | /// * 172 = maximum smooth, even spreading | ||
| 429 | /// * 173..255 = wider spreading, but increasing flicker | ||
| 430 | /// | ||
| 431 | /// Total light is NOT entirely conserved, so many repeated | ||
| 432 | /// calls to 'blur' will also result in the light fading, | ||
| 433 | /// eventually all the way to black; this is by design so that | ||
| 434 | /// it can be used to (slowly) clear the LEDs to black. | ||
| 435 | /// @param leds a pointer to the LED array to blur | ||
| 436 | /// @param width the width of the matrix | ||
| 437 | /// @param height the height of the matrix | ||
| 438 | /// @param blur_amount the amount of blur to apply | ||
| 439 | void blur2d( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); | ||
| 440 | |||
| 441 | |||
| 442 | /// Perform a blur1d() on every row of a rectangular matrix | ||
| 443 | /// @see blur1d() | ||
| 444 | /// @param leds a pointer to the LED array to blur | ||
| 445 | /// @param width the width of the matrix | ||
| 446 | /// @param height the height of the matrix | ||
| 447 | /// @param blur_amount the amount of blur to apply | ||
| 448 | void blurRows( CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); | ||
| 449 | |||
| 450 | /// Perform a blur1d() on every column of a rectangular matrix | ||
| 451 | /// @copydetails blurRows() | ||
| 452 | void blurColumns(CRGB* leds, uint8_t width, uint8_t height, fract8 blur_amount); | ||
| 453 | |||
| 454 | /// @} ColorBlurs | ||
| 455 | |||
| 456 | |||
| 457 | /// @addtogroup ColorFills | ||
| 458 | /// @{ | ||
| 459 | |||
| 460 | /// Approximates a "black body radiation" spectrum for | ||
| 461 | /// a given "heat" level. This is useful for animations of "fire". | ||
| 462 | /// Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot). | ||
| 463 | /// This is NOT a chromatically correct "black body radiation" | ||
| 464 | /// spectrum, but it's surprisingly close, and it's fast and small. | ||
| 465 | CRGB HeatColor( uint8_t temperature); | ||
| 466 | |||
| 467 | /// @} ColorFills | ||
| 468 | /// @} ColorUtils | ||
| 469 | |||
| 470 | |||
| 471 | /// @defgroup ColorPalettes Color Palettes | ||
| 472 | /// Functions and class definitions for color palettes. | ||
| 473 | /// | ||
| 474 | /// RGB palettes map an 8-bit value (0-255) to an RGB color. | ||
| 475 | /// | ||
| 476 | /// You can create any color palette you wish; a couple of starters | ||
| 477 | /// are provided: ForestColors_p, CloudColors_p, LavaColors_p, OceanColors_p, | ||
| 478 | /// RainbowColors_p, and RainbowStripeColors_p. | ||
| 479 | /// | ||
| 480 | /// Palettes come in the traditional 256-entry variety, which take | ||
| 481 | /// up 768 bytes of RAM, and lightweight 16-entry varieties. The 16-entry | ||
| 482 | /// variety automatically interpolates between its entries to produce | ||
| 483 | /// a full 256-element color map, but at a cost of only 48 bytes of RAM. | ||
| 484 | /// | ||
| 485 | /// Basic operation is like this (using the 16-entry variety): | ||
| 486 | /// | ||
| 487 | /// 1. Declare your palette storage: | ||
| 488 | /// @code{.cpp} | ||
| 489 | /// CRGBPalette16 myPalette; | ||
| 490 | /// @endcode | ||
| 491 | /// | ||
| 492 | /// 2. Fill `myPalette` with your own 16 colors, or with a preset color scheme. | ||
| 493 | /// You can specify your 16 colors a variety of ways: | ||
| 494 | /// @code{.cpp} | ||
| 495 | /// CRGBPalette16 myPalette( | ||
| 496 | /// CRGB::Black, | ||
| 497 | /// CRGB::Black, | ||
| 498 | /// CRGB::Red, | ||
| 499 | /// CRGB::Yellow, | ||
| 500 | /// CRGB::Green, | ||
| 501 | /// CRGB::Blue, | ||
| 502 | /// CRGB::Purple, | ||
| 503 | /// CRGB::Black, | ||
| 504 | /// | ||
| 505 | /// 0x100000, | ||
| 506 | /// 0x200000, | ||
| 507 | /// 0x400000, | ||
| 508 | /// 0x800000, | ||
| 509 | /// | ||
| 510 | /// CHSV( 30,255,255), | ||
| 511 | /// CHSV( 50,255,255), | ||
| 512 | /// CHSV( 70,255,255), | ||
| 513 | /// CHSV( 90,255,255) | ||
| 514 | /// ); | ||
| 515 | /// @endcode | ||
| 516 | /// | ||
| 517 | /// Or you can initiaize your palette with a preset color scheme: | ||
| 518 | /// @code{.cpp} | ||
| 519 | /// myPalette = RainbowStripesColors_p; | ||
| 520 | /// @endcode | ||
| 521 | /// | ||
| 522 | /// 3. Any time you want to set a pixel to a color from your palette, use | ||
| 523 | /// `ColorFromPalette()` as shown: | ||
| 524 | /// | ||
| 525 | /// @code{.cpp} | ||
| 526 | /// uint8_t index = /* any value 0-255 */; | ||
| 527 | /// leds[i] = ColorFromPalette(myPalette, index); | ||
| 528 | /// @endcode | ||
| 529 | /// | ||
| 530 | /// Even though your palette has only 16 explicily defined entries, you | ||
| 531 | /// can use an "index" from 0-255. The 16 explicit palette entries will | ||
| 532 | /// be spread evenly across the 0-255 range, and the intermedate values | ||
| 533 | /// will be RGB-interpolated between adjacent explicit entries. | ||
| 534 | /// | ||
| 535 | /// It's easier to use than it sounds. | ||
| 536 | /// | ||
| 537 | /// @{ | ||
| 538 | |||
| 539 | |||
| 540 | /// @defgroup PaletteClasses Palette Classes | ||
| 541 | /// Class definitions for color palettes. | ||
| 542 | /// @todo For documentation purposes it would be nice to reorder these | ||
| 543 | /// definitions by type and in ascending number of entries. | ||
| 544 | /// | ||
| 545 | /// @{ | ||
| 546 | |||
| 547 | class CRGBPalette16; | ||
| 548 | class CRGBPalette32; | ||
| 549 | class CRGBPalette256; | ||
| 550 | class CHSVPalette16; | ||
| 551 | class CHSVPalette32; | ||
| 552 | class CHSVPalette256; | ||
| 553 | |||
| 554 | typedef uint32_t TProgmemRGBPalette16[16]; ///< CRGBPalette16 entries stored in PROGMEM memory | ||
| 555 | typedef uint32_t TProgmemHSVPalette16[16]; ///< CHSVPalette16 entries stored in PROGMEM memory | ||
| 556 | /// Alias for TProgmemRGBPalette16 | ||
| 557 | #define TProgmemPalette16 TProgmemRGBPalette16 | ||
| 558 | typedef uint32_t TProgmemRGBPalette32[32]; ///< CRGBPalette32 entries stored in PROGMEM memory | ||
| 559 | typedef uint32_t TProgmemHSVPalette32[32]; ///< CHSVPalette32 entries stored in PROGMEM memory | ||
| 560 | /// Alias for TProgmemRGBPalette32 | ||
| 561 | #define TProgmemPalette32 TProgmemRGBPalette32 | ||
| 562 | |||
| 563 | /// Byte of an RGB gradient, stored in PROGMEM memory | ||
| 564 | typedef const uint8_t TProgmemRGBGradientPalette_byte; | ||
| 565 | /// Pointer to bytes of an RGB gradient, stored in PROGMEM memory | ||
| 566 | /// @see DEFINE_GRADIENT_PALETTE | ||
| 567 | /// @see DECLARE_GRADIENT_PALETTE | ||
| 568 | typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; | ||
| 569 | /// Alias of ::TProgmemRGBGradientPalette_bytes | ||
| 570 | typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr; | ||
| 571 | |||
| 572 | /// Struct for digesting gradient pointer data into its components. | ||
| 573 | /// This is used when loading a gradient stored in PROGMEM or on | ||
| 574 | /// the heap into a palette. The pointer is dereferenced and interpreted as | ||
| 575 | /// this struct, so the component parts can be addressed and copied by name. | ||
| 576 | typedef union { | ||
| 577 | struct { | ||
| 578 | uint8_t index; ///< index of the color entry in the gradient | ||
| 579 | uint8_t r; ///< CRGB::red channel value of the color entry | ||
| 580 | uint8_t g; ///< CRGB::green channel value of the color entry | ||
| 581 | uint8_t b; ///< CRGB::blue channel value of the color entry | ||
| 582 | }; | ||
| 583 | uint32_t dword; ///< values as a packed 32-bit double word | ||
| 584 | uint8_t bytes[4]; ///< values as an array | ||
| 585 | } TRGBGradientPaletteEntryUnion; | ||
| 586 | |||
| 587 | typedef uint8_t TDynamicRGBGradientPalette_byte; ///< Byte of an RGB gradient entry, stored in dynamic (heap) memory | ||
| 588 | typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes; ///< Pointer to bytes of an RGB gradient, stored in dynamic (heap) memory | ||
| 589 | typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; ///< Alias of ::TDynamicRGBGradientPalette_bytes | ||
| 590 | |||
| 591 | /// @} | ||
| 592 | |||
| 593 | |||
| 594 | /// @defgroup PaletteUpscale Palette Upscaling Functions | ||
| 595 | /// Functions to upscale palettes from one type to another. | ||
| 596 | /// @{ | ||
| 597 | |||
| 598 | /// Convert a 16-entry palette to a 256-entry palette | ||
| 599 | /// @param srcpal16 the source palette to upscale | ||
| 600 | /// @param destpal256 the destination palette for the upscaled data | ||
| 601 | void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette256& destpal256); | ||
| 602 | /// @copydoc UpscalePalette(const struct CRGBPalette16&, struct CRGBPalette256&) | ||
| 603 | void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette256& destpal256); | ||
| 604 | |||
| 605 | /// Convert a 16-entry palette to a 32-entry palette | ||
| 606 | /// @param srcpal16 the source palette to upscale | ||
| 607 | /// @param destpal32 the destination palette for the upscaled data | ||
| 608 | void UpscalePalette(const struct CRGBPalette16& srcpal16, struct CRGBPalette32& destpal32); | ||
| 609 | /// @copydoc UpscalePalette(const struct CRGBPalette16&, struct CRGBPalette32&) | ||
| 610 | void UpscalePalette(const struct CHSVPalette16& srcpal16, struct CHSVPalette32& destpal32); | ||
| 611 | |||
| 612 | /// Convert a 32-entry palette to a 256-entry palette | ||
| 613 | /// @param srcpal32 the source palette to upscale | ||
| 614 | /// @param destpal256 the destination palette for the upscaled data | ||
| 615 | void UpscalePalette(const struct CRGBPalette32& srcpal32, struct CRGBPalette256& destpal256); | ||
| 616 | /// @copydoc UpscalePalette(const struct CRGBPalette32&, struct CRGBPalette256&) | ||
| 617 | void UpscalePalette(const struct CHSVPalette32& srcpal32, struct CHSVPalette256& destpal256); | ||
| 618 | |||
| 619 | /// @} PaletteUpscale | ||
| 620 | |||
| 621 | |||
| 622 | /// @addtogroup PaletteClasses | ||
| 623 | /// @{ | ||
| 624 | |||
| 625 | /// HSV color palette with 16 discrete values | ||
| 626 | class CHSVPalette16 { | ||
| 627 | public: | ||
| 628 | CHSV entries[16]; ///< the color entries that make up the palette | ||
| 629 | |||
| 630 | /// @copydoc CHSV::CHSV() | ||
| 631 | CHSVPalette16() {}; | ||
| 632 | |||
| 633 | /// Create palette from 16 CHSV values | ||
| 634 | CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, | ||
| 635 | const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, | ||
| 636 | const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, | ||
| 637 | const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) | ||
| 638 | { | ||
| 639 | entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; | ||
| 640 | entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; | ||
| 641 | entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; | ||
| 642 | entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; | ||
| 643 | }; | ||
| 644 | |||
| 645 | /// Copy constructor | ||
| 646 | CHSVPalette16( const CHSVPalette16& rhs) | ||
| 647 | { | ||
| 648 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 649 | } | ||
| 650 | |||
| 651 | /// @copydoc CHSVPalette16(const CHSVPalette16& rhs) | ||
| 652 | CHSVPalette16& operator=( const CHSVPalette16& rhs) | ||
| 653 | { | ||
| 654 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 655 | return *this; | ||
| 656 | } | ||
| 657 | |||
| 658 | /// Create palette from palette stored in PROGMEM | ||
| 659 | CHSVPalette16( const TProgmemHSVPalette16& rhs) | ||
| 660 | { | ||
| 661 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 662 | CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 663 | entries[i].hue = xyz.red; | ||
| 664 | entries[i].sat = xyz.green; | ||
| 665 | entries[i].val = xyz.blue; | ||
| 666 | } | ||
| 667 | } | ||
| 668 | |||
| 669 | /// @copydoc CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 670 | CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) | ||
| 671 | { | ||
| 672 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 673 | CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 674 | entries[i].hue = xyz.red; | ||
| 675 | entries[i].sat = xyz.green; | ||
| 676 | entries[i].val = xyz.blue; | ||
| 677 | } | ||
| 678 | return *this; | ||
| 679 | } | ||
| 680 | |||
| 681 | /// Array access operator to index into the gradient entries | ||
| 682 | /// @param x the index to retrieve | ||
| 683 | /// @returns reference to an entry in the palette's color array | ||
| 684 | /// @note This does not perform any interpolation like ColorFromPalette(), | ||
| 685 | /// it accesses the underlying entries that make up the gradient. Beware | ||
| 686 | /// of bounds issues! | ||
| 687 | inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 688 | { | ||
| 689 | return entries[x]; | ||
| 690 | } | ||
| 691 | |||
| 692 | /// @copydoc operator[] | ||
| 693 | inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 694 | { | ||
| 695 | ✗ | return entries[x]; | |
| 696 | } | ||
| 697 | |||
| 698 | /// @copydoc operator[] | ||
| 699 | inline CHSV& operator[] (int x) __attribute__((always_inline)) | ||
| 700 | { | ||
| 701 | return entries[(uint8_t)x]; | ||
| 702 | } | ||
| 703 | |||
| 704 | /// @copydoc operator[] | ||
| 705 | inline const CHSV& operator[] (int x) const __attribute__((always_inline)) | ||
| 706 | { | ||
| 707 | ✗ | return entries[(uint8_t)x]; | |
| 708 | } | ||
| 709 | |||
| 710 | /// Get the underlying pointer to the CHSV entries making up the palette | ||
| 711 | operator CHSV*() | ||
| 712 | { | ||
| 713 | return &(entries[0]); | ||
| 714 | } | ||
| 715 | |||
| 716 | /// Check if two palettes have the same color entries | ||
| 717 | bool operator==( const CHSVPalette16 &rhs) const | ||
| 718 | { | ||
| 719 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | ||
| 720 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | ||
| 721 | if( p == q) return true; | ||
| 722 | for( uint8_t i = 0; i < (sizeof( entries)); ++i) { | ||
| 723 | if( *p != *q) return false; | ||
| 724 | ++p; | ||
| 725 | ++q; | ||
| 726 | } | ||
| 727 | return true; | ||
| 728 | } | ||
| 729 | |||
| 730 | /// Check if two palettes do not have the same color entries | ||
| 731 | bool operator!=( const CHSVPalette16 &rhs) const | ||
| 732 | { | ||
| 733 | return !( *this == rhs); | ||
| 734 | } | ||
| 735 | |||
| 736 | /// Create palette filled with one color | ||
| 737 | /// @param c1 the color to fill the palette with | ||
| 738 | CHSVPalette16( const CHSV& c1) | ||
| 739 | { | ||
| 740 | fill_solid( &(entries[0]), 16, c1); | ||
| 741 | } | ||
| 742 | |||
| 743 | /// Create palette with a gradient from one color to another | ||
| 744 | /// @param c1 the starting color for the gradient | ||
| 745 | /// @param c2 the end color for the gradient | ||
| 746 | CHSVPalette16( const CHSV& c1, const CHSV& c2) | ||
| 747 | { | ||
| 748 | fill_gradient( &(entries[0]), 16, c1, c2); | ||
| 749 | } | ||
| 750 | |||
| 751 | /// Create palette with three-color gradient | ||
| 752 | /// @param c1 the starting color for the gradient | ||
| 753 | /// @param c2 the middle color for the gradient | ||
| 754 | /// @param c3 the end color for the gradient | ||
| 755 | CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 756 | { | ||
| 757 | fill_gradient( &(entries[0]), 16, c1, c2, c3); | ||
| 758 | } | ||
| 759 | |||
| 760 | /// Create palette with four-color gradient | ||
| 761 | /// @param c1 the starting color for the gradient | ||
| 762 | /// @param c2 the first middle color for the gradient | ||
| 763 | /// @param c3 the second middle color for the gradient | ||
| 764 | /// @param c4 the end color for the gradient | ||
| 765 | CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | ||
| 766 | { | ||
| 767 | fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); | ||
| 768 | } | ||
| 769 | |||
| 770 | }; | ||
| 771 | |||
| 772 | /// HSV color palette with 256 discrete values | ||
| 773 | class CHSVPalette256 { | ||
| 774 | public: | ||
| 775 | CHSV entries[256]; ///< @copydoc CHSVPalette16::entries | ||
| 776 | |||
| 777 | /// @copydoc CHSVPalette16::CHSVPalette16() | ||
| 778 | CHSVPalette256() {}; | ||
| 779 | |||
| 780 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 781 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 782 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 783 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 784 | CHSVPalette256( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, | ||
| 785 | const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, | ||
| 786 | const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, | ||
| 787 | const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) | ||
| 788 | { | ||
| 789 | CHSVPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, | ||
| 790 | c08,c09,c10,c11,c12,c13,c14,c15); | ||
| 791 | *this = p16; | ||
| 792 | }; | ||
| 793 | |||
| 794 | /// Copy constructor | ||
| 795 | CHSVPalette256( const CHSVPalette256& rhs) | ||
| 796 | { | ||
| 797 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 798 | } | ||
| 799 | /// @copydoc CHSVPalette256( const CHSVPalette256&) | ||
| 800 | CHSVPalette256& operator=( const CHSVPalette256& rhs) | ||
| 801 | { | ||
| 802 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 803 | return *this; | ||
| 804 | } | ||
| 805 | |||
| 806 | /// Create upscaled palette from 16-entry palette | ||
| 807 | CHSVPalette256( const CHSVPalette16& rhs16) | ||
| 808 | { | ||
| 809 | UpscalePalette( rhs16, *this); | ||
| 810 | } | ||
| 811 | /// @copydoc CHSVPalette256( const CHSVPalette16&) | ||
| 812 | CHSVPalette256& operator=( const CHSVPalette16& rhs16) | ||
| 813 | { | ||
| 814 | UpscalePalette( rhs16, *this); | ||
| 815 | return *this; | ||
| 816 | } | ||
| 817 | |||
| 818 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 819 | CHSVPalette256( const TProgmemRGBPalette16& rhs) | ||
| 820 | { | ||
| 821 | CHSVPalette16 p16(rhs); | ||
| 822 | *this = p16; | ||
| 823 | } | ||
| 824 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 825 | CHSVPalette256& operator=( const TProgmemRGBPalette16& rhs) | ||
| 826 | { | ||
| 827 | CHSVPalette16 p16(rhs); | ||
| 828 | *this = p16; | ||
| 829 | return *this; | ||
| 830 | } | ||
| 831 | |||
| 832 | /// @copydoc CHSVPalette16::operator[] | ||
| 833 | inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 834 | { | ||
| 835 | ✗ | return entries[x]; | |
| 836 | } | ||
| 837 | /// @copydoc operator[] | ||
| 838 | inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 839 | { | ||
| 840 | return entries[x]; | ||
| 841 | } | ||
| 842 | |||
| 843 | /// @copydoc operator[] | ||
| 844 | inline CHSV& operator[] (int x) __attribute__((always_inline)) | ||
| 845 | { | ||
| 846 | return entries[(uint8_t)x]; | ||
| 847 | } | ||
| 848 | /// @copydoc operator[] | ||
| 849 | inline const CHSV& operator[] (int x) const __attribute__((always_inline)) | ||
| 850 | { | ||
| 851 | ✗ | return entries[(uint8_t)x]; | |
| 852 | } | ||
| 853 | |||
| 854 | /// Get the underlying pointer to the CHSV entries making up the palette | ||
| 855 | operator CHSV*() | ||
| 856 | { | ||
| 857 | return &(entries[0]); | ||
| 858 | } | ||
| 859 | |||
| 860 | /// @copydoc CHSVPalette16::operator== | ||
| 861 | bool operator==( const CHSVPalette256 &rhs) const | ||
| 862 | { | ||
| 863 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | ||
| 864 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | ||
| 865 | if( p == q) return true; | ||
| 866 | for( uint16_t i = 0; i < (sizeof( entries)); ++i) { | ||
| 867 | if( *p != *q) return false; | ||
| 868 | ++p; | ||
| 869 | ++q; | ||
| 870 | } | ||
| 871 | return true; | ||
| 872 | } | ||
| 873 | |||
| 874 | /// @copydoc CHSVPalette16::operator!= | ||
| 875 | bool operator!=( const CHSVPalette256 &rhs) const | ||
| 876 | { | ||
| 877 | return !( *this == rhs); | ||
| 878 | } | ||
| 879 | |||
| 880 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) | ||
| 881 | CHSVPalette256( const CHSV& c1) | ||
| 882 | { | ||
| 883 | fill_solid( &(entries[0]), 256, c1); | ||
| 884 | } | ||
| 885 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) | ||
| 886 | CHSVPalette256( const CHSV& c1, const CHSV& c2) | ||
| 887 | { | ||
| 888 | fill_gradient( &(entries[0]), 256, c1, c2); | ||
| 889 | } | ||
| 890 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 891 | CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 892 | { | ||
| 893 | fill_gradient( &(entries[0]), 256, c1, c2, c3); | ||
| 894 | } | ||
| 895 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 896 | CHSVPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | ||
| 897 | { | ||
| 898 | fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); | ||
| 899 | } | ||
| 900 | }; | ||
| 901 | |||
| 902 | /// RGB color palette with 16 discrete values | ||
| 903 | class CRGBPalette16 { | ||
| 904 | public: | ||
| 905 | CRGB entries[16]; ///< @copydoc CHSVPalette16::entries | ||
| 906 | |||
| 907 | /// @copydoc CRGB::CRGB() | ||
| 908 | CRGBPalette16() {}; | ||
| 909 | |||
| 910 | /// Create palette from 16 CRGB values | ||
| 911 | 95 | CRGBPalette16( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, | |
| 912 | const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, | ||
| 913 | const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, | ||
| 914 | const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) | ||
| 915 | 95 | { | |
| 916 | 95 | entries[0]=c00; entries[1]=c01; entries[2]=c02; entries[3]=c03; | |
| 917 | 95 | entries[4]=c04; entries[5]=c05; entries[6]=c06; entries[7]=c07; | |
| 918 | 95 | entries[8]=c08; entries[9]=c09; entries[10]=c10; entries[11]=c11; | |
| 919 | 95 | entries[12]=c12; entries[13]=c13; entries[14]=c14; entries[15]=c15; | |
| 920 | 95 | }; | |
| 921 | |||
| 922 | /// Copy constructor | ||
| 923 | 18 | CRGBPalette16( const CRGBPalette16& rhs) | |
| 924 | 18 | { | |
| 925 | 18 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | |
| 926 | 18 | } | |
| 927 | /// Create palette from array of CRGB colors | ||
| 928 | CRGBPalette16( const CRGB rhs[16]) | ||
| 929 | { | ||
| 930 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 931 | } | ||
| 932 | /// @copydoc CRGBPalette16(const CRGBPalette16&) | ||
| 933 | 7962 | CRGBPalette16& operator=( const CRGBPalette16& rhs) | |
| 934 | { | ||
| 935 | 7962 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | |
| 936 | 7962 | return *this; | |
| 937 | } | ||
| 938 | /// Create palette from array of CRGB colors | ||
| 939 | CRGBPalette16& operator=( const CRGB rhs[16]) | ||
| 940 | { | ||
| 941 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 942 | return *this; | ||
| 943 | } | ||
| 944 | |||
| 945 | /// Create palette from CHSV palette | ||
| 946 | CRGBPalette16( const CHSVPalette16& rhs) | ||
| 947 | { | ||
| 948 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 949 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 950 | } | ||
| 951 | } | ||
| 952 | /// Create palette from array of CHSV colors | ||
| 953 | CRGBPalette16( const CHSV rhs[16]) | ||
| 954 | { | ||
| 955 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 956 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 957 | } | ||
| 958 | } | ||
| 959 | /// @copydoc CRGBPalette16(const CHSVPalette16&) | ||
| 960 | CRGBPalette16& operator=( const CHSVPalette16& rhs) | ||
| 961 | { | ||
| 962 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 963 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 964 | } | ||
| 965 | return *this; | ||
| 966 | } | ||
| 967 | /// Create palette from array of CHSV colors | ||
| 968 | CRGBPalette16& operator=( const CHSV rhs[16]) | ||
| 969 | { | ||
| 970 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 971 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 972 | } | ||
| 973 | return *this; | ||
| 974 | } | ||
| 975 | |||
| 976 | /// Create palette from palette stored in PROGMEM | ||
| 977 | CRGBPalette16( const TProgmemRGBPalette16& rhs) | ||
| 978 | { | ||
| 979 | for( uint8_t i = 0; i < 16; ++i) { | ||
| 980 | entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 981 | } | ||
| 982 | } | ||
| 983 | /// @copydoc CRGBPalette16(const TProgmemRGBPalette16&) | ||
| 984 | 7498 | CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) | |
| 985 | { | ||
| 986 |
2/2✓ Branch 0 taken 119968 times.
✓ Branch 1 taken 7498 times.
|
127466 | for( uint8_t i = 0; i < 16; ++i) { |
| 987 | 119968 | entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); | |
| 988 | } | ||
| 989 | 7498 | return *this; | |
| 990 | } | ||
| 991 | |||
| 992 | /// @copydoc CHSVPalette16::operator== | ||
| 993 | 34 | bool operator==( const CRGBPalette16 &rhs) const | |
| 994 | { | ||
| 995 | 34 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | |
| 996 | 34 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | |
| 997 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
|
34 | if( p == q) return true; |
| 998 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | for( uint8_t i = 0; i < (sizeof( entries)); ++i) { |
| 999 |
1/2✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
|
34 | if( *p != *q) return false; |
| 1000 | ✗ | ++p; | |
| 1001 | ✗ | ++q; | |
| 1002 | } | ||
| 1003 | ✗ | return true; | |
| 1004 | } | ||
| 1005 | /// @copydoc CHSVPalette16::operator!= | ||
| 1006 | 34 | bool operator!=( const CRGBPalette16 &rhs) const | |
| 1007 | { | ||
| 1008 | 34 | return !( *this == rhs); | |
| 1009 | } | ||
| 1010 | /// @copydoc CHSVPalette16::operator[] | ||
| 1011 | inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 1012 | { | ||
| 1013 | ✗ | return entries[x]; | |
| 1014 | } | ||
| 1015 | /// @copydoc CHSVPalette16::operator[] | ||
| 1016 | inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 1017 | { | ||
| 1018 | ✗ | return entries[x]; | |
| 1019 | } | ||
| 1020 | |||
| 1021 | /// @copydoc CHSVPalette16::operator[] | ||
| 1022 | inline CRGB& operator[] (int x) __attribute__((always_inline)) | ||
| 1023 | { | ||
| 1024 | return entries[(uint8_t)x]; | ||
| 1025 | } | ||
| 1026 | /// @copydoc CHSVPalette16::operator[] | ||
| 1027 | inline const CRGB& operator[] (int x) const __attribute__((always_inline)) | ||
| 1028 | { | ||
| 1029 | 534788 | return entries[(uint8_t)x]; | |
| 1030 | } | ||
| 1031 | |||
| 1032 | /// Get the underlying pointer to the CHSV entries making up the palette | ||
| 1033 | operator CRGB*() | ||
| 1034 | { | ||
| 1035 | return &(entries[0]); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) | ||
| 1039 | CRGBPalette16( const CHSV& c1) | ||
| 1040 | { | ||
| 1041 | fill_solid( &(entries[0]), 16, c1); | ||
| 1042 | } | ||
| 1043 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) | ||
| 1044 | CRGBPalette16( const CHSV& c1, const CHSV& c2) | ||
| 1045 | { | ||
| 1046 | fill_gradient( &(entries[0]), 16, c1, c2); | ||
| 1047 | } | ||
| 1048 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 1049 | CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 1050 | { | ||
| 1051 | fill_gradient( &(entries[0]), 16, c1, c2, c3); | ||
| 1052 | } | ||
| 1053 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1054 | 4697 | CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | |
| 1055 | 4697 | { | |
| 1056 | 4697 | fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); | |
| 1057 | 4697 | } | |
| 1058 | |||
| 1059 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) | ||
| 1060 | 4939 | CRGBPalette16( const CRGB& c1) | |
| 1061 | 4939 | { | |
| 1062 | 4939 | fill_solid( &(entries[0]), 16, c1); | |
| 1063 | 4939 | } | |
| 1064 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) | ||
| 1065 | 15426 | CRGBPalette16( const CRGB& c1, const CRGB& c2) | |
| 1066 | 15426 | { | |
| 1067 | 15426 | fill_gradient_RGB( &(entries[0]), 16, c1, c2); | |
| 1068 | 15426 | } | |
| 1069 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 1070 | 3 | CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) | |
| 1071 | 3 | { | |
| 1072 | 3 | fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3); | |
| 1073 | 3 | } | |
| 1074 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1075 | 483 | CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) | |
| 1076 | 483 | { | |
| 1077 | 483 | fill_gradient_RGB( &(entries[0]), 16, c1, c2, c3, c4); | |
| 1078 | 483 | } | |
| 1079 | |||
| 1080 | /// Creates a palette from a gradient palette in PROGMEM. | ||
| 1081 | /// | ||
| 1082 | /// Gradient palettes are loaded into CRGBPalettes in such a way | ||
| 1083 | /// that, if possible, every color represented in the gradient palette | ||
| 1084 | /// is also represented in the CRGBPalette. | ||
| 1085 | /// | ||
| 1086 | /// For example, consider a gradient palette that is all black except | ||
| 1087 | /// for a single, one-element-wide (1/256th!) spike of red in the middle: | ||
| 1088 | /// @code | ||
| 1089 | /// 0, 0,0,0 | ||
| 1090 | /// 124, 0,0,0 | ||
| 1091 | /// 125, 255,0,0 // one 1/256th-palette-wide red stripe | ||
| 1092 | /// 126, 0,0,0 | ||
| 1093 | /// 255, 0,0,0 | ||
| 1094 | /// @endcode | ||
| 1095 | /// A naive conversion of this 256-element palette to a 16-element palette | ||
| 1096 | /// might accidentally completely eliminate the red spike, rendering the | ||
| 1097 | /// palette completely black. | ||
| 1098 | /// | ||
| 1099 | /// However, the conversions provided here would attempt to include a | ||
| 1100 | /// the red stripe in the output, more-or-less as faithfully as possible. | ||
| 1101 | /// So in this case, the resulting CRGBPalette16 palette would have a red | ||
| 1102 | /// stripe in the middle which was 1/16th of a palette wide -- the | ||
| 1103 | /// narrowest possible in a CRGBPalette16. | ||
| 1104 | /// | ||
| 1105 | /// This means that the relative width of stripes in a CRGBPalette16 | ||
| 1106 | /// will be, by definition, different from the widths in the gradient | ||
| 1107 | /// palette. This code attempts to preserve "all the colors", rather than | ||
| 1108 | /// the exact stripe widths at the expense of dropping some colors. | ||
| 1109 | CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1110 | { | ||
| 1111 | *this = progpal; | ||
| 1112 | } | ||
| 1113 | /// @copydoc CRGBPalette16(TProgmemRGBGradientPalette_bytes) | ||
| 1114 | CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1115 | { | ||
| 1116 | TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); | ||
| 1117 | TRGBGradientPaletteEntryUnion u; | ||
| 1118 | |||
| 1119 | // Count entries | ||
| 1120 | uint16_t count = 0; | ||
| 1121 | do { | ||
| 1122 | u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); | ||
| 1123 | ++count; | ||
| 1124 | } while ( u.index != 255); | ||
| 1125 | |||
| 1126 | int8_t lastSlotUsed = -1; | ||
| 1127 | |||
| 1128 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1129 | CRGB rgbstart( u.r, u.g, u.b); | ||
| 1130 | |||
| 1131 | int indexstart = 0; | ||
| 1132 | uint8_t istart8 = 0; | ||
| 1133 | uint8_t iend8 = 0; | ||
| 1134 | while( indexstart < 255) { | ||
| 1135 | ++progent; | ||
| 1136 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1137 | int indexend = u.index; | ||
| 1138 | CRGB rgbend( u.r, u.g, u.b); | ||
| 1139 | istart8 = indexstart / 16; | ||
| 1140 | iend8 = indexend / 16; | ||
| 1141 | if( count < 16) { | ||
| 1142 | if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { | ||
| 1143 | istart8 = lastSlotUsed + 1; | ||
| 1144 | if( iend8 < istart8) { | ||
| 1145 | iend8 = istart8; | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | lastSlotUsed = iend8; | ||
| 1149 | } | ||
| 1150 | fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); | ||
| 1151 | indexstart = indexend; | ||
| 1152 | rgbstart = rgbend; | ||
| 1153 | } | ||
| 1154 | return *this; | ||
| 1155 | } | ||
| 1156 | /// Creates a palette from a gradient palette in dynamic (heap) memory. | ||
| 1157 | /// @copydetails CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) | ||
| 1158 | 791 | CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) | |
| 1159 | { | ||
| 1160 | 791 | TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); | |
| 1161 | TRGBGradientPaletteEntryUnion u; | ||
| 1162 | |||
| 1163 | // Count entries | ||
| 1164 | 791 | uint16_t count = 0; | |
| 1165 | do { | ||
| 1166 | 6706 | u = *(ent + count); | |
| 1167 | 6706 | ++count; | |
| 1168 |
2/2✓ Branch 0 taken 5915 times.
✓ Branch 1 taken 791 times.
|
6706 | } while ( u.index != 255); |
| 1169 | |||
| 1170 | 791 | int8_t lastSlotUsed = -1; | |
| 1171 | |||
| 1172 | |||
| 1173 | 791 | u = *ent; | |
| 1174 | 791 | CRGB rgbstart( u.r, u.g, u.b); | |
| 1175 | |||
| 1176 | 791 | int indexstart = 0; | |
| 1177 | 791 | uint8_t istart8 = 0; | |
| 1178 | 791 | uint8_t iend8 = 0; | |
| 1179 |
2/2✓ Branch 0 taken 5915 times.
✓ Branch 1 taken 791 times.
|
6706 | while( indexstart < 255) { |
| 1180 | 5915 | ++ent; | |
| 1181 | 5915 | u = *ent; | |
| 1182 | 5915 | int indexend = u.index; | |
| 1183 | 5915 | CRGB rgbend( u.r, u.g, u.b); | |
| 1184 | 5915 | istart8 = indexstart / 16; | |
| 1185 | 5915 | iend8 = indexend / 16; | |
| 1186 |
2/2✓ Branch 0 taken 5034 times.
✓ Branch 1 taken 881 times.
|
5915 | if( count < 16) { |
| 1187 |
4/4✓ Branch 0 taken 4295 times.
✓ Branch 1 taken 739 times.
✓ Branch 2 taken 4127 times.
✓ Branch 3 taken 168 times.
|
5034 | if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { |
| 1188 | 4127 | istart8 = lastSlotUsed + 1; | |
| 1189 |
2/2✓ Branch 0 taken 697 times.
✓ Branch 1 taken 3430 times.
|
4127 | if( iend8 < istart8) { |
| 1190 | 697 | iend8 = istart8; | |
| 1191 | } | ||
| 1192 | } | ||
| 1193 | 5034 | lastSlotUsed = iend8; | |
| 1194 | } | ||
| 1195 |
1/2✓ Branch 0 taken 5915 times.
✗ Branch 1 not taken.
|
5915 | fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); |
| 1196 | 5915 | indexstart = indexend; | |
| 1197 | 5915 | rgbstart = rgbend; | |
| 1198 | } | ||
| 1199 | 791 | return *this; | |
| 1200 | } | ||
| 1201 | |||
| 1202 | }; | ||
| 1203 | |||
| 1204 | |||
| 1205 | /// HSV color palette with 32 discrete values | ||
| 1206 | class CHSVPalette32 { | ||
| 1207 | public: | ||
| 1208 | CHSV entries[32]; ///< @copydoc CHSVPalette16::entries | ||
| 1209 | |||
| 1210 | /// @copydoc CHSVPalette16::CHSVPalette16() | ||
| 1211 | CHSVPalette32() {}; | ||
| 1212 | |||
| 1213 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 1214 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 1215 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&, | ||
| 1216 | /// const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1217 | CHSVPalette32( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, | ||
| 1218 | const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, | ||
| 1219 | const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, | ||
| 1220 | const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& c15 ) | ||
| 1221 | { | ||
| 1222 | for( uint8_t i = 0; i < 2; ++i) { | ||
| 1223 | entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; | ||
| 1224 | entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; | ||
| 1225 | entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; | ||
| 1226 | entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; | ||
| 1227 | } | ||
| 1228 | }; | ||
| 1229 | |||
| 1230 | /// Copy constructor | ||
| 1231 | CHSVPalette32( const CHSVPalette32& rhs) | ||
| 1232 | { | ||
| 1233 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1234 | } | ||
| 1235 | /// @copydoc CHSVPalette32( const CHSVPalette32&) | ||
| 1236 | CHSVPalette32& operator=( const CHSVPalette32& rhs) | ||
| 1237 | { | ||
| 1238 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1239 | return *this; | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1243 | CHSVPalette32( const TProgmemHSVPalette32& rhs) | ||
| 1244 | { | ||
| 1245 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1246 | CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 1247 | entries[i].hue = xyz.red; | ||
| 1248 | entries[i].sat = xyz.green; | ||
| 1249 | entries[i].val = xyz.blue; | ||
| 1250 | } | ||
| 1251 | } | ||
| 1252 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1253 | CHSVPalette32& operator=( const TProgmemHSVPalette32& rhs) | ||
| 1254 | { | ||
| 1255 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1256 | CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 1257 | entries[i].hue = xyz.red; | ||
| 1258 | entries[i].sat = xyz.green; | ||
| 1259 | entries[i].val = xyz.blue; | ||
| 1260 | } | ||
| 1261 | return *this; | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1265 | inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 1266 | { | ||
| 1267 | return entries[x]; | ||
| 1268 | } | ||
| 1269 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1270 | inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 1271 | { | ||
| 1272 | return entries[x]; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1276 | inline CHSV& operator[] (int x) __attribute__((always_inline)) | ||
| 1277 | { | ||
| 1278 | ✗ | return entries[(uint8_t)x]; | |
| 1279 | } | ||
| 1280 | /// @copydoc CHSVPalette16::CHSVPalette16(const TProgmemHSVPalette16&) | ||
| 1281 | inline const CHSV& operator[] (int x) const __attribute__((always_inline)) | ||
| 1282 | { | ||
| 1283 | ✗ | return entries[(uint8_t)x]; | |
| 1284 | } | ||
| 1285 | |||
| 1286 | /// Get the underlying pointer to the CHSV entries making up the palette | ||
| 1287 | operator CHSV*() | ||
| 1288 | { | ||
| 1289 | return &(entries[0]); | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | /// @copydoc CHSVPalette16::operator== | ||
| 1293 | bool operator==( const CHSVPalette32 &rhs) const | ||
| 1294 | { | ||
| 1295 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | ||
| 1296 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | ||
| 1297 | if( p == q) return true; | ||
| 1298 | for( uint8_t i = 0; i < (sizeof( entries)); ++i) { | ||
| 1299 | if( *p != *q) return false; | ||
| 1300 | ++p; | ||
| 1301 | ++q; | ||
| 1302 | } | ||
| 1303 | return true; | ||
| 1304 | } | ||
| 1305 | /// @copydoc CHSVPalette16::operator!= | ||
| 1306 | bool operator!=( const CHSVPalette32 &rhs) const | ||
| 1307 | { | ||
| 1308 | return !( *this == rhs); | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) | ||
| 1312 | CHSVPalette32( const CHSV& c1) | ||
| 1313 | { | ||
| 1314 | fill_solid( &(entries[0]), 32, c1); | ||
| 1315 | } | ||
| 1316 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) | ||
| 1317 | CHSVPalette32( const CHSV& c1, const CHSV& c2) | ||
| 1318 | { | ||
| 1319 | fill_gradient( &(entries[0]), 32, c1, c2); | ||
| 1320 | } | ||
| 1321 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 1322 | CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 1323 | { | ||
| 1324 | fill_gradient( &(entries[0]), 32, c1, c2, c3); | ||
| 1325 | } | ||
| 1326 | /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1327 | CHSVPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | ||
| 1328 | { | ||
| 1329 | fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | }; | ||
| 1333 | |||
| 1334 | /// RGB color palette with 32 discrete values | ||
| 1335 | class CRGBPalette32 { | ||
| 1336 | public: | ||
| 1337 | CRGB entries[32]; ///< @copydoc CHSVPalette16::entries | ||
| 1338 | |||
| 1339 | /// @copydoc CRGB::CRGB() | ||
| 1340 | CRGBPalette32() {}; | ||
| 1341 | |||
| 1342 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, | ||
| 1343 | /// const CRGB&, const CRGB&, const CRGB&, | ||
| 1344 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, | ||
| 1345 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, | ||
| 1346 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&) | ||
| 1347 | CRGBPalette32( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, | ||
| 1348 | const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, | ||
| 1349 | const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, | ||
| 1350 | const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) | ||
| 1351 | { | ||
| 1352 | for( uint8_t i = 0; i < 2; ++i) { | ||
| 1353 | entries[0+i]=c00; entries[2+i]=c01; entries[4+i]=c02; entries[6+i]=c03; | ||
| 1354 | entries[8+i]=c04; entries[10+i]=c05; entries[12+i]=c06; entries[14+i]=c07; | ||
| 1355 | entries[16+i]=c08; entries[18+i]=c09; entries[20+i]=c10; entries[22+i]=c11; | ||
| 1356 | entries[24+i]=c12; entries[26+i]=c13; entries[28+i]=c14; entries[30+i]=c15; | ||
| 1357 | } | ||
| 1358 | }; | ||
| 1359 | |||
| 1360 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGBPalette16&) | ||
| 1361 | CRGBPalette32( const CRGBPalette32& rhs) | ||
| 1362 | { | ||
| 1363 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1364 | } | ||
| 1365 | /// Create palette from array of CRGB colors | ||
| 1366 | CRGBPalette32( const CRGB rhs[32]) | ||
| 1367 | { | ||
| 1368 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 1369 | } | ||
| 1370 | /// @copydoc CRGBPalette32(const CRGBPalette32&) | ||
| 1371 | CRGBPalette32& operator=( const CRGBPalette32& rhs) | ||
| 1372 | { | ||
| 1373 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1374 | return *this; | ||
| 1375 | } | ||
| 1376 | /// Create palette from array of CRGB colors | ||
| 1377 | CRGBPalette32& operator=( const CRGB rhs[32]) | ||
| 1378 | { | ||
| 1379 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 1380 | return *this; | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSVPalette16&) | ||
| 1384 | CRGBPalette32( const CHSVPalette32& rhs) | ||
| 1385 | { | ||
| 1386 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1387 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 1388 | } | ||
| 1389 | } | ||
| 1390 | /// Create palette from array of CHSV colors | ||
| 1391 | CRGBPalette32( const CHSV rhs[32]) | ||
| 1392 | { | ||
| 1393 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1394 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 1395 | } | ||
| 1396 | } | ||
| 1397 | /// @copydoc CRGBPalette32(const CHSVPalette32&) | ||
| 1398 | CRGBPalette32& operator=( const CHSVPalette32& rhs) | ||
| 1399 | { | ||
| 1400 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1401 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 1402 | } | ||
| 1403 | return *this; | ||
| 1404 | } | ||
| 1405 | /// Create palette from array of CHSV colors | ||
| 1406 | CRGBPalette32& operator=( const CHSV rhs[32]) | ||
| 1407 | { | ||
| 1408 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1409 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 1410 | } | ||
| 1411 | return *this; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) | ||
| 1415 | CRGBPalette32( const TProgmemRGBPalette32& rhs) | ||
| 1416 | { | ||
| 1417 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1418 | entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 1419 | } | ||
| 1420 | } | ||
| 1421 | /// @copydoc CRGBPalette32(const TProgmemRGBPalette32&) | ||
| 1422 | CRGBPalette32& operator=( const TProgmemRGBPalette32& rhs) | ||
| 1423 | { | ||
| 1424 | for( uint8_t i = 0; i < 32; ++i) { | ||
| 1425 | entries[i] = FL_PGM_READ_DWORD_NEAR( rhs + i); | ||
| 1426 | } | ||
| 1427 | return *this; | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | /// @copydoc CRGBPalette16::operator== | ||
| 1431 | bool operator==( const CRGBPalette32 &rhs) const | ||
| 1432 | { | ||
| 1433 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | ||
| 1434 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | ||
| 1435 | if( p == q) return true; | ||
| 1436 | for( uint8_t i = 0; i < (sizeof( entries)); ++i) { | ||
| 1437 | if( *p != *q) return false; | ||
| 1438 | ++p; | ||
| 1439 | ++q; | ||
| 1440 | } | ||
| 1441 | return true; | ||
| 1442 | } | ||
| 1443 | /// @copydoc CRGBPalette16::operator!= | ||
| 1444 | bool operator!=( const CRGBPalette32 &rhs) const | ||
| 1445 | { | ||
| 1446 | return !( *this == rhs); | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | /// @copydoc CRGBPalette16::operator[] | ||
| 1450 | inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 1451 | { | ||
| 1452 | return entries[x]; | ||
| 1453 | } | ||
| 1454 | /// @copydoc CRGBPalette16::operator[] | ||
| 1455 | inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 1456 | { | ||
| 1457 | return entries[x]; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | /// @copydoc CRGBPalette16::operator[] | ||
| 1461 | inline CRGB& operator[] (int x) __attribute__((always_inline)) | ||
| 1462 | { | ||
| 1463 | ✗ | return entries[(uint8_t)x]; | |
| 1464 | } | ||
| 1465 | /// @copydoc CRGBPalette16::operator[] | ||
| 1466 | inline const CRGB& operator[] (int x) const __attribute__((always_inline)) | ||
| 1467 | { | ||
| 1468 | ✗ | return entries[(uint8_t)x]; | |
| 1469 | } | ||
| 1470 | |||
| 1471 | /// Get the underlying pointer to the CRGB entries making up the palette | ||
| 1472 | operator CRGB*() | ||
| 1473 | { | ||
| 1474 | return &(entries[0]); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&) | ||
| 1478 | CRGBPalette32( const CHSV& c1) | ||
| 1479 | { | ||
| 1480 | fill_solid( &(entries[0]), 32, c1); | ||
| 1481 | } | ||
| 1482 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&) | ||
| 1483 | CRGBPalette32( const CHSV& c1, const CHSV& c2) | ||
| 1484 | { | ||
| 1485 | fill_gradient( &(entries[0]), 32, c1, c2); | ||
| 1486 | } | ||
| 1487 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 1488 | CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 1489 | { | ||
| 1490 | fill_gradient( &(entries[0]), 32, c1, c2, c3); | ||
| 1491 | } | ||
| 1492 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1493 | CRGBPalette32( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | ||
| 1494 | { | ||
| 1495 | fill_gradient( &(entries[0]), 32, c1, c2, c3, c4); | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&) | ||
| 1499 | CRGBPalette32( const CRGB& c1) | ||
| 1500 | { | ||
| 1501 | fill_solid( &(entries[0]), 32, c1); | ||
| 1502 | } | ||
| 1503 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&) | ||
| 1504 | CRGBPalette32( const CRGB& c1, const CRGB& c2) | ||
| 1505 | { | ||
| 1506 | fill_gradient_RGB( &(entries[0]), 32, c1, c2); | ||
| 1507 | } | ||
| 1508 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&) | ||
| 1509 | CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3) | ||
| 1510 | { | ||
| 1511 | fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3); | ||
| 1512 | } | ||
| 1513 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&, const CRGB&) | ||
| 1514 | CRGBPalette32( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) | ||
| 1515 | { | ||
| 1516 | fill_gradient_RGB( &(entries[0]), 32, c1, c2, c3, c4); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | |||
| 1520 | /// Create upscaled palette from 16-entry palette | ||
| 1521 | CRGBPalette32( const CRGBPalette16& rhs16) | ||
| 1522 | { | ||
| 1523 | UpscalePalette( rhs16, *this); | ||
| 1524 | } | ||
| 1525 | /// @copydoc CRGBPalette32(const CRGBPalette16&) | ||
| 1526 | CRGBPalette32& operator=( const CRGBPalette16& rhs16) | ||
| 1527 | { | ||
| 1528 | UpscalePalette( rhs16, *this); | ||
| 1529 | return *this; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) | ||
| 1533 | CRGBPalette32( const TProgmemRGBPalette16& rhs) | ||
| 1534 | { | ||
| 1535 | CRGBPalette16 p16(rhs); | ||
| 1536 | *this = p16; | ||
| 1537 | } | ||
| 1538 | /// @copydoc CRGBPalette32(const TProgmemRGBPalette16&) | ||
| 1539 | CRGBPalette32& operator=( const TProgmemRGBPalette16& rhs) | ||
| 1540 | { | ||
| 1541 | CRGBPalette16 p16(rhs); | ||
| 1542 | *this = p16; | ||
| 1543 | return *this; | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | |||
| 1547 | /// @copydoc CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) | ||
| 1548 | CRGBPalette32( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1549 | { | ||
| 1550 | *this = progpal; | ||
| 1551 | } | ||
| 1552 | /// @copydoc CRGBPalette32(TProgmemRGBGradientPalette_bytes) | ||
| 1553 | CRGBPalette32& operator=( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1554 | { | ||
| 1555 | TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); | ||
| 1556 | TRGBGradientPaletteEntryUnion u; | ||
| 1557 | |||
| 1558 | // Count entries | ||
| 1559 | uint16_t count = 0; | ||
| 1560 | do { | ||
| 1561 | u.dword = FL_PGM_READ_DWORD_NEAR(progent + count); | ||
| 1562 | ++count; | ||
| 1563 | } while ( u.index != 255); | ||
| 1564 | |||
| 1565 | int8_t lastSlotUsed = -1; | ||
| 1566 | |||
| 1567 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1568 | CRGB rgbstart( u.r, u.g, u.b); | ||
| 1569 | |||
| 1570 | int indexstart = 0; | ||
| 1571 | uint8_t istart8 = 0; | ||
| 1572 | uint8_t iend8 = 0; | ||
| 1573 | while( indexstart < 255) { | ||
| 1574 | ++progent; | ||
| 1575 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1576 | int indexend = u.index; | ||
| 1577 | CRGB rgbend( u.r, u.g, u.b); | ||
| 1578 | istart8 = indexstart / 8; | ||
| 1579 | iend8 = indexend / 8; | ||
| 1580 | if( count < 16) { | ||
| 1581 | if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { | ||
| 1582 | istart8 = lastSlotUsed + 1; | ||
| 1583 | if( iend8 < istart8) { | ||
| 1584 | iend8 = istart8; | ||
| 1585 | } | ||
| 1586 | } | ||
| 1587 | lastSlotUsed = iend8; | ||
| 1588 | } | ||
| 1589 | fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); | ||
| 1590 | indexstart = indexend; | ||
| 1591 | rgbstart = rgbend; | ||
| 1592 | } | ||
| 1593 | return *this; | ||
| 1594 | } | ||
| 1595 | /// @copydoc CRGBPalette16::loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes) | ||
| 1596 | CRGBPalette32& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) | ||
| 1597 | { | ||
| 1598 | TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); | ||
| 1599 | TRGBGradientPaletteEntryUnion u; | ||
| 1600 | |||
| 1601 | // Count entries | ||
| 1602 | uint16_t count = 0; | ||
| 1603 | do { | ||
| 1604 | u = *(ent + count); | ||
| 1605 | ++count; | ||
| 1606 | } while ( u.index != 255); | ||
| 1607 | |||
| 1608 | int8_t lastSlotUsed = -1; | ||
| 1609 | |||
| 1610 | |||
| 1611 | u = *ent; | ||
| 1612 | CRGB rgbstart( u.r, u.g, u.b); | ||
| 1613 | |||
| 1614 | int indexstart = 0; | ||
| 1615 | uint8_t istart8 = 0; | ||
| 1616 | uint8_t iend8 = 0; | ||
| 1617 | while( indexstart < 255) { | ||
| 1618 | ++ent; | ||
| 1619 | u = *ent; | ||
| 1620 | int indexend = u.index; | ||
| 1621 | CRGB rgbend( u.r, u.g, u.b); | ||
| 1622 | istart8 = indexstart / 8; | ||
| 1623 | iend8 = indexend / 8; | ||
| 1624 | if( count < 16) { | ||
| 1625 | if( (istart8 <= lastSlotUsed) && (lastSlotUsed < 31)) { | ||
| 1626 | istart8 = lastSlotUsed + 1; | ||
| 1627 | if( iend8 < istart8) { | ||
| 1628 | iend8 = istart8; | ||
| 1629 | } | ||
| 1630 | } | ||
| 1631 | lastSlotUsed = iend8; | ||
| 1632 | } | ||
| 1633 | fill_gradient_RGB( &(entries[0]), istart8, rgbstart, iend8, rgbend); | ||
| 1634 | indexstart = indexend; | ||
| 1635 | rgbstart = rgbend; | ||
| 1636 | } | ||
| 1637 | return *this; | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | }; | ||
| 1641 | |||
| 1642 | |||
| 1643 | /// RGB color palette with 256 discrete values | ||
| 1644 | class CRGBPalette256 { | ||
| 1645 | public: | ||
| 1646 | CRGB entries[256]; ///< @copydoc CHSVPalette16::entries | ||
| 1647 | |||
| 1648 | /// @copydoc CRGB::CRGB() | ||
| 1649 | CRGBPalette256() {}; | ||
| 1650 | |||
| 1651 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, | ||
| 1652 | /// const CRGB&, const CRGB&, const CRGB&, | ||
| 1653 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, | ||
| 1654 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&, | ||
| 1655 | /// const CRGB&, const CRGB&, const CRGB&, const CRGB&) | ||
| 1656 | CRGBPalette256( const CRGB& c00,const CRGB& c01,const CRGB& c02,const CRGB& c03, | ||
| 1657 | const CRGB& c04,const CRGB& c05,const CRGB& c06,const CRGB& c07, | ||
| 1658 | const CRGB& c08,const CRGB& c09,const CRGB& c10,const CRGB& c11, | ||
| 1659 | const CRGB& c12,const CRGB& c13,const CRGB& c14,const CRGB& c15 ) | ||
| 1660 | { | ||
| 1661 | CRGBPalette16 p16(c00,c01,c02,c03,c04,c05,c06,c07, | ||
| 1662 | c08,c09,c10,c11,c12,c13,c14,c15); | ||
| 1663 | *this = p16; | ||
| 1664 | }; | ||
| 1665 | |||
| 1666 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGBPalette16&) | ||
| 1667 | CRGBPalette256( const CRGBPalette256& rhs) | ||
| 1668 | { | ||
| 1669 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1670 | } | ||
| 1671 | /// Create palette from array of CRGB colors | ||
| 1672 | CRGBPalette256( const CRGB rhs[256]) | ||
| 1673 | { | ||
| 1674 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 1675 | } | ||
| 1676 | /// @copydoc CRGBPalette256(const CRGBPalette256&) | ||
| 1677 | CRGBPalette256& operator=( const CRGBPalette256& rhs) | ||
| 1678 | { | ||
| 1679 | memmove8( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); | ||
| 1680 | return *this; | ||
| 1681 | } | ||
| 1682 | /// Create palette from array of CRGB colors | ||
| 1683 | CRGBPalette256& operator=( const CRGB rhs[256]) | ||
| 1684 | { | ||
| 1685 | memmove8( (void *) &(entries[0]), &(rhs[0]), sizeof( entries)); | ||
| 1686 | return *this; | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSVPalette16&) | ||
| 1690 | CRGBPalette256( const CHSVPalette256& rhs) | ||
| 1691 | { | ||
| 1692 | for( int i = 0; i < 256; ++i) { | ||
| 1693 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 1694 | } | ||
| 1695 | } | ||
| 1696 | /// Create palette from array of CHSV colors | ||
| 1697 | CRGBPalette256( const CHSV rhs[256]) | ||
| 1698 | { | ||
| 1699 | for( int i = 0; i < 256; ++i) { | ||
| 1700 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 1701 | } | ||
| 1702 | } | ||
| 1703 | /// @copydoc CRGBPalette256(const CRGBPalette256&) | ||
| 1704 | CRGBPalette256& operator=( const CHSVPalette256& rhs) | ||
| 1705 | { | ||
| 1706 | for( int i = 0; i < 256; ++i) { | ||
| 1707 | entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion | ||
| 1708 | } | ||
| 1709 | return *this; | ||
| 1710 | } | ||
| 1711 | /// Create palette from array of CHSV colors | ||
| 1712 | CRGBPalette256& operator=( const CHSV rhs[256]) | ||
| 1713 | { | ||
| 1714 | for( int i = 0; i < 256; ++i) { | ||
| 1715 | entries[i] = rhs[i]; // implicit HSV-to-RGB conversion | ||
| 1716 | } | ||
| 1717 | return *this; | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | /// @copydoc CRGBPalette32::CRGBPalette32(const CRGBPalette16&) | ||
| 1721 | CRGBPalette256( const CRGBPalette16& rhs16) | ||
| 1722 | { | ||
| 1723 | UpscalePalette( rhs16, *this); | ||
| 1724 | } | ||
| 1725 | /// @copydoc CRGBPalette256(const CRGBPalette16&) | ||
| 1726 | CRGBPalette256& operator=( const CRGBPalette16& rhs16) | ||
| 1727 | { | ||
| 1728 | UpscalePalette( rhs16, *this); | ||
| 1729 | return *this; | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | /// @copydoc CRGBPalette16::CRGBPalette16(const TProgmemRGBPalette16&) | ||
| 1733 | CRGBPalette256( const TProgmemRGBPalette16& rhs) | ||
| 1734 | { | ||
| 1735 | CRGBPalette16 p16(rhs); | ||
| 1736 | *this = p16; | ||
| 1737 | } | ||
| 1738 | /// @copydoc CRGBPalette256(const TProgmemRGBPalette16&) | ||
| 1739 | CRGBPalette256& operator=( const TProgmemRGBPalette16& rhs) | ||
| 1740 | { | ||
| 1741 | CRGBPalette16 p16(rhs); | ||
| 1742 | *this = p16; | ||
| 1743 | return *this; | ||
| 1744 | } | ||
| 1745 | |||
| 1746 | /// @copydoc CRGBPalette16::operator== | ||
| 1747 | bool operator==( const CRGBPalette256 &rhs) const | ||
| 1748 | { | ||
| 1749 | const uint8_t* p = (const uint8_t*)(&(this->entries[0])); | ||
| 1750 | const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); | ||
| 1751 | if( p == q) return true; | ||
| 1752 | for( uint16_t i = 0; i < (sizeof( entries)); ++i) { | ||
| 1753 | if( *p != *q) return false; | ||
| 1754 | ++p; | ||
| 1755 | ++q; | ||
| 1756 | } | ||
| 1757 | return true; | ||
| 1758 | } | ||
| 1759 | /// @copydoc CRGBPalette16::operator!= | ||
| 1760 | bool operator!=( const CRGBPalette256 &rhs) const | ||
| 1761 | { | ||
| 1762 | return !( *this == rhs); | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | /// @copydoc CRGBPalette16::operator[] | ||
| 1766 | inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) | ||
| 1767 | { | ||
| 1768 | ✗ | return entries[x]; | |
| 1769 | } | ||
| 1770 | /// @copydoc CRGBPalette16::operator[] | ||
| 1771 | inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) | ||
| 1772 | { | ||
| 1773 | return entries[x]; | ||
| 1774 | } | ||
| 1775 | |||
| 1776 | /// @copydoc CRGBPalette16::operator[] | ||
| 1777 | inline CRGB& operator[] (int x) __attribute__((always_inline)) | ||
| 1778 | { | ||
| 1779 | return entries[(uint8_t)x]; | ||
| 1780 | } | ||
| 1781 | /// @copydoc CRGBPalette16::operator[] | ||
| 1782 | inline const CRGB& operator[] (int x) const __attribute__((always_inline)) | ||
| 1783 | { | ||
| 1784 | ✗ | return entries[(uint8_t)x]; | |
| 1785 | } | ||
| 1786 | |||
| 1787 | /// Get the underlying pointer to the CHSV entries making up the palette | ||
| 1788 | operator CRGB*() | ||
| 1789 | { | ||
| 1790 | return &(entries[0]); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&) | ||
| 1794 | CRGBPalette256( const CHSV& c1) | ||
| 1795 | { | ||
| 1796 | fill_solid( &(entries[0]), 256, c1); | ||
| 1797 | } | ||
| 1798 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&) | ||
| 1799 | CRGBPalette256( const CHSV& c1, const CHSV& c2) | ||
| 1800 | { | ||
| 1801 | fill_gradient( &(entries[0]), 256, c1, c2); | ||
| 1802 | } | ||
| 1803 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&) | ||
| 1804 | CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3) | ||
| 1805 | { | ||
| 1806 | fill_gradient( &(entries[0]), 256, c1, c2, c3); | ||
| 1807 | } | ||
| 1808 | /// @copydoc CRGBPalette16::CRGBPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) | ||
| 1809 | CRGBPalette256( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) | ||
| 1810 | { | ||
| 1811 | fill_gradient( &(entries[0]), 256, c1, c2, c3, c4); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&) | ||
| 1815 | CRGBPalette256( const CRGB& c1) | ||
| 1816 | { | ||
| 1817 | fill_solid( &(entries[0]), 256, c1); | ||
| 1818 | } | ||
| 1819 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&) | ||
| 1820 | CRGBPalette256( const CRGB& c1, const CRGB& c2) | ||
| 1821 | { | ||
| 1822 | fill_gradient_RGB( &(entries[0]), 256, c1, c2); | ||
| 1823 | } | ||
| 1824 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&) | ||
| 1825 | CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3) | ||
| 1826 | { | ||
| 1827 | fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3); | ||
| 1828 | } | ||
| 1829 | /// @copydoc CRGBPalette16::CRGBPalette16(const CRGB&, const CRGB&, const CRGB&, const CRGB&) | ||
| 1830 | CRGBPalette256( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) | ||
| 1831 | { | ||
| 1832 | fill_gradient_RGB( &(entries[0]), 256, c1, c2, c3, c4); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | /// @copydoc CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) | ||
| 1836 | CRGBPalette256( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1837 | { | ||
| 1838 | *this = progpal; | ||
| 1839 | } | ||
| 1840 | /// @copydoc CRGBPalette256(TProgmemRGBGradientPalette_bytes) | ||
| 1841 | CRGBPalette256& operator=( TProgmemRGBGradientPalette_bytes progpal ) | ||
| 1842 | { | ||
| 1843 | TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); | ||
| 1844 | TRGBGradientPaletteEntryUnion u; | ||
| 1845 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1846 | CRGB rgbstart( u.r, u.g, u.b); | ||
| 1847 | |||
| 1848 | int indexstart = 0; | ||
| 1849 | while( indexstart < 255) { | ||
| 1850 | ++progent; | ||
| 1851 | u.dword = FL_PGM_READ_DWORD_NEAR( progent); | ||
| 1852 | int indexend = u.index; | ||
| 1853 | CRGB rgbend( u.r, u.g, u.b); | ||
| 1854 | fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); | ||
| 1855 | indexstart = indexend; | ||
| 1856 | rgbstart = rgbend; | ||
| 1857 | } | ||
| 1858 | return *this; | ||
| 1859 | } | ||
| 1860 | /// @copydoc CRGBPalette16::loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes) | ||
| 1861 | CRGBPalette256& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) | ||
| 1862 | { | ||
| 1863 | TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); | ||
| 1864 | TRGBGradientPaletteEntryUnion u; | ||
| 1865 | u = *ent; | ||
| 1866 | CRGB rgbstart( u.r, u.g, u.b); | ||
| 1867 | |||
| 1868 | int indexstart = 0; | ||
| 1869 | while( indexstart < 255) { | ||
| 1870 | ++ent; | ||
| 1871 | u = *ent; | ||
| 1872 | int indexend = u.index; | ||
| 1873 | CRGB rgbend( u.r, u.g, u.b); | ||
| 1874 | fill_gradient_RGB( &(entries[0]), indexstart, rgbstart, indexend, rgbend); | ||
| 1875 | indexstart = indexend; | ||
| 1876 | rgbstart = rgbend; | ||
| 1877 | } | ||
| 1878 | return *this; | ||
| 1879 | } | ||
| 1880 | }; | ||
| 1881 | |||
| 1882 | /// @} PaletteClasses | ||
| 1883 | |||
| 1884 | |||
| 1885 | /// @defgroup PaletteColors Palette Color Functions | ||
| 1886 | /// Functions to retrieve smooth color data from palettes | ||
| 1887 | /// @{ | ||
| 1888 | |||
| 1889 | /// Color interpolation options for palette | ||
| 1890 | typedef enum { | ||
| 1891 | NOBLEND=0, ///< No interpolation between palette entries | ||
| 1892 | LINEARBLEND=1, ///< Linear interpolation between palette entries, with wrap-around from end to the beginning again | ||
| 1893 | LINEARBLEND_NOWRAP=2 ///< Linear interpolation between palette entries, but no wrap-around | ||
| 1894 | } TBlendType; | ||
| 1895 | |||
| 1896 | /// Get a color from a palette. | ||
| 1897 | /// These are the main functions for getting and using palette colors. Regardless | ||
| 1898 | /// of the number of entries in the base palette, this function will interpolate | ||
| 1899 | /// between entries to turn the discrete colors into a smooth gradient. | ||
| 1900 | /// @param pal the palette to retrieve the color from | ||
| 1901 | /// @param index the position in the palette to retrieve the color for (0-255) | ||
| 1902 | /// @param brightness optional brightness value to scale the resulting color | ||
| 1903 | /// @param blendType whether to take the palette entries directly (NOBLEND) | ||
| 1904 | /// or blend linearly between palette entries (LINEARBLEND) | ||
| 1905 | CRGB ColorFromPalette( const CRGBPalette16& pal, | ||
| 1906 | uint8_t index, | ||
| 1907 | uint8_t brightness=255, | ||
| 1908 | TBlendType blendType=LINEARBLEND); | ||
| 1909 | |||
| 1910 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1911 | CRGB ColorFromPalette( const TProgmemRGBPalette16& pal, | ||
| 1912 | uint8_t index, | ||
| 1913 | uint8_t brightness=255, | ||
| 1914 | TBlendType blendType=LINEARBLEND); | ||
| 1915 | |||
| 1916 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1917 | CRGB ColorFromPalette( const CRGBPalette256& pal, | ||
| 1918 | uint8_t index, | ||
| 1919 | uint8_t brightness=255, | ||
| 1920 | TBlendType blendType=NOBLEND ); | ||
| 1921 | |||
| 1922 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1923 | CHSV ColorFromPalette( const CHSVPalette16& pal, | ||
| 1924 | uint8_t index, | ||
| 1925 | uint8_t brightness=255, | ||
| 1926 | TBlendType blendType=LINEARBLEND); | ||
| 1927 | |||
| 1928 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1929 | CHSV ColorFromPalette( const CHSVPalette256& pal, | ||
| 1930 | uint8_t index, | ||
| 1931 | uint8_t brightness=255, | ||
| 1932 | TBlendType blendType=NOBLEND ); | ||
| 1933 | |||
| 1934 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1935 | CRGB ColorFromPalette( const CRGBPalette32& pal, | ||
| 1936 | uint8_t index, | ||
| 1937 | uint8_t brightness=255, | ||
| 1938 | TBlendType blendType=LINEARBLEND); | ||
| 1939 | |||
| 1940 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1941 | CRGB ColorFromPalette( const TProgmemRGBPalette32& pal, | ||
| 1942 | uint8_t index, | ||
| 1943 | uint8_t brightness=255, | ||
| 1944 | TBlendType blendType=LINEARBLEND); | ||
| 1945 | |||
| 1946 | /// @copydoc ColorFromPalette(const CRGBPalette16&, uint8_t, uint8_t, TBlendType) | ||
| 1947 | CHSV ColorFromPalette( const CHSVPalette32& pal, | ||
| 1948 | uint8_t index, | ||
| 1949 | uint8_t brightness=255, | ||
| 1950 | TBlendType blendType=LINEARBLEND); | ||
| 1951 | |||
| 1952 | |||
| 1953 | /// Fill a range of LEDs with a sequence of entries from a palette | ||
| 1954 | /// @tparam PALETTE the type of the palette used (auto-deduced) | ||
| 1955 | /// @param L pointer to the LED array to fill | ||
| 1956 | /// @param N number of LEDs to fill in the array | ||
| 1957 | /// @param startIndex the starting color index in the palette | ||
| 1958 | /// @param incIndex how much to increment the palette color index per LED | ||
| 1959 | /// @param pal the color palette to pull colors from | ||
| 1960 | /// @param brightness brightness value used to scale the resulting color | ||
| 1961 | /// @param blendType whether to take the palette entries directly (NOBLEND) | ||
| 1962 | /// or blend linearly between palette entries (LINEARBLEND) | ||
| 1963 | template <typename PALETTE> | ||
| 1964 | void fill_palette(CRGB* L, uint16_t N, uint8_t startIndex, uint8_t incIndex, | ||
| 1965 | const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND) | ||
| 1966 | { | ||
| 1967 | uint8_t colorIndex = startIndex; | ||
| 1968 | for( uint16_t i = 0; i < N; ++i) { | ||
| 1969 | L[i] = ColorFromPalette( pal, colorIndex, brightness, blendType); | ||
| 1970 | colorIndex += incIndex; | ||
| 1971 | } | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | |||
| 1975 | /// Fill a range of LEDs with a sequence of entries from a palette, so that | ||
| 1976 | /// the entire palette smoothly covers the range of LEDs. | ||
| 1977 | /// @tparam PALETTE the type of the palette used (auto-deduced) | ||
| 1978 | /// @param L pointer to the LED array to fill | ||
| 1979 | /// @param N number of LEDs to fill in the array | ||
| 1980 | /// @param startIndex the starting color index in the palette | ||
| 1981 | /// @param pal the color palette to pull colors from | ||
| 1982 | /// @param brightness brightness value used to scale the resulting color | ||
| 1983 | /// @param blendType whether to take the palette entries directly (NOBLEND) | ||
| 1984 | /// or blend linearly between palette entries (LINEARBLEND) | ||
| 1985 | /// @param reversed whether to progress through the palette backwards | ||
| 1986 | template <typename PALETTE> | ||
| 1987 | void fill_palette_circular(CRGB* L, uint16_t N, uint8_t startIndex, | ||
| 1988 | const PALETTE& pal, uint8_t brightness=255, TBlendType blendType=LINEARBLEND, | ||
| 1989 | bool reversed=false) | ||
| 1990 | { | ||
| 1991 | if (N == 0) return; // avoiding div/0 | ||
| 1992 | |||
| 1993 | const uint16_t colorChange = 65535 / N; // color change for each LED, * 256 for precision | ||
| 1994 | uint16_t colorIndex = ((uint16_t) startIndex) << 8; // offset for color index, with precision (*256) | ||
| 1995 | |||
| 1996 | for (uint16_t i = 0; i < N; ++i) { | ||
| 1997 | L[i] = ColorFromPalette(pal, (colorIndex >> 8), brightness, blendType); | ||
| 1998 | if (reversed) colorIndex -= colorChange; | ||
| 1999 | else colorIndex += colorChange; | ||
| 2000 | } | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | |||
| 2004 | /// Maps an array of palette color indexes into an array of LED colors. | ||
| 2005 | /// | ||
| 2006 | /// This function provides an easy way to create lightweight color patterns that | ||
| 2007 | /// can be deployed using any palette. | ||
| 2008 | /// | ||
| 2009 | /// @param dataArray the source array, containing color indexes for the palette | ||
| 2010 | /// @param dataCount the number of data elements in the array | ||
| 2011 | /// @param targetColorArray the LED array to store the resulting colors into. Must be | ||
| 2012 | /// at least as long as `dataCount`. | ||
| 2013 | /// @param pal the color palette to pull colors from | ||
| 2014 | /// @param brightness optional brightness value used to scale the resulting color | ||
| 2015 | /// @param opacity optional opacity value for the new color. If this is 255 (default), | ||
| 2016 | /// the new colors will be written to the array directly. Otherwise the existing LED | ||
| 2017 | /// data will be scaled down using `CRGB::nscale8_video()` and then new colors will | ||
| 2018 | /// be added on top. A higher value means that the new colors will be more visible. | ||
| 2019 | /// @param blendType whether to take the palette entries directly (NOBLEND) | ||
| 2020 | /// or blend linearly between palette entries (LINEARBLEND) | ||
| 2021 | template <typename PALETTE> | ||
| 2022 | void map_data_into_colors_through_palette( | ||
| 2023 | uint8_t *dataArray, uint16_t dataCount, | ||
| 2024 | CRGB* targetColorArray, | ||
| 2025 | const PALETTE& pal, | ||
| 2026 | uint8_t brightness=255, | ||
| 2027 | uint8_t opacity=255, | ||
| 2028 | TBlendType blendType=LINEARBLEND) | ||
| 2029 | { | ||
| 2030 | for( uint16_t i = 0; i < dataCount; ++i) { | ||
| 2031 | uint8_t d = dataArray[i]; | ||
| 2032 | CRGB rgb = ColorFromPalette( pal, d, brightness, blendType); | ||
| 2033 | if( opacity == 255 ) { | ||
| 2034 | targetColorArray[i] = rgb; | ||
| 2035 | } else { | ||
| 2036 | targetColorArray[i].nscale8( 256 - opacity); | ||
| 2037 | rgb.nscale8_video( opacity); | ||
| 2038 | targetColorArray[i] += rgb; | ||
| 2039 | } | ||
| 2040 | } | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | |||
| 2044 | /// Alter one palette by making it slightly more like a "target palette". | ||
| 2045 | /// Used for palette cross-fades. | ||
| 2046 | /// | ||
| 2047 | /// It does this by comparing each of the R, G, and B channels | ||
| 2048 | /// of each entry in the current palette to the corresponding | ||
| 2049 | /// entry in the target palette and making small adjustments: | ||
| 2050 | /// * If the CRGB::red channel is too low, it will be increased. | ||
| 2051 | /// * If the CRGB::red channel is too high, it will be slightly reduced. | ||
| 2052 | /// | ||
| 2053 | /// ...and so on and so forth for the CRGB::green and CRGB::blue channels. | ||
| 2054 | /// | ||
| 2055 | /// Additionally, there are two significant visual improvements | ||
| 2056 | /// to this algorithm implemented here. First is this: | ||
| 2057 | /// * When increasing a channel, it is stepped up by ONE. | ||
| 2058 | /// * When decreasing a channel, it is stepped down by TWO. | ||
| 2059 | /// | ||
| 2060 | /// Due to the way the eye perceives light, and the way colors | ||
| 2061 | /// are represented in RGB, this produces a more uniform apparent | ||
| 2062 | /// brightness when cross-fading between most palette colors. | ||
| 2063 | /// | ||
| 2064 | /// The second visual tweak is limiting the number of changes | ||
| 2065 | /// that will be made to the palette at once. If all the palette | ||
| 2066 | /// entries are changed at once, it can give a muddled appearance. | ||
| 2067 | /// However, if only a *few* palette entries are changed at once, | ||
| 2068 | /// you get a visually smoother transition: in the middle of the | ||
| 2069 | /// cross-fade your current palette will actually contain some | ||
| 2070 | /// colors from the old palette, a few blended colors, and some | ||
| 2071 | /// colors from the new palette. | ||
| 2072 | /// | ||
| 2073 | /// @param currentPalette the palette to modify | ||
| 2074 | /// @param targetPalette the palette to move towards | ||
| 2075 | /// @param maxChanges the maximum number of possible palette changes | ||
| 2076 | /// to make to the color channels per call. The limit is 48 (16 color | ||
| 2077 | /// entries times 3 channels each). The default is 24, meaning that | ||
| 2078 | /// only half of the palette entries can be changed per call. | ||
| 2079 | /// @warning The palette passed as `currentPalette` will be modified! Be sure | ||
| 2080 | /// to make a copy beforehand if needed. | ||
| 2081 | /// @todo Shouldn't the `targetPalette` be `const`? | ||
| 2082 | void nblendPaletteTowardPalette( CRGBPalette16& currentPalette, | ||
| 2083 | CRGBPalette16& targetPalette, | ||
| 2084 | uint8_t maxChanges=24); | ||
| 2085 | |||
| 2086 | /// @} PaletteColors | ||
| 2087 | |||
| 2088 | |||
| 2089 | /// Defines a static RGB palette very compactly using a series | ||
| 2090 | /// of connected color gradients. | ||
| 2091 | /// | ||
| 2092 | /// For example, if you want the first 3/4ths of the palette to be a slow | ||
| 2093 | /// gradient ramping from black to red, and then the remaining 1/4 of the | ||
| 2094 | /// palette to be a quicker ramp to white, you specify just three points: the | ||
| 2095 | /// starting black point (at index 0), the red midpoint (at index 192), | ||
| 2096 | /// and the final white point (at index 255). It looks like this: | ||
| 2097 | /// @code | ||
| 2098 | /// index: 0 192 255 | ||
| 2099 | /// |----------r-r-r-rrrrrrrrRrRrRrRrRRRR-|-RRWRWWRWWW-| | ||
| 2100 | /// color: (0,0,0) (255,0,0) (255,255,255) | ||
| 2101 | /// @endcode | ||
| 2102 | /// | ||
| 2103 | /// Here's how you'd define that gradient palette using this macro: | ||
| 2104 | /// @code{.cpp} | ||
| 2105 | /// DEFINE_GRADIENT_PALETTE( black_to_red_to_white_p ) { | ||
| 2106 | /// 0, 0, 0, 0, /* at index 0, black(0,0,0) */ | ||
| 2107 | /// 192, 255, 0, 0, /* at index 192, red(255,0,0) */ | ||
| 2108 | /// 255, 255, 255, 255 /* at index 255, white(255,255,255) */ | ||
| 2109 | /// }; | ||
| 2110 | /// @endcode | ||
| 2111 | /// | ||
| 2112 | /// This format is designed for compact storage. The example palette here | ||
| 2113 | /// takes up just 12 bytes of PROGMEM (flash) storage, and zero bytes | ||
| 2114 | /// of SRAM when not currently in use. | ||
| 2115 | /// | ||
| 2116 | /// To use one of these gradient palettes, simply assign it into a | ||
| 2117 | /// CRGBPalette16 or a CRGBPalette256, like this: | ||
| 2118 | /// @code{.cpp} | ||
| 2119 | /// CRGBPalette16 pal = black_to_red_to_white_p; | ||
| 2120 | /// @endcode | ||
| 2121 | /// | ||
| 2122 | /// When the assignment is made, the gradients are expanded out into | ||
| 2123 | /// either 16 or 256 palette entries, depending on the kind of palette | ||
| 2124 | /// object they're assigned to. | ||
| 2125 | /// | ||
| 2126 | /// @warning The last "index" position **MUST** be 255! Failure to end | ||
| 2127 | /// with index 255 will result in program hangs or crashes. | ||
| 2128 | /// @par | ||
| 2129 | /// @warning At this point, these gradient palette definitions **MUST** | ||
| 2130 | /// be stored in PROGMEM on AVR-based Arduinos. If you use the | ||
| 2131 | /// `DEFINE_GRADIENT_PALETTE` macro, this is taken of automatically. | ||
| 2132 | /// | ||
| 2133 | #define DEFINE_GRADIENT_PALETTE(X) \ | ||
| 2134 | FL_ALIGN_PROGMEM \ | ||
| 2135 | extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM = | ||
| 2136 | |||
| 2137 | /// Forward-declaration macro for DEFINE_GRADIENT_PALETTE(X) | ||
| 2138 | #define DECLARE_GRADIENT_PALETTE(X) \ | ||
| 2139 | FL_ALIGN_PROGMEM \ | ||
| 2140 | extern const TProgmemRGBGradientPalette_byte X[] FL_PROGMEM | ||
| 2141 | |||
| 2142 | /// @} ColorPalettes | ||
| 2143 | |||
| 2144 | |||
| 2145 | /// @defgroup GammaFuncs Gamma Adjustment Functions | ||
| 2146 | /// Functions for applying gamma adjustments to LED data. | ||
| 2147 | /// | ||
| 2148 | /// Gamma correction tries to compensate for the non-linear | ||
| 2149 | /// manner in which humans perceive light and color. Gamma | ||
| 2150 | /// correction is applied using the following expression: | ||
| 2151 | /// @code{.cpp} | ||
| 2152 | /// output = pow(input / 255.0, gamma) * 255.0; | ||
| 2153 | /// @endcode | ||
| 2154 | /// | ||
| 2155 | /// Larger gamma values result in darker images that have more contrast. | ||
| 2156 | /// Lower gamma values result in lighter images with less contrast. | ||
| 2157 | /// | ||
| 2158 | /// These functions apply either: | ||
| 2159 | /// * a single gamma adjustment to a single scalar value | ||
| 2160 | /// * a single gamma adjustment to each channel of a CRGB color, or | ||
| 2161 | /// * different gamma adjustments for each channel of a CRGB color | ||
| 2162 | /// | ||
| 2163 | /// Note that the gamma is specified as a traditional floating point value, | ||
| 2164 | /// e.g., "2.5", and as such these functions should not be called in | ||
| 2165 | /// your innermost pixel loops, or in animations that are extremely | ||
| 2166 | /// low on program storage space. Nevertheless, if you need these | ||
| 2167 | /// functions, here they are. | ||
| 2168 | /// | ||
| 2169 | /// Furthermore, bear in mind that CRGB LEDs have only eight bits | ||
| 2170 | /// per channel of color resolution, and that very small, subtle shadings | ||
| 2171 | /// may not be visible. | ||
| 2172 | /// | ||
| 2173 | /// @see @ref Dimming | ||
| 2174 | /// @see https://en.wikipedia.org/wiki/Gamma_correction | ||
| 2175 | /// @{ | ||
| 2176 | |||
| 2177 | /// Applies a gamma adjustment to a color channel | ||
| 2178 | /// @param brightness the value of the color data | ||
| 2179 | /// @param gamma the gamma value to apply | ||
| 2180 | /// @returns the color data, adjusted for gamma | ||
| 2181 | uint8_t applyGamma_video( uint8_t brightness, float gamma); | ||
| 2182 | |||
| 2183 | /// Applies a gamma adjustment to a color | ||
| 2184 | /// @param orig the color to apply an adjustment to | ||
| 2185 | /// @param gamma the gamma value to apply | ||
| 2186 | /// @returns copy of the CRGB object with gamma adjustment applied | ||
| 2187 | CRGB applyGamma_video( const CRGB& orig, float gamma); | ||
| 2188 | |||
| 2189 | /// Applies a gamma adjustment to a color | ||
| 2190 | /// @param orig the color to apply an adjustment to | ||
| 2191 | /// @param gammaR the gamma value to apply to the CRGB::red channel | ||
| 2192 | /// @param gammaG the gamma value to apply to the CRGB::green channel | ||
| 2193 | /// @param gammaB the gamma value to apply to the CRGB::blue channel | ||
| 2194 | /// @returns copy of the CRGB object with gamma adjustment applied | ||
| 2195 | CRGB applyGamma_video( const CRGB& orig, float gammaR, float gammaG, float gammaB); | ||
| 2196 | |||
| 2197 | |||
| 2198 | /// Destructively applies a gamma adjustment to a color | ||
| 2199 | /// @param rgb the color to apply an adjustment to (modified in place) | ||
| 2200 | /// @param gamma the gamma value to apply | ||
| 2201 | CRGB& napplyGamma_video( CRGB& rgb, float gamma); | ||
| 2202 | |||
| 2203 | /// Destructively applies a gamma adjustment to a color | ||
| 2204 | /// @param rgb the color to apply an adjustment to (modified in place) | ||
| 2205 | /// @param gammaR the gamma value to apply to the CRGB::red channel | ||
| 2206 | /// @param gammaG the gamma value to apply to the CRGB::green channel | ||
| 2207 | /// @param gammaB the gamma value to apply to the CRGB::blue channel | ||
| 2208 | CRGB& napplyGamma_video( CRGB& rgb, float gammaR, float gammaG, float gammaB); | ||
| 2209 | |||
| 2210 | /// Destructively applies a gamma adjustment to a color array | ||
| 2211 | /// @param rgbarray pointer to an LED array to apply an adjustment to (modified in place) | ||
| 2212 | /// @param count the number of LEDs to modify | ||
| 2213 | /// @param gamma the gamma value to apply | ||
| 2214 | void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gamma); | ||
| 2215 | |||
| 2216 | /// Destructively applies a gamma adjustment to a color array | ||
| 2217 | /// @param rgbarray pointer to an LED array to apply an adjustment to (modified in place) | ||
| 2218 | /// @param count the number of LEDs to modify | ||
| 2219 | /// @param gammaR the gamma value to apply to the CRGB::red channel | ||
| 2220 | /// @param gammaG the gamma value to apply to the CRGB::green channel | ||
| 2221 | /// @param gammaB the gamma value to apply to the CRGB::blue channel | ||
| 2222 | void napplyGamma_video( CRGB* rgbarray, uint16_t count, float gammaR, float gammaG, float gammaB); | ||
| 2223 | |||
| 2224 | /// @} GammaFuncs | ||
| 2225 | |||
| 2226 | FASTLED_NAMESPACE_END | ||
| 2227 | |||
| 2228 | #endif | ||
| 2229 |