FastLED 3.9.15
Loading...
Searching...
No Matches
hsv16.cpp
Go to the documentation of this file.
1#include "fl/hsv16.h"
2#include "fl/math.h"
3
4#include "lib8tion/intmap.h"
5#include "fl/ease.h"
6
7namespace fl {
8
9// Improved 8-bit to 16-bit scaling using the same technique as map8_to_16
10// but with proper rounding for the 0-255 to 0-65535 conversion
11static inline u16 scale8_to_16_accurate(u8 x) {
12 if (x == 0) return 0;
13 if (x == 255) return 65535;
14 // Use 32-bit arithmetic with rounding: (x * 65535 + 127) / 255
15 // This is equivalent to: (x * 65535 + 255/2) / 255
16 return (u16)(((u32)x * 65535 + 127) / 255);
17}
18
19static HSV16 RGBtoHSV16(const CRGB &rgb) {
20 // Work with 8-bit values directly
21 u8 r = rgb.r;
22 u8 g = rgb.g;
23 u8 b = rgb.b;
24
25 // Find min and max
26 u8 mx = fl_max(r, fl_max(g, b));
27 u8 mn = fl_min(r, fl_min(g, b));
28 u8 delta = mx - mn;
29
30 u16 h = 0;
31 u16 s = 0;
32 u16 v = scale8_to_16_accurate(mx);
33
34 // Calculate saturation using improved scaling
35 if (mx > 0) {
36 // s = (delta * 65535) / mx, but with better accuracy
37 // Use the same technique as scale8_to_16_accurate but for arbitrary denominator
38 if (delta == mx) {
39 s = 65535; // Saturation is 100%
40 } else {
41 s = (u16)(((u32)delta * 65535 + (mx >> 1)) / mx);
42 }
43 }
44
45 // Calculate hue using improved algorithms
46 if (delta > 0) {
47 u32 hue_calc = 0;
48
49 if (mx == r) {
50 // Hue in red sector (0-60 degrees)
51 if (g >= b) {
52 // Use improved division: hue_calc = (g - b) * 65535 / (6 * delta)
53 u32 numerator = (u32)(g - b) * 65535;
54 if (delta <= 42) { // 6 * 42 = 252, safe for small delta
55 hue_calc = numerator / (6 * delta);
56 } else {
57 hue_calc = numerator / delta / 6; // Avoid overflow
58 }
59 } else {
60 u32 numerator = (u32)(b - g) * 65535;
61 if (delta <= 42) {
62 hue_calc = 65535 - numerator / (6 * delta);
63 } else {
64 hue_calc = 65535 - numerator / delta / 6;
65 }
66 }
67 } else if (mx == g) {
68 // Hue in green sector (60-180 degrees)
69 // Handle signed arithmetic properly to avoid integer underflow
70 i32 signed_diff = (i32)b - (i32)r;
71 u32 sector_offset = 65535 / 3; // 60 degrees (120 degrees in 16-bit space)
72
73 if (signed_diff >= 0) {
74 // Positive case: b >= r
75 u32 numerator = (u32)signed_diff * 65535;
76 if (delta <= 42) {
77 hue_calc = sector_offset + numerator / (6 * delta);
78 } else {
79 hue_calc = sector_offset + numerator / delta / 6;
80 }
81 } else {
82 // Negative case: b < r
83 u32 numerator = (u32)(-signed_diff) * 65535;
84 if (delta <= 42) {
85 hue_calc = sector_offset - numerator / (6 * delta);
86 } else {
87 hue_calc = sector_offset - numerator / delta / 6;
88 }
89 }
90 } else { // mx == b
91 // Hue in blue sector (180-300 degrees)
92 // Handle signed arithmetic properly to avoid integer underflow
93 i32 signed_diff = (i32)r - (i32)g;
94 u32 sector_offset = (2 * 65535) / 3; // 240 degrees (240 degrees in 16-bit space)
95
96 if (signed_diff >= 0) {
97 // Positive case: r >= g
98 u32 numerator = (u32)signed_diff * 65535;
99 if (delta <= 42) {
100 hue_calc = sector_offset + numerator / (6 * delta);
101 } else {
102 hue_calc = sector_offset + numerator / delta / 6;
103 }
104 } else {
105 // Negative case: r < g
106 u32 numerator = (u32)(-signed_diff) * 65535;
107 if (delta <= 42) {
108 hue_calc = sector_offset - numerator / (6 * delta);
109 } else {
110 hue_calc = sector_offset - numerator / delta / 6;
111 }
112 }
113 }
114
115 h = (u16)(hue_calc & 0xFFFF);
116 }
117
118 return HSV16{h, s, v};
119}
120
121static CRGB HSV16toRGB(const HSV16& hsv) {
122 // Convert 16-bit values to working range
123 u32 h = hsv.h;
124 u32 s = hsv.s;
125 u32 v = hsv.v;
126
127 if (s == 0) {
128 // Grayscale case - use precise mapping
129 u8 gray = map16_to_8(v);
130 return CRGB{gray, gray, gray};
131 }
132
133 // Determine which sector of the color wheel (0-5)
134 u32 sector = (h * 6) / 65536;
135 u32 sector_pos = (h * 6) % 65536; // Position within sector (0-65535)
136
137 // Calculate intermediate values using precise mapping
138 // c = v * s / 65536, with proper rounding
139 u32 c = map32_to_16(v * s);
140
141 // Calculate x = c * (1 - |2*(sector_pos/65536) - 1|)
142 u32 x;
143 if (sector & 1) {
144 // For odd sectors (1, 3, 5), we want decreasing values
145 // x = c * (65535 - sector_pos) / 65535
146 x = map32_to_16(c * (65535 - sector_pos));
147 } else {
148 // For even sectors (0, 2, 4), we want increasing values
149 // x = c * sector_pos / 65535
150 x = map32_to_16(c * sector_pos);
151 }
152
153 u32 m = v - c;
154
155 u32 r1, g1, b1;
156 switch (sector) {
157 case 0: r1 = c; g1 = x; b1 = 0; break;
158 case 1: r1 = x; g1 = c; b1 = 0; break;
159 case 2: r1 = 0; g1 = c; b1 = x; break;
160 case 3: r1 = 0; g1 = x; b1 = c; break;
161 case 4: r1 = x; g1 = 0; b1 = c; break;
162 default: r1 = c; g1 = 0; b1 = x; break;
163 }
164
165 // Add baseline and scale to 8-bit using accurate mapping
166 u8 R = map16_to_8(u16(r1 + m));
167 u8 G = map16_to_8(u16(g1 + m));
168 u8 B = map16_to_8(u16(b1 + m));
169
170 return CRGB{R, G, B};
171}
172
173HSV16::HSV16(const CRGB& rgb) {
174 *this = RGBtoHSV16(rgb);
175}
176
178 return HSV16toRGB(*this);
179}
180
181CRGB HSV16::colorBoost(EaseType saturation_function, EaseType luminance_function) const {
182 HSV16 hsv = *this;
183
184 if (saturation_function != EASE_NONE) {
185 u16 inv_sat = 65535 - hsv.s;
186 inv_sat = ease16(saturation_function, inv_sat);
187 hsv.s = (65535 - inv_sat);
188 }
189
190 if (luminance_function != EASE_NONE) {
191 hsv.v = ease16(luminance_function, hsv.v);
192 }
193
194 return hsv.ToRGB();
195}
196
197} // namespace fl
int x
Definition simple.h:92
LIB8STATIC_ALWAYS_INLINE uint8_t map16_to_8(uint16_t x)
Definition intmap.h:35
LIB8STATIC_ALWAYS_INLINE uint16_t map32_to_16(uint32_t x)
Definition intmap.h:47
Defines integer mapping functions.
unsigned char u8
Definition int.h:17
static CRGB HSV16toRGB(const HSV16 &hsv)
Definition hsv16.cpp:121
u16 ease16(EaseType type, u16 i)
Definition ease.h:126
FL_DISABLE_WARNING_PUSH U common_type_t< T, U > fl_min(T a, U b)
Definition math_macros.h:25
common_type_t< T, U > fl_max(T a, U b)
Definition math_macros.h:29
static HSV16 RGBtoHSV16(const CRGB &rgb)
Definition hsv16.cpp:19
static u16 scale8_to_16_accurate(u8 x)
Definition hsv16.cpp:11
EaseType
Definition ease.h:21
@ EASE_NONE
Definition ease.h:22
IMPORTANT!
Definition crgb.h:20
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:86
CRGB colorBoost(EaseType saturation_function=EASE_IN_QUAD, EaseType luminance_function=EASE_NONE) const
Definition hsv16.cpp:181
HSV16()=default
u16 v
Definition hsv16.h:13
CRGB ToRGB() const
Definition hsv16.cpp:177
u16 h
Definition hsv16.h:11
u16 s
Definition hsv16.h:12