mirror of
https://github.com/wled/WLED.git
synced 2025-07-19 16:56:35 +00:00
Added HSV2RGB and RGB2HSV functions for higher accuracy conversions
- also added a struct to handle HSV with 16bit hue better (including some conversions, can be extended easily) - the functions are optimized for speed and flash use. They are faster and more accurate than what fastled offers (and use much less flash). - replaced colorHStoRGB() with a call to the new hsv2rgb() function, saving even more flash (new function is untested!) - the 16bit hue calculations result in an almost perfect conversion from RGB to HSV and back, the maximum error was 1/255 in the cases I tested.
This commit is contained in:
parent
906f8fc2e7
commit
bef1ac2668
@ -239,26 +239,64 @@ CRGBPalette16 generateRandomPalette() //generate fully random palette
|
|||||||
CHSV(random8(), random8(160, 255), random8(128, 255)));
|
CHSV(random8(), random8(160, 255), random8(128, 255)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
|
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0)
|
||||||
{
|
{
|
||||||
float h = ((float)hue)/10922.5f; // hue*6/65535
|
unsigned int remainder, region, p, q, t;
|
||||||
float s = ((float)sat)/255.0f;
|
unsigned int h = hsv.h;
|
||||||
int i = int(h);
|
unsigned int s = hsv.s;
|
||||||
float f = h - i;
|
unsigned int v = hsv.v;
|
||||||
int p = int(255.0f * (1.0f-s));
|
if (s == 0) {
|
||||||
int q = int(255.0f * (1.0f-s*f));
|
rgb = v << 16 | v << 8 | v;
|
||||||
int t = int(255.0f * (1.0f-s*(1.0f-f)));
|
return;
|
||||||
p = constrain(p, 0, 255);
|
|
||||||
q = constrain(q, 0, 255);
|
|
||||||
t = constrain(t, 0, 255);
|
|
||||||
switch (i%6) {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
region = h / 10923; // 65536 / 6 = 10923
|
||||||
|
remainder = (h - (region * 10923)) * 6;
|
||||||
|
p = (v * (256 - s)) >> 8;
|
||||||
|
q = (v * (255 - ((s * remainder) >> 16))) >> 8;
|
||||||
|
t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8;
|
||||||
|
switch (region) {
|
||||||
|
case 0:
|
||||||
|
rgb = v << 16 | t << 8 | p; break;
|
||||||
|
case 1:
|
||||||
|
rgb = q << 16 | v << 8 | p; break;
|
||||||
|
case 2:
|
||||||
|
rgb = p << 16 | v << 8 | t; break;
|
||||||
|
case 3:
|
||||||
|
rgb = p << 16 | q << 8 | v; break;
|
||||||
|
case 4:
|
||||||
|
rgb = t << 16 | p << 8 | v; break;
|
||||||
|
default:
|
||||||
|
rgb = v << 16 | p << 8 | q; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version
|
||||||
|
{
|
||||||
|
hsv.raw = 0;
|
||||||
|
int32_t r = (rgb>>16)&0xFF;
|
||||||
|
int32_t g = (rgb>>8)&0xFF;
|
||||||
|
int32_t b = rgb&0xFF;
|
||||||
|
int32_t minval, maxval, delta;
|
||||||
|
minval = min(r, g);
|
||||||
|
minval = min(minval, b);
|
||||||
|
maxval = max(r, g);
|
||||||
|
maxval = max(maxval, b);
|
||||||
|
if (maxval == 0) return; // black
|
||||||
|
hsv.v = maxval;
|
||||||
|
delta = maxval - minval;
|
||||||
|
hsv.s = (255 * delta) / maxval;
|
||||||
|
if (hsv.s == 0) return; // gray value
|
||||||
|
if (maxval == r) hsv.h = (10923 * (g - b)) / delta;
|
||||||
|
else if (maxval == g) hsv.h = 21845 + (10923 * (b - r)) / delta;
|
||||||
|
else hsv.h = 43690 + (10923 * (r - g)) / delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb
|
||||||
|
uint32_t crgb;
|
||||||
|
hsv2rgb(CHSV32(hue, sat, 255), crgb);
|
||||||
|
rgb[0] = byte((crgb) >> 16);
|
||||||
|
rgb[1] = byte((crgb) >> 8);
|
||||||
|
rgb[2] = byte(crgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
|
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
|
||||||
|
@ -67,6 +67,30 @@ typedef struct WiFiConfig {
|
|||||||
|
|
||||||
//colors.cpp
|
//colors.cpp
|
||||||
#define ColorFromPalette ColorFromPaletteWLED // override fastled version
|
#define ColorFromPalette ColorFromPaletteWLED // override fastled version
|
||||||
|
|
||||||
|
struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t h; // hue
|
||||||
|
uint8_t s; // saturation
|
||||||
|
uint8_t v; // value
|
||||||
|
};
|
||||||
|
uint32_t raw; // 32bit access
|
||||||
|
};
|
||||||
|
inline CHSV32() __attribute__((always_inline)) = default; // default constructor
|
||||||
|
|
||||||
|
/// Allow construction from hue, saturation, and value
|
||||||
|
/// @param ih input hue
|
||||||
|
/// @param is input saturation
|
||||||
|
/// @param iv input value
|
||||||
|
inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v
|
||||||
|
: h(ih), s(is), v(iv) {}
|
||||||
|
inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v
|
||||||
|
: h((uint16_t)ih << 8), s(is), v(iv) {}
|
||||||
|
inline CHSV32(const CHSV& chsv) __attribute__((always_inline)) // constructor from CHSV
|
||||||
|
: h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {}
|
||||||
|
inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV
|
||||||
|
};
|
||||||
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
|
// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod)
|
||||||
class NeoGammaWLEDMethod {
|
class NeoGammaWLEDMethod {
|
||||||
public:
|
public:
|
||||||
@ -86,7 +110,10 @@ class NeoGammaWLEDMethod {
|
|||||||
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette);
|
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette);
|
||||||
CRGBPalette16 generateRandomPalette();
|
CRGBPalette16 generateRandomPalette();
|
||||||
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
|
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
|
||||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
|
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb);
|
||||||
|
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb);
|
||||||
|
void rgb2hsv(const uint32_t rgb, CHSV32& hsv);
|
||||||
|
inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv
|
||||||
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
||||||
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
|
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
|
||||||
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
|
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
|
||||||
|
@ -129,7 +129,7 @@ static void changeEffectSpeed(int8_t amount)
|
|||||||
} else { // if Effect == "solid Color", change the hue of the primary color
|
} else { // if Effect == "solid Color", change the hue of the primary color
|
||||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
CHSV prim_hsv = rgb2hsv(fastled_col);
|
||||||
int16_t new_val = (int16_t)prim_hsv.h + amount;
|
int16_t new_val = (int16_t)prim_hsv.h + amount;
|
||||||
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
|
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
|
||||||
if (new_val < 0) new_val += 255; // roll-over if smaller than 0
|
if (new_val < 0) new_val += 255; // roll-over if smaller than 0
|
||||||
@ -173,7 +173,7 @@ static void changeEffectIntensity(int8_t amount)
|
|||||||
} else { // if Effect == "solid Color", change the saturation of the primary color
|
} else { // if Effect == "solid Color", change the saturation of the primary color
|
||||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
CHSV prim_hsv = rgb2hsv(fastled_col);
|
||||||
int16_t new_val = (int16_t) prim_hsv.s + amount;
|
int16_t new_val = (int16_t) prim_hsv.s + amount;
|
||||||
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
|
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
|
||||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user