2020-03-25 08:00:55 +00:00
# include "wled.h"
2018-11-01 14:36:13 +00:00
2020-03-31 00:38:08 +00:00
/*
2022-07-06 11:13:54 +00:00
* Color conversion & utility methods
2020-03-31 00:38:08 +00:00
*/
2022-07-06 11:13:54 +00:00
/*
* color blend function
*/
2022-08-03 19:36:47 +00:00
uint32_t color_blend ( uint32_t color1 , uint32_t color2 , uint16_t blend , bool b16 ) {
2024-08-05 14:42:21 +00:00
if ( blend = = 0 ) return color1 ;
2024-07-09 19:50:27 +00:00
unsigned blendmax = b16 ? 0xFFFF : 0xFF ;
2024-08-05 14:42:21 +00:00
if ( blend = = blendmax ) return color2 ;
unsigned shift = b16 ? 16 : 8 ;
2022-07-06 11:13:54 +00:00
uint32_t w1 = W ( color1 ) ;
uint32_t r1 = R ( color1 ) ;
uint32_t g1 = G ( color1 ) ;
uint32_t b1 = B ( color1 ) ;
uint32_t w2 = W ( color2 ) ;
uint32_t r2 = R ( color2 ) ;
uint32_t g2 = G ( color2 ) ;
uint32_t b2 = B ( color2 ) ;
uint32_t w3 = ( ( w2 * blend ) + ( w1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t r3 = ( ( r2 * blend ) + ( r1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t g3 = ( ( g2 * blend ) + ( g1 * ( blendmax - blend ) ) ) > > shift ;
uint32_t b3 = ( ( b2 * blend ) + ( b1 * ( blendmax - blend ) ) ) > > shift ;
return RGBW32 ( r3 , g3 , b3 , w3 ) ;
}
/*
* color add function that preserves ratio
2024-09-18 20:10:27 +00:00
* original idea : https : //github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
* heavily optimized for speed by @ dedehai
2022-07-06 11:13:54 +00:00
*/
2024-09-18 20:10:27 +00:00
uint32_t color_add ( uint32_t c1 , uint32_t c2 )
2022-07-06 11:13:54 +00:00
{
2024-09-11 15:14:59 +00:00
if ( c1 = = BLACK ) return c2 ;
if ( c2 = = BLACK ) return c1 ;
2024-09-18 20:10:27 +00:00
uint32_t rb = ( c1 & 0x00FF00FF ) + ( c2 & 0x00FF00FF ) ; // mask and add two colors at once
uint32_t wg = ( ( c1 > > 8 ) & 0x00FF00FF ) + ( ( c2 > > 8 ) & 0x00FF00FF ) ;
uint32_t r = rb > > 16 ; // extract single color values
uint32_t b = rb & 0xFFFF ;
2024-09-12 14:34:55 +00:00
uint32_t w = wg > > 16 ;
2024-09-18 20:10:27 +00:00
uint32_t g = wg & 0xFFFF ;
unsigned max = r ; // check for overflow note: not checking and just topping out at 255 (formerly 'fast') is not any faster (but even slower if not overflowing)
max = g > max ? g : max ;
max = b > max ? b : max ;
max = w > max ? w : max ;
if ( max > 255 ) {
uint32_t scale = ( uint32_t ( 255 ) < < 8 ) / max ; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead
rb = ( ( rb * scale ) > > 8 ) & 0x00FF00FF ; //
wg = ( wg * scale ) & 0xFF00FF00 ;
2023-09-10 16:52:14 +00:00
}
2024-09-18 20:10:27 +00:00
else wg = wg < < 8 ; //shift white and green back to correct position
return rb | wg ;
2023-09-10 16:52:14 +00:00
}
/*
* fades color toward black
* if using " video " method the resulting color will never become black unless it is already black
*/
2024-04-15 19:20:45 +00:00
2023-09-10 16:52:14 +00:00
uint32_t color_fade ( uint32_t c1 , uint8_t amount , bool video )
{
2024-09-12 04:43:20 +00:00
if ( c1 = = BLACK | | amount = = 0 ) return BLACK ;
2024-09-18 20:10:27 +00:00
if ( amount = = 255 ) return c1 ;
2024-04-16 08:43:06 +00:00
uint32_t scaledcolor ; // color order is: W R G B from MSB to LSB
2024-09-11 15:14:59 +00:00
uint32_t scale = amount ; // 32bit for faster calculation
2024-09-12 04:43:20 +00:00
uint32_t addRemains = 0 ;
2024-09-18 20:10:27 +00:00
if ( ! video ) scale + + ; // add one for correct scaling using bitshifts
2024-09-12 04:43:20 +00:00
else { // video scaling: make sure colors do not dim to zero if they started non-zero
2024-09-18 20:10:27 +00:00
addRemains = R ( c1 ) ? 0x00010000 : 0 ;
2024-09-12 04:43:20 +00:00
addRemains | = G ( c1 ) ? 0x00000100 : 0 ;
addRemains | = B ( c1 ) ? 0x00000001 : 0 ;
addRemains | = W ( c1 ) ? 0x01000000 : 0 ;
2023-09-10 16:52:14 +00:00
}
2024-09-12 04:43:20 +00:00
uint32_t rb = ( ( ( c1 & 0x00FF00FF ) * scale ) > > 8 ) & 0x00FF00FF ; // scale red and blue
uint32_t wg = ( ( ( c1 & 0xFF00FF00 ) > > 8 ) * scale ) & 0xFF00FF00 ; // scale white and green
2024-09-18 20:10:27 +00:00
scaledcolor = ( rb | wg ) + addRemains ;
2024-08-05 14:42:21 +00:00
return scaledcolor ;
2022-07-06 11:13:54 +00:00
}
2024-09-11 19:41:42 +00:00
// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes)
CRGB ColorFromPaletteWLED ( const CRGBPalette16 & pal , unsigned index , uint8_t brightness , TBlendType blendType )
{
2024-09-18 20:10:27 +00:00
if ( blendType = = LINEARBLEND_NOWRAP ) {
2024-09-11 19:41:42 +00:00
index = ( index * 240 ) > > 8 ; // Blend range is affected by lo4 blend of values, remap to avoid wrapping
}
unsigned hi4 = byte ( index ) > > 4 ;
2024-09-12 12:09:09 +00:00
const CRGB * entry = ( CRGB * ) ( ( uint8_t * ) ( & ( pal [ 0 ] ) ) + ( hi4 * sizeof ( CRGB ) ) ) ;
unsigned red1 = entry - > r ;
unsigned green1 = entry - > g ;
2024-09-18 20:10:27 +00:00
unsigned blue1 = entry - > b ;
2024-09-11 19:41:42 +00:00
if ( blendType ! = NOBLEND ) {
if ( hi4 = = 15 ) entry = & ( pal [ 0 ] ) ;
else + + entry ;
2024-09-12 12:09:09 +00:00
unsigned f2 = ( ( index & 0x0F ) < < 4 ) + 1 ; // +1 so we scale by 256 as a max value, then result can just be shifted by 8
2024-09-11 19:41:42 +00:00
unsigned f1 = ( 257 - f2 ) ; // f2 is 1 minimum, so this is 256 max
2024-09-18 20:10:27 +00:00
red1 = ( red1 * f1 + ( unsigned ) entry - > r * f2 ) > > 8 ;
green1 = ( green1 * f1 + ( unsigned ) entry - > g * f2 ) > > 8 ;
blue1 = ( blue1 * f1 + ( unsigned ) entry - > b * f2 ) > > 8 ;
2024-09-11 19:41:42 +00:00
}
2024-09-12 12:09:09 +00:00
if ( brightness < 255 ) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted
2024-09-18 20:10:27 +00:00
uint32_t scale = brightness + 1 ; // adjust for rounding (bitshift)
2024-09-11 19:41:42 +00:00
red1 = ( red1 * scale ) > > 8 ;
green1 = ( green1 * scale ) > > 8 ;
blue1 = ( blue1 * scale ) > > 8 ;
2024-09-18 20:10:27 +00:00
}
2024-09-11 19:41:42 +00:00
return CRGB ( ( uint8_t ) red1 , ( uint8_t ) green1 , ( uint8_t ) blue1 ) ;
}
2022-02-20 21:24:11 +00:00
void setRandomColor ( byte * rgb )
{
2023-09-10 16:52:14 +00:00
lastRandomIndex = get_random_wheel_index ( lastRandomIndex ) ;
2022-02-20 21:24:11 +00:00
colorHStoRGB ( lastRandomIndex * 256 , 255 , rgb ) ;
}
2024-02-06 10:06:23 +00:00
/*
* generates a random palette based on harmonic color theory
* takes a base palette as the input , it will choose one color of the base palette and keep it
2024-01-27 14:16:59 +00:00
*/
2024-02-06 10:06:23 +00:00
CRGBPalette16 generateHarmonicRandomPalette ( CRGBPalette16 & basepalette )
2024-01-27 14:16:59 +00:00
{
CHSV palettecolors [ 4 ] ; //array of colors for the new palette
uint8_t keepcolorposition = random8 ( 4 ) ; //color position of current random palette to keep
2024-01-28 13:19:46 +00:00
palettecolors [ keepcolorposition ] = rgb2hsv_approximate ( basepalette . entries [ keepcolorposition * 5 ] ) ; //read one of the base colors of the current palette
2024-01-30 21:28:40 +00:00
palettecolors [ keepcolorposition ] . hue + = random8 ( 10 ) - 5 ; // +/- 5 randomness of base color
2024-01-27 14:16:59 +00:00
//generate 4 saturation and brightness value numbers
//only one saturation is allowed to be below 200 creating mostly vibrant colors
2024-02-06 10:06:23 +00:00
//only one brightness value number is allowed below 200, creating mostly bright palettes
2024-01-27 14:16:59 +00:00
2024-01-30 21:28:40 +00:00
for ( int i = 0 ; i < 3 ; i + + ) { //generate three high values
palettecolors [ i ] . saturation = random8 ( 200 , 255 ) ;
palettecolors [ i ] . value = random8 ( 220 , 255 ) ;
2024-01-27 14:16:59 +00:00
}
//allow one to be lower
2024-02-01 19:06:48 +00:00
palettecolors [ 3 ] . saturation = random8 ( 20 , 255 ) ;
2024-01-30 21:28:40 +00:00
palettecolors [ 3 ] . value = random8 ( 80 , 255 ) ;
2024-01-27 14:16:59 +00:00
2024-02-06 10:06:23 +00:00
//shuffle the arrays
2024-01-27 14:16:59 +00:00
for ( int i = 3 ; i > 0 ; i - - ) {
2024-01-30 21:28:40 +00:00
std : : swap ( palettecolors [ i ] . saturation , palettecolors [ random8 ( i + 1 ) ] . saturation ) ;
std : : swap ( palettecolors [ i ] . value , palettecolors [ random8 ( i + 1 ) ] . value ) ;
2024-01-27 14:16:59 +00:00
}
//now generate three new hues based off of the hue of the chosen current color
uint8_t basehue = palettecolors [ keepcolorposition ] . hue ;
2024-01-28 18:22:36 +00:00
uint8_t harmonics [ 3 ] ; //hues that are harmonic but still a little random
2024-01-27 14:16:59 +00:00
uint8_t type = random8 ( 5 ) ; //choose a harmony type
switch ( type ) {
case 0 : // analogous
harmonics [ 0 ] = basehue + random8 ( 30 , 50 ) ;
harmonics [ 1 ] = basehue + random8 ( 10 , 30 ) ;
harmonics [ 2 ] = basehue - random8 ( 10 , 30 ) ;
break ;
case 1 : // triadic
2024-01-30 21:28:40 +00:00
harmonics [ 0 ] = basehue + 113 + random8 ( 15 ) ;
harmonics [ 1 ] = basehue + 233 + random8 ( 15 ) ;
2024-08-05 14:42:21 +00:00
harmonics [ 2 ] = basehue - 7 + random8 ( 15 ) ;
2024-01-27 14:16:59 +00:00
break ;
case 2 : // split-complementary
2024-01-30 21:28:40 +00:00
harmonics [ 0 ] = basehue + 145 + random8 ( 10 ) ;
harmonics [ 1 ] = basehue + 205 + random8 ( 10 ) ;
2024-08-05 14:42:21 +00:00
harmonics [ 2 ] = basehue - 5 + random8 ( 10 ) ;
2024-01-27 14:16:59 +00:00
break ;
2024-09-18 20:10:27 +00:00
2024-01-30 21:28:40 +00:00
case 3 : // square
2024-08-05 14:42:21 +00:00
harmonics [ 0 ] = basehue + 85 + random8 ( 10 ) ;
2024-01-30 21:28:40 +00:00
harmonics [ 1 ] = basehue + 175 + random8 ( 10 ) ;
harmonics [ 2 ] = basehue + 265 + random8 ( 10 ) ;
break ;
2024-02-06 10:06:23 +00:00
case 4 : // tetradic
2024-08-05 14:42:21 +00:00
harmonics [ 0 ] = basehue + 80 + random8 ( 20 ) ;
2024-01-27 14:16:59 +00:00
harmonics [ 1 ] = basehue + 170 + random8 ( 20 ) ;
2024-08-05 14:42:21 +00:00
harmonics [ 2 ] = basehue - 15 + random8 ( 30 ) ;
2024-01-30 21:28:40 +00:00
break ;
2024-01-27 14:16:59 +00:00
}
2024-02-06 10:06:23 +00:00
if ( random8 ( ) < 128 ) {
//50:50 chance of shuffling hues or keep the color order
2024-01-30 21:28:40 +00:00
for ( int i = 2 ; i > 0 ; i - - ) {
std : : swap ( harmonics [ i ] , harmonics [ random8 ( i + 1 ) ] ) ;
}
2024-01-27 14:16:59 +00:00
}
//now set the hues
2024-01-30 21:28:40 +00:00
int j = 0 ;
for ( int i = 0 ; i < 4 ; i + + ) {
2024-02-06 10:06:23 +00:00
if ( i = = keepcolorposition ) continue ; //skip the base color
2024-01-27 14:16:59 +00:00
palettecolors [ i ] . hue = harmonics [ j ] ;
j + + ;
2024-02-06 10:06:23 +00:00
}
2024-01-27 14:16:59 +00:00
2024-01-30 21:28:40 +00:00
bool makepastelpalette = false ;
2024-02-06 10:06:23 +00:00
if ( random8 ( ) < 25 ) { //~10% chance of desaturated 'pastel' colors
makepastelpalette = true ;
2024-01-30 21:28:40 +00:00
}
//apply saturation & gamma correction
2024-01-28 13:19:46 +00:00
CRGB RGBpalettecolors [ 4 ] ;
2024-01-30 21:28:40 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2024-09-18 20:10:27 +00:00
if ( makepastelpalette & & palettecolors [ i ] . saturation > 180 ) {
2024-01-30 21:28:40 +00:00
palettecolors [ i ] . saturation - = 160 ; //desaturate all four colors
2024-09-18 20:10:27 +00:00
}
2024-01-28 13:19:46 +00:00
RGBpalettecolors [ i ] = ( CRGB ) palettecolors [ i ] ; //convert to RGB
2024-02-06 10:06:23 +00:00
RGBpalettecolors [ i ] = gamma32 ( ( ( uint32_t ) RGBpalettecolors [ i ] ) & 0x00FFFFFFU ) ; //strip alpha from CRGB
2024-01-28 13:19:46 +00:00
}
2024-02-06 10:06:23 +00:00
return CRGBPalette16 ( RGBpalettecolors [ 0 ] ,
RGBpalettecolors [ 1 ] ,
RGBpalettecolors [ 2 ] ,
RGBpalettecolors [ 3 ] ) ;
2024-01-30 21:28:40 +00:00
}
2024-09-11 15:14:59 +00:00
CRGBPalette16 generateRandomPalette ( ) //generate fully random palette
2024-01-30 21:28:40 +00:00
{
2024-02-06 10:06:23 +00:00
return CRGBPalette16 ( CHSV ( random8 ( ) , random8 ( 160 , 255 ) , random8 ( 128 , 255 ) ) ,
2024-02-01 19:06:48 +00:00
CHSV ( random8 ( ) , random8 ( 160 , 255 ) , random8 ( 128 , 255 ) ) ,
CHSV ( random8 ( ) , random8 ( 160 , 255 ) , random8 ( 128 , 255 ) ) ,
CHSV ( random8 ( ) , random8 ( 160 , 255 ) , random8 ( 128 , 255 ) ) ) ;
2024-01-27 14:16:59 +00:00
}
2018-11-01 14:36:13 +00:00
void colorHStoRGB ( uint16_t hue , byte sat , byte * rgb ) //hue, sat to rgb
{
2024-06-30 08:44:25 +00:00
float h = ( ( float ) hue ) / 10922.5f ; // hue*6/65535
2023-03-19 10:24:59 +00:00
float s = ( ( float ) sat ) / 255.0f ;
2024-06-30 08:44:25 +00:00
int i = int ( h ) ;
float f = h - i ;
2023-03-19 10:24:59 +00:00
int p = int ( 255.0f * ( 1.0f - s ) ) ;
2024-06-30 08:44:25 +00:00
int q = int ( 255.0f * ( 1.0f - s * f ) ) ;
int t = int ( 255.0f * ( 1.0f - s * ( 1.0f - f ) ) ) ;
2023-03-19 10:24:59 +00:00
p = constrain ( p , 0 , 255 ) ;
q = constrain ( q , 0 , 255 ) ;
t = constrain ( t , 0 , 255 ) ;
2018-11-01 14:36:13 +00:00
switch ( i % 6 ) {
2023-03-19 10:24:59 +00:00
case 0 : rgb [ 0 ] = 255 , rgb [ 1 ] = t , rgb [ 2 ] = p ; break ;
case 1 : rgb [ 0 ] = q , rgb [ 1 ] = 255 , rgb [ 2 ] = p ; break ;
case 2 : rgb [ 0 ] = p , rgb [ 1 ] = 255 , rgb [ 2 ] = t ; break ;
case 3 : rgb [ 0 ] = p , rgb [ 1 ] = q , rgb [ 2 ] = 255 ; break ;
case 4 : rgb [ 0 ] = t , rgb [ 1 ] = p , rgb [ 2 ] = 255 ; break ;
case 5 : rgb [ 0 ] = 255 , rgb [ 1 ] = p , rgb [ 2 ] = q ; break ;
2018-11-01 14:36:13 +00:00
}
}
2021-10-16 13:13:30 +00:00
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
2020-09-27 09:43:28 +00:00
void colorKtoRGB ( uint16_t kelvin , byte * rgb ) //white spectrum to rgb, calc
{
2023-03-19 10:24:59 +00:00
int r = 0 , g = 0 , b = 0 ;
float temp = kelvin / 100.0f ;
if ( temp < = 66.0f ) {
2020-09-27 09:43:28 +00:00
r = 255 ;
2023-03-19 10:24:59 +00:00
g = roundf ( 99.4708025861f * logf ( temp ) - 161.1195681661f ) ;
if ( temp < = 19.0f ) {
2020-09-27 09:43:28 +00:00
b = 0 ;
} else {
2023-03-19 10:24:59 +00:00
b = roundf ( 138.5177312231f * logf ( ( temp - 10.0f ) ) - 305.0447927307f ) ;
2020-09-27 09:43:28 +00:00
}
} else {
2023-03-19 10:24:59 +00:00
r = roundf ( 329.698727446f * powf ( ( temp - 60.0f ) , - 0.1332047592f ) ) ;
g = roundf ( 288.1221695283f * powf ( ( temp - 60.0f ) , - 0.0755148492f ) ) ;
2020-09-27 09:43:28 +00:00
b = 255 ;
2023-01-06 08:24:29 +00:00
}
2021-11-21 22:46:44 +00:00
//g += 12; //mod by Aircoookie, a bit less accurate but visibly less pinkish
2020-09-27 09:43:28 +00:00
rgb [ 0 ] = ( uint8_t ) constrain ( r , 0 , 255 ) ;
rgb [ 1 ] = ( uint8_t ) constrain ( g , 0 , 255 ) ;
rgb [ 2 ] = ( uint8_t ) constrain ( b , 0 , 255 ) ;
rgb [ 3 ] = 0 ;
}
void colorCTtoRGB ( uint16_t mired , byte * rgb ) //white spectrum to rgb, bins
2018-01-14 23:20:23 +00:00
{
2018-02-27 23:27:10 +00:00
//this is only an approximation using WS2812B with gamma correction enabled
2018-11-09 16:00:36 +00:00
if ( mired > 475 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 255 ; rgb [ 1 ] = 199 ; rgb [ 2 ] = 92 ; //500
2018-11-09 16:00:36 +00:00
} else if ( mired > 425 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 255 ; rgb [ 1 ] = 213 ; rgb [ 2 ] = 118 ; //450
2018-11-09 16:00:36 +00:00
} else if ( mired > 375 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 255 ; rgb [ 1 ] = 216 ; rgb [ 2 ] = 118 ; //400
2018-11-09 16:00:36 +00:00
} else if ( mired > 325 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 255 ; rgb [ 1 ] = 234 ; rgb [ 2 ] = 140 ; //350
2018-11-09 16:00:36 +00:00
} else if ( mired > 275 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 255 ; rgb [ 1 ] = 243 ; rgb [ 2 ] = 160 ; //300
2018-11-09 16:00:36 +00:00
} else if ( mired > 225 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 250 ; rgb [ 1 ] = 255 ; rgb [ 2 ] = 188 ; //250
2018-11-09 16:00:36 +00:00
} else if ( mired > 175 ) {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 247 ; rgb [ 1 ] = 255 ; rgb [ 2 ] = 215 ; //200
2018-11-09 16:00:36 +00:00
} else {
2018-02-27 23:27:10 +00:00
rgb [ 0 ] = 237 ; rgb [ 1 ] = 255 ; rgb [ 2 ] = 239 ; //150
}
2018-01-14 23:20:23 +00:00
}
2020-03-24 23:59:48 +00:00
# ifndef WLED_DISABLE_HUESYNC
2018-03-14 12:16:28 +00:00
void colorXYtoRGB ( float x , float y , byte * rgb ) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
2018-01-14 23:20:23 +00:00
{
float z = 1.0f - x - y ;
float X = ( 1.0f / y ) * x ;
float Z = ( 1.0f / y ) * z ;
2018-02-27 23:27:10 +00:00
float r = ( int ) 255 * ( X * 1.656492f - 0.354851f - Z * 0.255038f ) ;
float g = ( int ) 255 * ( - X * 0.707196f + 1.655397f + Z * 0.036152f ) ;
float b = ( int ) 255 * ( X * 0.051713f - 0.121364f + Z * 1.011530f ) ;
if ( r > b & & r > g & & r > 1.0f ) {
2018-11-09 16:00:36 +00:00
// red is too big
g = g / r ;
b = b / r ;
r = 1.0f ;
2018-02-27 23:27:10 +00:00
} else if ( g > b & & g > r & & g > 1.0f ) {
2018-11-09 16:00:36 +00:00
// green is too big
r = r / g ;
b = b / g ;
g = 1.0f ;
2018-02-27 23:27:10 +00:00
} else if ( b > r & & b > g & & b > 1.0f ) {
2018-11-09 16:00:36 +00:00
// blue is too big
r = r / b ;
g = g / b ;
b = 1.0f ;
2018-02-27 23:27:10 +00:00
}
// Apply gamma correction
2023-03-19 10:24:59 +00:00
r = r < = 0.0031308f ? 12.92f * r : ( 1.0f + 0.055f ) * powf ( r , ( 1.0f / 2.4f ) ) - 0.055f ;
g = g < = 0.0031308f ? 12.92f * g : ( 1.0f + 0.055f ) * powf ( g , ( 1.0f / 2.4f ) ) - 0.055f ;
b = b < = 0.0031308f ? 12.92f * b : ( 1.0f + 0.055f ) * powf ( b , ( 1.0f / 2.4f ) ) - 0.055f ;
2018-02-27 23:27:10 +00:00
if ( r > b & & r > g ) {
2018-11-09 16:00:36 +00:00
// red is biggest
if ( r > 1.0f ) {
g = g / r ;
b = b / r ;
r = 1.0f ;
}
2018-03-06 22:47:08 +00:00
} else if ( g > b & & g > r ) {
2018-11-09 16:00:36 +00:00
// green is biggest
if ( g > 1.0f ) {
r = r / g ;
b = b / g ;
g = 1.0f ;
}
2018-03-06 22:47:08 +00:00
} else if ( b > r & & b > g ) {
2018-11-09 16:00:36 +00:00
// blue is biggest
if ( b > 1.0f ) {
r = r / b ;
g = g / b ;
b = 1.0f ;
}
2018-02-27 23:27:10 +00:00
}
2023-03-19 10:24:59 +00:00
rgb [ 0 ] = byte ( 255.0f * r ) ;
rgb [ 1 ] = byte ( 255.0f * g ) ;
rgb [ 2 ] = byte ( 255.0f * b ) ;
2018-01-14 23:20:23 +00:00
}
2018-11-01 14:36:13 +00:00
void colorRGBtoXY ( byte * rgb , float * xy ) //rgb to coordinates (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy)
{
float X = rgb [ 0 ] * 0.664511f + rgb [ 1 ] * 0.154324f + rgb [ 2 ] * 0.162028f ;
float Y = rgb [ 0 ] * 0.283881f + rgb [ 1 ] * 0.668433f + rgb [ 2 ] * 0.047685f ;
float Z = rgb [ 0 ] * 0.000088f + rgb [ 1 ] * 0.072310f + rgb [ 2 ] * 0.986039f ;
xy [ 0 ] = X / ( X + Y + Z ) ;
xy [ 1 ] = Y / ( X + Y + Z ) ;
}
2020-03-25 08:00:55 +00:00
# endif // WLED_DISABLE_HUESYNC
2020-11-19 23:33:17 +00:00
//RRGGBB / WWRRGGBB order for hex
2019-02-05 20:53:39 +00:00
void colorFromDecOrHexString ( byte * rgb , char * in )
2018-09-28 21:53:51 +00:00
{
if ( in [ 0 ] = = 0 ) return ;
char first = in [ 0 ] ;
uint32_t c = 0 ;
2023-01-06 08:24:29 +00:00
2018-09-28 21:53:51 +00:00
if ( first = = ' # ' | | first = = ' h ' | | first = = ' H ' ) //is HEX encoded
{
c = strtoul ( in + 1 , NULL , 16 ) ;
} else
{
c = strtoul ( in , NULL , 10 ) ;
}
2021-10-26 18:35:45 +00:00
rgb [ 0 ] = R ( c ) ;
rgb [ 1 ] = G ( c ) ;
rgb [ 2 ] = B ( c ) ;
rgb [ 3 ] = W ( c ) ;
2018-09-28 21:53:51 +00:00
}
2020-11-19 23:33:17 +00:00
//contrary to the colorFromDecOrHexString() function, this uses the more standard RRGGBB / RRGGBBWW order
bool colorFromHexString ( byte * rgb , const char * in ) {
if ( in = = nullptr ) return false ;
size_t inputSize = strnlen ( in , 9 ) ;
if ( inputSize ! = 6 & & inputSize ! = 8 ) return false ;
uint32_t c = strtoul ( in , NULL , 16 ) ;
if ( inputSize = = 6 ) {
2021-10-26 18:35:45 +00:00
rgb [ 0 ] = ( c > > 16 ) ;
rgb [ 1 ] = ( c > > 8 ) ;
rgb [ 2 ] = c ;
2020-11-19 23:33:17 +00:00
} else {
2021-10-26 18:35:45 +00:00
rgb [ 0 ] = ( c > > 24 ) ;
rgb [ 1 ] = ( c > > 16 ) ;
rgb [ 2 ] = ( c > > 8 ) ;
rgb [ 3 ] = c ;
2020-11-19 23:33:17 +00:00
}
return true ;
}
2024-08-05 14:42:21 +00:00
static inline float minf ( float v , float w )
2018-03-06 22:47:08 +00:00
{
if ( w > v ) return v ;
return w ;
}
2024-08-05 14:42:21 +00:00
static inline float maxf ( float v , float w )
2018-03-06 22:47:08 +00:00
{
if ( w > v ) return w ;
return v ;
}
2021-11-24 10:02:25 +00:00
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
2023-03-19 10:24:59 +00:00
// called from bus manager when color correction is enabled!
2021-10-16 13:13:30 +00:00
uint32_t colorBalanceFromKelvin ( uint16_t kelvin , uint32_t rgb )
{
2021-11-21 22:46:44 +00:00
//remember so that slow colorKtoRGB() doesn't have to run for every setPixelColor()
2023-03-19 10:24:59 +00:00
static byte correctionRGB [ 4 ] = { 0 , 0 , 0 , 0 } ;
static uint16_t lastKelvin = 0 ;
2021-11-21 22:46:44 +00:00
if ( lastKelvin ! = kelvin ) colorKtoRGB ( kelvin , correctionRGB ) ; // convert Kelvin to RGB
lastKelvin = kelvin ;
byte rgbw [ 4 ] ;
rgbw [ 0 ] = ( ( uint16_t ) correctionRGB [ 0 ] * R ( rgb ) ) / 255 ; // correct R
rgbw [ 1 ] = ( ( uint16_t ) correctionRGB [ 1 ] * G ( rgb ) ) / 255 ; // correct G
rgbw [ 2 ] = ( ( uint16_t ) correctionRGB [ 2 ] * B ( rgb ) ) / 255 ; // correct B
2021-11-24 10:02:25 +00:00
rgbw [ 3 ] = W ( rgb ) ;
2022-01-14 13:27:11 +00:00
return RGBW32 ( rgbw [ 0 ] , rgbw [ 1 ] , rgbw [ 2 ] , rgbw [ 3 ] ) ;
2021-10-16 13:13:30 +00:00
}
2021-11-24 10:02:25 +00:00
//approximates a Kelvin color temperature from an RGB color.
//this does no check for the "whiteness" of the color,
//so should be used combined with a saturation check (as done by auto-white)
//values from http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html (10deg)
//equation spreadsheet at https://bit.ly/30RkHaN
//accuracy +-50K from 1900K up to 8000K
//minimum returned: 1900K, maximum returned: 10091K (range of 8192)
uint16_t approximateKelvinFromRGB ( uint32_t rgb ) {
//if not either red or blue is 255, color is dimmed. Scale up
uint8_t r = R ( rgb ) , b = B ( rgb ) ;
if ( r = = b ) return 6550 ; //red == blue at about 6600K (also can't go further if both R and B are 0)
if ( r > b ) {
//scale blue up as if red was at 255
uint16_t scale = 0xFFFF / r ; //get scale factor (range 257-65535)
b = ( ( uint16_t ) b * scale ) > > 8 ;
//For all temps K<6600 R is bigger than B (for full bri colors R=255)
//-> Use 9 linear approximations for blackbody radiation blue values from 2000-6600K (blue is always 0 below 2000K)
if ( b < 33 ) return 1900 + b * 6 ;
if ( b < 72 ) return 2100 + ( b - 33 ) * 10 ;
if ( b < 101 ) return 2492 + ( b - 72 ) * 14 ;
if ( b < 132 ) return 2900 + ( b - 101 ) * 16 ;
if ( b < 159 ) return 3398 + ( b - 132 ) * 19 ;
if ( b < 186 ) return 3906 + ( b - 159 ) * 22 ;
if ( b < 210 ) return 4500 + ( b - 186 ) * 25 ;
if ( b < 230 ) return 5100 + ( b - 210 ) * 30 ;
return 5700 + ( b - 230 ) * 34 ;
} else {
//scale red up as if blue was at 255
uint16_t scale = 0xFFFF / b ; //get scale factor (range 257-65535)
r = ( ( uint16_t ) r * scale ) > > 8 ;
//For all temps K>6600 B is bigger than R (for full bri colors B=255)
//-> Use 2 linear approximations for blackbody radiation red values from 6600-10091K (blue is always 0 below 2000K)
if ( r > 225 ) return 6600 + ( 254 - r ) * 50 ;
uint16_t k = 8080 + ( 225 - r ) * 86 ;
return ( k > 10091 ) ? 10091 : k ;
}
2021-12-01 13:51:45 +00:00
}
2022-07-30 21:58:29 +00:00
//gamma 2.8 lookup table used for color correction
2024-09-14 12:10:46 +00:00
uint8_t NeoGammaWLEDMethod : : gammaT [ 256 ] ;
2022-07-30 21:58:29 +00:00
2022-11-13 11:13:49 +00:00
// re-calculates & fills gamma table
2023-06-10 18:43:27 +00:00
void NeoGammaWLEDMethod : : calcGammaTable ( float gamma )
2022-07-30 21:58:29 +00:00
{
2023-06-10 18:43:27 +00:00
for ( size_t i = 0 ; i < 256 ; i + + ) {
gammaT [ i ] = ( int ) ( powf ( ( float ) i / 255.0f , gamma ) * 255.0f + 0.5f ) ;
2022-07-30 21:58:29 +00:00
}
}
2024-09-12 06:30:46 +00:00
uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod : : Correct ( uint8_t value )
2022-07-30 21:58:29 +00:00
{
2023-06-10 18:43:27 +00:00
if ( ! gammaCorrectCol ) return value ;
return gammaT [ value ] ;
2022-07-30 21:58:29 +00:00
}
2022-11-13 11:13:49 +00:00
// used for color gamma correction
2024-09-12 06:30:46 +00:00
uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod : : Correct32 ( uint32_t color )
2022-07-30 21:58:29 +00:00
{
2022-09-04 18:17:05 +00:00
if ( ! gammaCorrectCol ) return color ;
2022-07-30 21:58:29 +00:00
uint8_t w = W ( color ) ;
uint8_t r = R ( color ) ;
uint8_t g = G ( color ) ;
uint8_t b = B ( color ) ;
w = gammaT [ w ] ;
r = gammaT [ r ] ;
g = gammaT [ g ] ;
b = gammaT [ b ] ;
return RGBW32 ( r , g , b , w ) ;
}