GCC Code Coverage Report


Directory: ./
File: overlays/wled/hsv2rgb.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 76 191 39.8%
Functions: 1 8 12.5%
Branches: 37 120 30.8%

Line Branch Exec Source
1 /// @file hsv2rgb.cpp
2 /// Functions to convert from the HSV colorspace to the RGB colorspace
3
4 /// Disables pragma messages and warnings
5 #define FASTLED_INTERNAL
6 #include <stdint.h>
7
8 #include "FastLED.h"
9
10 FASTLED_NAMESPACE_BEGIN
11
12 /// HSV to RGB implementation in raw C, platform independent
13 void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb);
14 /// HSV to RGB implementation in raw C, for the AVR platform only
15 void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb);
16
17 #if defined(__AVR__) && !defined( LIB8_ATTINY )
18 void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
19 {
20 hsv2rgb_raw_avr( hsv, rgb);
21 }
22 #else
23 ✗ void hsv2rgb_raw(const struct CHSV & hsv, struct CRGB & rgb)
24 {
25 ✗ hsv2rgb_raw_C( hsv, rgb);
26 ✗ }
27 #endif
28
29
30 /// Apply dimming compensation to values
31 #define APPLY_DIMMING(X) (X)
32
33 /// Divide the color wheel into eight sections, 32 elements each
34 /// @todo Unused. Remove?
35 #define HSV_SECTION_6 (0x20)
36
37 /// Divide the color wheel into four sections, 64 elements each
38 /// @todo I believe this is mis-named, and should be HSV_SECTION_4
39 #define HSV_SECTION_3 (0x40)
40
41 ✗ void hsv2rgb_raw_C (const struct CHSV & hsv, struct CRGB & rgb)
42 {
43 // Convert hue, saturation and brightness ( HSV/HSB ) to RGB
44 // "Dimming" is used on saturation and brightness to make
45 // the output more visually linear.
46
47 // Apply dimming curves
48 ✗ uint8_t value = APPLY_DIMMING( hsv.val); // cppcheck-suppress selfAssignment
49 ✗ uint8_t saturation = hsv.sat;
50
51 // The brightness floor is minimum number that all of
52 // R, G, and B will be set to.
53 ✗ uint8_t invsat = APPLY_DIMMING( 255 - saturation); // cppcheck-suppress selfAssignment
54 ✗ uint8_t brightness_floor = (value * invsat) / 256;
55
56 // The color amplitude is the maximum amount of R, G, and B
57 // that will be added on top of the brightness_floor to
58 // create the specific hue desired.
59 ✗ uint8_t color_amplitude = value - brightness_floor;
60
61 // Figure out which section of the hue wheel we're in,
62 // and how far offset we are withing that section
63 ✗ uint8_t section = hsv.hue / HSV_SECTION_3; // 0..2
64 ✗ uint8_t offset = hsv.hue % HSV_SECTION_3; // 0..63
65
66 ✗ uint8_t rampup = offset; // 0..63
67 ✗ uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0
68
69 // We now scale rampup and rampdown to a 0-255 range -- at least
70 // in theory, but here's where architecture-specific decsions
71 // come in to play:
72 // To scale them up to 0-255, we'd want to multiply by 4.
73 // But in the very next step, we multiply the ramps by other
74 // values and then divide the resulting product by 256.
75 // So which is faster?
76 // ((ramp * 4) * othervalue) / 256
77 // or
78 // ((ramp ) * othervalue) / 64
79 // It depends on your processor architecture.
80 // On 8-bit AVR, the "/ 256" is just a one-cycle register move,
81 // but the "/ 64" might be a multicycle shift process. So on AVR
82 // it's faster do multiply the ramp values by four, and then
83 // divide by 256.
84 // On ARM, the "/ 256" and "/ 64" are one cycle each, so it's
85 // faster to NOT multiply the ramp values by four, and just to
86 // divide the resulting product by 64 (instead of 256).
87 // Moral of the story: trust your profiler, not your insticts.
88
89 // Since there's an AVR assembly version elsewhere, we'll
90 // assume what we're on an architecture where any number of
91 // bit shifts has roughly the same cost, and we'll remove the
92 // redundant math at the source level:
93
94 // // scale up to 255 range
95 // //rampup *= 4; // 0..252
96 // //rampdown *= 4; // 0..252
97
98 // compute color-amplitude-scaled-down versions of rampup and rampdown
99 ✗ uint8_t rampup_amp_adj = (rampup * color_amplitude) / (256 / 4);
100 ✗ uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4);
101
102 // add brightness_floor offset to everything
103 ✗ uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
104 ✗ uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
105
106
107 ✗ if( section ) {
108 ✗ if( section == 1) {
109 // section 1: 0x40..0x7F
110 ✗ rgb.r = brightness_floor;
111 ✗ rgb.g = rampdown_adj_with_floor;
112 ✗ rgb.b = rampup_adj_with_floor;
113 } else {
114 // section 2; 0x80..0xBF
115 ✗ rgb.r = rampup_adj_with_floor;
116 ✗ rgb.g = brightness_floor;
117 ✗ rgb.b = rampdown_adj_with_floor;
118 }
119 } else {
120 // section 0: 0x00..0x3F
121 ✗ rgb.r = rampdown_adj_with_floor;
122 ✗ rgb.g = rampup_adj_with_floor;
123 ✗ rgb.b = brightness_floor;
124 }
125 ✗ }
126
127
128
129 #if defined(__AVR__) && !defined( LIB8_ATTINY )
130 void hsv2rgb_raw_avr(const struct CHSV & hsv, struct CRGB & rgb)
131 {
132 uint8_t hue, saturation, value;
133
134 hue = hsv.hue;
135 saturation = hsv.sat;
136 value = hsv.val;
137
138 // Saturation more useful the other way around
139 saturation = 255 - saturation;
140 uint8_t invsat = APPLY_DIMMING( saturation ); // cppcheck-suppress selfAssignment
141
142 // Apply dimming curves
143 value = APPLY_DIMMING( value ); // cppcheck-suppress selfAssignment
144
145 // The brightness floor is minimum number that all of
146 // R, G, and B will be set to, which is value * invsat
147 uint8_t brightness_floor;
148
149 asm volatile(
150 "mul %[value], %[invsat] \n"
151 "mov %[brightness_floor], r1 \n"
152 : [brightness_floor] "=r" (brightness_floor)
153 : [value] "r" (value),
154 [invsat] "r" (invsat)
155 : "r0", "r1"
156 );
157
158 // The color amplitude is the maximum amount of R, G, and B
159 // that will be added on top of the brightness_floor to
160 // create the specific hue desired.
161 uint8_t color_amplitude = value - brightness_floor;
162
163 // Figure how far we are offset into the section of the
164 // color wheel that we're in
165 uint8_t offset = hsv.hue & (HSV_SECTION_3 - 1); // 0..63
166 uint8_t rampup = offset * 4; // 0..252
167
168
169 // compute color-amplitude-scaled-down versions of rampup and rampdown
170 uint8_t rampup_amp_adj;
171 uint8_t rampdown_amp_adj;
172
173 asm volatile(
174 "mul %[rampup], %[color_amplitude] \n"
175 "mov %[rampup_amp_adj], r1 \n"
176 "com %[rampup] \n"
177 "mul %[rampup], %[color_amplitude] \n"
178 "mov %[rampdown_amp_adj], r1 \n"
179 : [rampup_amp_adj] "=&r" (rampup_amp_adj),
180 [rampdown_amp_adj] "=&r" (rampdown_amp_adj),
181 [rampup] "+r" (rampup)
182 : [color_amplitude] "r" (color_amplitude)
183 : "r0", "r1"
184 );
185
186
187 // add brightness_floor offset to everything
188 uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
189 uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
190
191
192 // keep gcc from using "X" as the index register for storing
193 // results back in the return structure. AVR's X register can't
194 // do "std X+q, rnn", but the Y and Z registers can.
195 // if the pointer to 'rgb' is in X, gcc will add all kinds of crazy
196 // extra instructions. Simply killing X here seems to help it
197 // try Y or Z first.
198 asm volatile( "" : : : "r26", "r27" );
199
200
201 if( hue & 0x80 ) {
202 // section 2: 0x80..0xBF
203 rgb.r = rampup_adj_with_floor;
204 rgb.g = brightness_floor;
205 rgb.b = rampdown_adj_with_floor;
206 } else {
207 if( hue & 0x40) {
208 // section 1: 0x40..0x7F
209 rgb.r = brightness_floor;
210 rgb.g = rampdown_adj_with_floor;
211 rgb.b = rampup_adj_with_floor;
212 } else {
213 // section 0: 0x00..0x3F
214 rgb.r = rampdown_adj_with_floor;
215 rgb.g = rampup_adj_with_floor;
216 rgb.b = brightness_floor;
217 }
218 }
219
220 cleanup_R1();
221 }
222 // End of AVR asm implementation
223
224 #endif
225
226 ✗ void hsv2rgb_spectrum( const struct CHSV& hsv, CRGB& rgb)
227 {
228 ✗ CHSV hsv2(hsv);
229 ✗ hsv2.hue = scale8( hsv2.hue, 191);
230 ✗ hsv2rgb_raw(hsv2, rgb);
231 ✗ }
232
233
234 /// Force a variable reference to avoid compiler over-optimization.
235 /// Sometimes the compiler will do clever things to reduce
236 /// code size that result in a net slowdown, if it thinks that
237 /// a variable is not used in a certain location.
238 /// This macro does its best to convince the compiler that
239 /// the variable is used in this location, to help control
240 /// code motion and de-duplication that would result in a slowdown.
241 #define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
242
243
244 /// @cond
245 #define K255 255
246 #define K171 171
247 #define K170 170
248 #define K85 85
249 /// @endcond
250
251 115369 void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
252 {
253 // Yellow has a higher inherent brightness than
254 // any other color; 'pure' yellow is perceived to
255 // be 93% as bright as white. In order to make
256 // yellow appear the correct relative brightness,
257 // it has to be rendered brighter than all other
258 // colors.
259 // Level Y1 is a moderate boost, the default.
260 // Level Y2 is a strong boost.
261 115369 const uint8_t Y1 = 1;
262 115369 const uint8_t Y2 = 0;
263
264 // G2: Whether to divide all greens by two.
265 // Depends GREATLY on your particular LEDs
266 115369 const uint8_t G2 = 0;
267
268 // Gscale: what to scale green down by.
269 // Depends GREATLY on your particular LEDs
270 115369 const uint8_t Gscale = 0;
271
272
273 115369 uint8_t hue = hsv.hue;
274 115369 uint8_t sat = hsv.sat;
275 115369 uint8_t val = hsv.val;
276
277 115369 uint8_t offset = hue & 0x1F; // 0..31
278
279 // offset8 = offset * 8
280 115369 uint8_t offset8 = offset;
281 {
282 #if defined(__AVR__)
283 // Left to its own devices, gcc turns "x <<= 3" into a loop
284 // It's much faster and smaller to just do three single-bit shifts
285 // So this business is to force that.
286 offset8 <<= 1;
287 asm volatile("");
288 offset8 <<= 1;
289 asm volatile("");
290 offset8 <<= 1;
291 #else
292 // On ARM and other non-AVR platforms, we just shift 3.
293 115369 offset8 <<= 3;
294 #endif
295 }
296
297 115369 uint8_t third = scale8( offset8, (256 / 3)); // max = 85
298
299 uint8_t r, g, b;
300
301
2/2
✓ Branch 0 taken 50202 times.
✓ Branch 1 taken 65167 times.
115369 if( ! (hue & 0x80) ) {
302 // 0XX
303
2/2
✓ Branch 0 taken 33836 times.
✓ Branch 1 taken 16366 times.
50202 if( ! (hue & 0x40) ) {
304 // 00X
305 //section 0-1
306
2/2
✓ Branch 0 taken 19444 times.
✓ Branch 1 taken 14392 times.
33836 if( ! (hue & 0x20) ) {
307 // 000
308 //case 0: // R -> O
309 19444 r = K255 - third;
310 19444 g = third;
311 19444 b = 0;
312 19444 FORCE_REFERENCE(b);
313 } else {
314 // 001
315 //case 1: // O -> Y
316 if( Y1 ) {
317 14392 r = K171;
318 14392 g = K85 + third ;
319 14392 b = 0;
320 14392 FORCE_REFERENCE(b);
321 }
322 if( Y2 ) {
323 r = K170 + third;
324 //uint8_t twothirds = (third << 1);
325 uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
326 g = K85 + twothirds;
327 b = 0;
328 FORCE_REFERENCE(b);
329 }
330 }
331 } else {
332 //01X
333 // section 2-3
334
2/2
✓ Branch 0 taken 11215 times.
✓ Branch 1 taken 5151 times.
16366 if( ! (hue & 0x20) ) {
335 // 010
336 //case 2: // Y -> G
337 if( Y1 ) {
338 //uint8_t twothirds = (third << 1);
339 11215 uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
340 11215 r = K171 - twothirds;
341 11215 g = K170 + third;
342 11215 b = 0;
343 11215 FORCE_REFERENCE(b);
344 }
345 if( Y2 ) {
346 r = K255 - offset8;
347 g = K255;
348 b = 0;
349 FORCE_REFERENCE(b);
350 }
351 } else {
352 // 011
353 // case 3: // G -> A
354 5151 r = 0;
355 5151 FORCE_REFERENCE(r);
356 5151 g = K255 - third;
357 5151 b = third;
358 }
359 }
360 } else {
361 // section 4-7
362 // 1XX
363
2/2
✓ Branch 0 taken 20189 times.
✓ Branch 1 taken 44978 times.
65167 if( ! (hue & 0x40) ) {
364 // 10X
365
2/2
✓ Branch 0 taken 5616 times.
✓ Branch 1 taken 14573 times.
20189 if( ! ( hue & 0x20) ) {
366 // 100
367 //case 4: // A -> B
368 5616 r = 0;
369 5616 FORCE_REFERENCE(r);
370 //uint8_t twothirds = (third << 1);
371 5616 uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
372 5616 g = K171 - twothirds; //K170?
373 5616 b = K85 + twothirds;
374
375 } else {
376 // 101
377 //case 5: // B -> P
378 14573 r = third;
379 14573 g = 0;
380 14573 FORCE_REFERENCE(g);
381 14573 b = K255 - third;
382
383 }
384 } else {
385
2/2
✓ Branch 0 taken 28641 times.
✓ Branch 1 taken 16337 times.
44978 if( ! (hue & 0x20) ) {
386 // 110
387 //case 6: // P -- K
388 28641 r = K85 + third;
389 28641 g = 0;
390 28641 FORCE_REFERENCE(g);
391 28641 b = K171 - third;
392
393 } else {
394 // 111
395 //case 7: // K -> R
396 16337 r = K170 + third;
397 16337 g = 0;
398 16337 FORCE_REFERENCE(g);
399 16337 b = K85 - third;
400
401 }
402 }
403 }
404
405 // This is one of the good places to scale the green down,
406 // although the client can scale green down as well.
407 if( G2 ) g = g >> 1;
408 if( Gscale ) g = scale8_video_LEAVING_R1_DIRTY( g, Gscale);
409
410 // Scale down colors if we're desaturated at all
411 // and add the brightness_floor to r, g, and b.
412
2/2
✓ Branch 0 taken 94181 times.
✓ Branch 1 taken 21188 times.
115369 if( sat != 255 ) {
413
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94181 times.
94181 if( sat == 0) {
414 ✗ r = 255; b = 255; g = 255;
415 } else {
416
1/2
✓ Branch 0 taken 7056 times.
✗ Branch 1 not taken.
94181 uint8_t desat = 255 - sat;
417
1/2
✓ Branch 0 taken 87125 times.
✗ Branch 1 not taken.
94181 desat = scale8_video( desat, desat);
418
419 94181 uint8_t satscale = 255 - desat;
420 //satscale = sat; // uncomment to revert to pre-2021 saturation behavior
421
422 //nscale8x3_video( r, g, b, sat);
423 #if (FASTLED_SCALE8_FIXED==1)
424 r = scale8_LEAVING_R1_DIRTY( r, satscale);
425 asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943
426 g = scale8_LEAVING_R1_DIRTY( g, satscale);
427 asm volatile("");
428 b = scale8_LEAVING_R1_DIRTY( b, satscale);
429 asm volatile("");
430 cleanup_R1();
431 #else
432
2/2
✓ Branch 0 taken 87198 times.
✓ Branch 1 taken 6983 times.
181379 if( r ) r = scale8( r, satscale) + 1;
433
2/2
✓ Branch 0 taken 42950 times.
✓ Branch 1 taken 51231 times.
137131 if( g ) g = scale8( g, satscale) + 1;
434
2/2
✓ Branch 0 taken 58037 times.
✓ Branch 1 taken 36144 times.
152218 if( b ) b = scale8( b, satscale) + 1;
435 #endif
436 94181 uint8_t brightness_floor = desat;
437 94181 r += brightness_floor;
438 94181 g += brightness_floor;
439 94181 b += brightness_floor;
440 }
441 }
442
443 // Now scale everything down if we're at value < 255.
444
2/2
✓ Branch 0 taken 107507 times.
✓ Branch 1 taken 7862 times.
115369 if( val != 255 ) {
445
446
2/2
✓ Branch 0 taken 97576 times.
✓ Branch 1 taken 2875 times.
107507 val = scale8_video_LEAVING_R1_DIRTY( val, val);
447
2/2
✓ Branch 0 taken 2875 times.
✓ Branch 1 taken 104632 times.
107507 if( val == 0 ) {
448 2875 r=0; g=0; b=0;
449 } else {
450 // nscale8x3_video( r, g, b, val);
451 #if (FASTLED_SCALE8_FIXED==1)
452 r = scale8_LEAVING_R1_DIRTY( r, val);
453 asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943
454 g = scale8_LEAVING_R1_DIRTY( g, val);
455 asm volatile("");
456 b = scale8_LEAVING_R1_DIRTY( b, val);
457 asm volatile("");
458 cleanup_R1();
459 #else
460
2/2
✓ Branch 0 taken 102708 times.
✓ Branch 1 taken 1924 times.
207340 if( r ) r = scale8( r, val) + 1;
461
2/2
✓ Branch 0 taken 100447 times.
✓ Branch 1 taken 4185 times.
205079 if( g ) g = scale8( g, val) + 1;
462
2/2
✓ Branch 0 taken 99798 times.
✓ Branch 1 taken 4834 times.
204430 if( b ) b = scale8( b, val) + 1;
463 #endif
464 }
465 }
466
467 // Here we have the old AVR "missing std X+n" problem again
468 // It turns out that fixing it winds up costing more than
469 // not fixing it.
470 // To paraphrase Dr Bronner, profile! profile! profile!
471 //asm volatile( "" : : : "r26", "r27" );
472 //asm volatile (" movw r30, r26 \n" : : : "r30", "r31");
473 115369 rgb.r = r;
474 115369 rgb.g = g;
475 115369 rgb.b = b;
476 115369 }
477
478
479 ✗ void hsv2rgb_raw(const struct CHSV * phsv, struct CRGB * prgb, int numLeds) {
480 ✗ for(int i = 0; i < numLeds; ++i) {
481 ✗ hsv2rgb_raw(phsv[i], prgb[i]);
482 }
483 ✗ }
484
485 ✗ void hsv2rgb_rainbow( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
486 ✗ for(int i = 0; i < numLeds; ++i) {
487 ✗ hsv2rgb_rainbow(phsv[i], prgb[i]);
488 }
489 ✗ }
490
491 ✗ void hsv2rgb_spectrum( const struct CHSV* phsv, struct CRGB * prgb, int numLeds) {
492 ✗ for(int i = 0; i < numLeds; ++i) {
493 ✗ hsv2rgb_spectrum(phsv[i], prgb[i]);
494 }
495 ✗ }
496
497
498 /// Convert a fractional input into a constant
499 #define FIXFRAC8(N,D) (((N)*256)/(D))
500
501 // This function is only an approximation, and it is not
502 // nearly as fast as the normal HSV-to-RGB conversion.
503 // See extended notes in the .h file.
504 ✗ CHSV rgb2hsv_approximate( const CRGB& rgb)
505 {
506 ✗ uint8_t r = rgb.r;
507 ✗ uint8_t g = rgb.g;
508 ✗ uint8_t b = rgb.b;
509 uint8_t h, s, v;
510
511 // find desaturation
512 ✗ uint8_t desat = 255;
513 ✗ if( r < desat) desat = r;
514 ✗ if( g < desat) desat = g;
515 ✗ if( b < desat) desat = b;
516
517 // remove saturation from all channels
518 ✗ r -= desat;
519 ✗ g -= desat;
520 ✗ b -= desat;
521
522 //Serial.print("desat="); Serial.print(desat); Serial.println("");
523
524 //uint8_t orig_desat = sqrt16( desat * 256);
525 //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println("");
526
527 // saturation is opposite of desaturation
528 ✗ s = 255 - desat;
529 //Serial.print("s.1="); Serial.print(s); Serial.println("");
530
531 ✗ if( s != 255 ) {
532 // undo 'dimming' of saturation
533 ✗ s = 255 - sqrt16( (255-s) * 256);
534 }
535 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
536 // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
537 //Serial.print("s.2="); Serial.print(s); Serial.println("");
538
539
540 // at least one channel is now zero
541 // if all three channels are zero, we had a
542 // shade of gray.
543 ✗ if( (r + g + b) == 0) {
544 // we pick hue zero for no special reason
545 ✗ return CHSV( 0, 0, 255 - s);
546 }
547
548 // scale all channels up to compensate for desaturation
549 ✗ if( s < 255) {
550 ✗ if( s == 0) s = 1;
551 ✗ uint32_t scaleup = 65535 / (s);
552 ✗ r = ((uint32_t)(r) * scaleup) / 256;
553 ✗ g = ((uint32_t)(g) * scaleup) / 256;
554 ✗ b = ((uint32_t)(b) * scaleup) / 256;
555 }
556 //Serial.print("r.2="); Serial.print(r); Serial.println("");
557 //Serial.print("g.2="); Serial.print(g); Serial.println("");
558 //Serial.print("b.2="); Serial.print(b); Serial.println("");
559
560 ✗ uint16_t total = r + g + b;
561
562 //Serial.print("total="); Serial.print(total); Serial.println("");
563
564 // scale all channels up to compensate for low values
565 ✗ if( total < 255) {
566 ✗ if( total == 0) total = 1;
567 ✗ uint32_t scaleup = 65535 / (total);
568 ✗ r = ((uint32_t)(r) * scaleup) / 256;
569 ✗ g = ((uint32_t)(g) * scaleup) / 256;
570 ✗ b = ((uint32_t)(b) * scaleup) / 256;
571 }
572 //Serial.print("r.3="); Serial.print(r); Serial.println("");
573 //Serial.print("g.3="); Serial.print(g); Serial.println("");
574 //Serial.print("b.3="); Serial.print(b); Serial.println("");
575
576 ✗ if( total > 255 ) {
577 ✗ v = 255;
578 } else {
579 ✗ v = qadd8(desat,total);
580 // undo 'dimming' of brightness
581 ✗ if( v != 255) v = sqrt16( v * 256);
582 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
583 // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
584
585 }
586
587 //Serial.print("v="); Serial.print(v); Serial.println("");
588
589
590 #if 0
591
592 //#else
593 if( v != 255) {
594 // this part could probably use refinement/rethinking,
595 // (but it doesn't overflow & wrap anymore)
596 uint16_t s16;
597 s16 = (s * 256);
598 s16 /= v;
599 //Serial.print("s16="); Serial.print(s16); Serial.println("");
600 if( s16 < 256) {
601 s = s16;
602 } else {
603 s = 255; // clamp to prevent overflow
604 }
605 }
606 #endif
607
608 //Serial.print("s.3="); Serial.print(s); Serial.println("");
609
610
611 // since this wasn't a pure shade of gray,
612 // the interesting question is what hue is it
613
614
615
616 // start with which channel is highest
617 // (ties don't matter)
618 ✗ uint8_t highest = r;
619 ✗ if( g > highest) highest = g;
620 ✗ if( b > highest) highest = b;
621
622 ✗ if( highest == r ) {
623 // Red is highest.
624 // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
625 ✗ if( g == 0 ) {
626 // if green is zero, we're in Purple/Pink-Red
627 ✗ h = (HUE_PURPLE + HUE_PINK) / 2;
628 ✗ h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
629 ✗ } else if ( (r - g) > g) {
630 // if R-G > G then we're in Red-Orange
631 ✗ h = HUE_RED;
632 ✗ h += scale8( g, FIXFRAC8(32,85));
633 } else {
634 // R-G < G, we're in Orange-Yellow
635 ✗ h = HUE_ORANGE;
636 ✗ h += scale8( qsub8((g - 85) + (171 - r), 4), FIXFRAC8(32,85)); //221
637 }
638
639 ✗ } else if ( highest == g) {
640 // Green is highest
641 // Hue could be Yellow-Green, Green-Aqua
642 ✗ if( b == 0) {
643 // if Blue is zero, we're in Yellow-Green
644 // G = 171..255
645 // R = 171.. 0
646 ✗ h = HUE_YELLOW;
647 ✗ uint8_t radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31
648 ✗ uint8_t gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31;
649 ✗ uint8_t rgadj = radj + gadj;
650 ✗ uint8_t hueadv = rgadj / 2;
651 ✗ h += hueadv;
652 //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))),
653 // FIXFRAC8(32,255)); //
654 } else {
655 // if Blue is nonzero we're in Green-Aqua
656 ✗ if( (g-b) > b) {
657 ✗ h = HUE_GREEN;
658 ✗ h += scale8( b, FIXFRAC8(32,85));
659 } else {
660 ✗ h = HUE_AQUA;
661 ✗ h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
662 }
663 }
664
665 } else /* highest == b */ {
666 // Blue is highest
667 // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
668 ✗ if( r == 0) {
669 // if red is zero, we're in Aqua/Blue-Blue
670 ✗ h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
671 ✗ h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
672 ✗ } else if ( (b-r) > r) {
673 // B-R > R, we're in Blue-Purple
674 ✗ h = HUE_BLUE;
675 ✗ h += scale8( r, FIXFRAC8(32,85));
676 } else {
677 // B-R < R, we're in Purple-Pink
678 ✗ h = HUE_PURPLE;
679 ✗ h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
680 }
681 }
682
683 ✗ h += 1;
684 ✗ return CHSV( h, s, v);
685 }
686
687 // Examples that need work:
688 // 0,192,192
689 // 192,64,64
690 // 224,32,32
691 // 252,0,126
692 // 252,252,0
693 // 252,252,126
694
695 FASTLED_NAMESPACE_END
696