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