FastLED 3.10.8
Loading...
Searching...
No Matches
hsv2rgb.cpp.hpp
Go to the documentation of this file.
1
3
4#include "platforms/is_platform.h"
5
7#define FASTLED_INTERNAL
8#include "fl/stl/stdint.h"
9
10#include "fl/system/fastled.h"
11#include "fl/math/math.h"
12
13#include "hsv2rgb.h"
14#include "fl/stl/noexcept.h"
15
16
18void hsv2rgb_raw_C (const CHSV & hsv, CRGB & rgb) FL_NO_EXCEPT;
20void hsv2rgb_raw_avr(const CHSV & hsv, CRGB & rgb) FL_NO_EXCEPT;
21
22#if defined(FL_IS_AVR) && !defined(FL_IS_AVR_ATTINY)
23void hsv2rgb_raw(const CHSV & hsv, CRGB & rgb)
24{
25 hsv2rgb_raw_avr( hsv, rgb);
26}
27#else
28void hsv2rgb_raw(const CHSV & hsv, CRGB & rgb)
30 hsv2rgb_raw_C( hsv, rgb);
31}
32#endif
33
34
36#define APPLY_DIMMING(X) (X)
37
40#define HSV_SECTION_6 (0x20)
41
44#define HSV_SECTION_3 (0x40)
45
48 CRGB rgb;
49 hsv2rgb_spectrum(hsv, rgb);
50 return rgb;
51}
52
53void hsv2rgb_raw_C (const CHSV & hsv, CRGB & rgb)
55 // Convert hue, saturation and brightness ( HSV/HSB ) to RGB
56 // "Dimming" is used on saturation and brightness to make
57 // the output more visually linear.
58
59 // Apply dimming curves
60 fl::u8 value = APPLY_DIMMING( hsv.val); // cppcheck-suppress selfAssignment
61 fl::u8 saturation = hsv.sat;
62
63 // The brightness floor is minimum number that all of
64 // R, G, and B will be set to.
65 fl::u8 invsat = APPLY_DIMMING( 255 - saturation); // cppcheck-suppress selfAssignment
66 fl::u8 brightness_floor = (value * invsat) / 256;
67
68 // The color amplitude is the maximum amount of R, G, and B
69 // that will be added on top of the brightness_floor to
70 // create the specific hue desired.
71 fl::u8 color_amplitude = value - brightness_floor;
72
73 // Figure out which section of the hue wheel we're in,
74 // and how far offset we are withing that section
75 fl::u8 section = hsv.hue / HSV_SECTION_3; // 0..2
76 fl::u8 offset = hsv.hue % HSV_SECTION_3; // 0..63
77
78 fl::u8 rampup = offset; // 0..63
79 fl::u8 rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0
80
81 // We now scale rampup and rampdown to a 0-255 range -- at least
82 // in theory, but here's where architecture-specific decsions
83 // come in to play:
84 // To scale them up to 0-255, we'd want to multiply by 4.
85 // But in the very next step, we multiply the ramps by other
86 // values and then divide the resulting product by 256.
87 // So which is faster?
88 // ((ramp * 4) * othervalue) / 256
89 // or
90 // ((ramp ) * othervalue) / 64
91 // It depends on your processor architecture.
92 // On 8-bit AVR, the "/ 256" is just a one-cycle register move,
93 // but the "/ 64" might be a multicycle shift process. So on AVR
94 // it's faster do multiply the ramp values by four, and then
95 // divide by 256.
96 // On ARM, the "/ 256" and "/ 64" are one cycle each, so it's
97 // faster to NOT multiply the ramp values by four, and just to
98 // divide the resulting product by 64 (instead of 256).
99 // Moral of the story: trust your profiler, not your insticts.
100
101 // Since there's an AVR assembly version elsewhere, we'll
102 // assume what we're on an architecture where any number of
103 // bit shifts has roughly the same cost, and we'll remove the
104 // redundant math at the source level:
105
106 // // scale up to 255 range
107 // //rampup *= 4; // 0..252
108 // //rampdown *= 4; // 0..252
109
110 // compute color-amplitude-scaled-down versions of rampup and rampdown
111 fl::u8 rampup_amp_adj = (rampup * color_amplitude) / (256 / 4);
112 fl::u8 rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4);
113
114 // add brightness_floor offset to everything
115 fl::u8 rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
116 fl::u8 rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
117
118
119 if( section ) {
120 if( section == 1) {
121 // section 1: 0x40..0x7F
122 rgb.r = brightness_floor;
123 rgb.g = rampdown_adj_with_floor;
124 rgb.b = rampup_adj_with_floor;
125 } else {
126 // section 2; 0x80..0xBF
127 rgb.r = rampup_adj_with_floor;
128 rgb.g = brightness_floor;
129 rgb.b = rampdown_adj_with_floor;
130 }
131 } else {
132 // section 0: 0x00..0x3F
133 rgb.r = rampdown_adj_with_floor;
134 rgb.g = rampup_adj_with_floor;
135 rgb.b = brightness_floor;
136 }
137}
138
139
140
141#if defined(FL_IS_AVR) && !defined(FL_IS_AVR_ATTINY)
142void hsv2rgb_raw_avr(const CHSV & hsv, CRGB & rgb)
143{
144 fl::u8 hue, saturation, value;
145
146 hue = hsv.hue;
147 saturation = hsv.sat;
148 value = hsv.val;
149
150 // Saturation more useful the other way around
151 saturation = 255 - saturation;
152 fl::u8 invsat = APPLY_DIMMING( saturation ); // cppcheck-suppress selfAssignment
153
154 // Apply dimming curves
155 value = APPLY_DIMMING( value ); // cppcheck-suppress selfAssignment
156
157 // The brightness floor is minimum number that all of
158 // R, G, and B will be set to, which is value * invsat
159 fl::u8 brightness_floor;
160
161 asm volatile(
162 "mul %[value], %[invsat] \n"
163 "mov %[brightness_floor], r1 \n"
164 : [brightness_floor] "=r" (brightness_floor)
165 : [value] "r" (value),
166 [invsat] "r" (invsat)
167 : "r0", "r1"
168 );
169
170 // The color amplitude is the maximum amount of R, G, and B
171 // that will be added on top of the brightness_floor to
172 // create the specific hue desired.
173 fl::u8 color_amplitude = value - brightness_floor;
174
175 // Figure how far we are offset into the section of the
176 // color wheel that we're in
177 fl::u8 offset = hsv.hue & (HSV_SECTION_3 - 1); // 0..63
178 fl::u8 rampup = offset * 4; // 0..252
179
180
181 // compute color-amplitude-scaled-down versions of rampup and rampdown
182 fl::u8 rampup_amp_adj;
183 fl::u8 rampdown_amp_adj;
184
185 asm volatile(
186 "mul %[rampup], %[color_amplitude] \n"
187 "mov %[rampup_amp_adj], r1 \n"
188 "com %[rampup] \n"
189 "mul %[rampup], %[color_amplitude] \n"
190 "mov %[rampdown_amp_adj], r1 \n"
191 : [rampup_amp_adj] "=&r" (rampup_amp_adj),
192 [rampdown_amp_adj] "=&r" (rampdown_amp_adj),
193 [rampup] "+r" (rampup)
194 : [color_amplitude] "r" (color_amplitude)
195 : "r0", "r1"
196 );
197
198
199 // add brightness_floor offset to everything
200 fl::u8 rampup_adj_with_floor = rampup_amp_adj + brightness_floor;
201 fl::u8 rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor;
202
203
204 // keep gcc from using "X" as the index register for storing
205 // results back in the return structure. AVR's X register can't
206 // do "std X+q, rnn", but the Y and Z registers can.
207 // if the pointer to 'rgb' is in X, gcc will add all kinds of crazy
208 // extra instructions. Simply killing X here seems to help it
209 // try Y or Z first.
210 asm volatile( "" : : : "r26", "r27" );
211
212
213 if( hue & 0x80 ) {
214 // section 2: 0x80..0xBF
215 rgb.r = rampup_adj_with_floor;
216 rgb.g = brightness_floor;
217 rgb.b = rampdown_adj_with_floor;
218 } else {
219 if( hue & 0x40) {
220 // section 1: 0x40..0x7F
221 rgb.r = brightness_floor;
222 rgb.g = rampdown_adj_with_floor;
223 rgb.b = rampup_adj_with_floor;
224 } else {
225 // section 0: 0x00..0x3F
226 rgb.r = rampdown_adj_with_floor;
227 rgb.g = rampup_adj_with_floor;
228 rgb.b = brightness_floor;
229 }
230 }
231
232 cleanup_R1();
233}
234// End of AVR asm implementation
235
236#endif
237
238void hsv2rgb_spectrum( const CHSV& hsv, CRGB& rgb)
240 CHSV hsv2(hsv);
241 hsv2.hue = scale8( hsv2.hue, 191);
242 hsv2rgb_raw(hsv2, rgb);
243}
244
245
253#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) )
254
255
257#define K255 255
258#define K171 171
259#define K170 170
260#define K85 85
262
264 CRGB rgb;
265 hsv2rgb_rainbow(hsv, rgb);
266 return rgb;
267}
268
269void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb)
271 // Yellow has a higher inherent brightness than
272 // any other color; 'pure' yellow is perceived to
273 // be 93% as bright as white. In order to make
274 // yellow appear the correct relative brightness,
275 // it has to be rendered brighter than all other
276 // colors.
277 // Level Y1 is a moderate boost, the default.
278 // Level Y2 is a strong boost.
279 const fl::u8 Y1 = 1;
280 const fl::u8 Y2 = 0;
281
282 // G2: Whether to divide all greens by two.
283 // Depends GREATLY on your particular LEDs
284 const fl::u8 G2 = 0;
285
286 // Gscale: what to scale green down by.
287 // Depends GREATLY on your particular LEDs
288 const fl::u8 Gscale = 0;
289
290
291 fl::u8 hue = hsv.hue;
292 fl::u8 sat = hsv.sat;
293 fl::u8 val = hsv.val;
294
295 fl::u8 offset = hue & 0x1F; // 0..31
296
297 // offset8 = offset * 8
298 fl::u8 offset8 = offset;
299 {
300#if defined(FL_IS_AVR)
301 // Left to its own devices, gcc turns "x <<= 3" into a loop
302 // It's much faster and smaller to just do three single-bit shifts
303 // So this business is to force that.
304 offset8 <<= 1;
305 asm volatile("");
306 offset8 <<= 1;
307 asm volatile("");
308 offset8 <<= 1;
309#else
310 // On ARM and other non-AVR platforms, we just shift 3.
311 offset8 <<= 3;
312#endif
313 }
314
315 fl::u8 third = scale8( offset8, (256 / 3)); // max = 85
316
317 fl::u8 r, g, b;
318
319 if( ! (hue & 0x80) ) {
320 // 0XX
321 if( ! (hue & 0x40) ) {
322 // 00X
323 //section 0-1
324 if( ! (hue & 0x20) ) {
325 // 000
326 //case 0: // R -> O
327 r = K255 - third;
328 g = third;
329 b = 0;
331 } else {
332 // 001
333 //case 1: // O -> Y
334 if( Y1 ) {
335 r = K171;
336 g = K85 + third ;
337 b = 0;
339 }
340 if( Y2 ) {
341 r = K170 + third;
342 //uint8_t twothirds = (third << 1);
343 fl::u8 twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
344 g = K85 + twothirds;
345 b = 0;
347 }
348 }
349 } else {
350 //01X
351 // section 2-3
352 if( ! (hue & 0x20) ) {
353 // 010
354 //case 2: // Y -> G
355 if( Y1 ) {
356 //uint8_t twothirds = (third << 1);
357 fl::u8 twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
358 r = K171 - twothirds;
359 g = K170 + third;
360 b = 0;
362 }
363 if( Y2 ) {
364 r = K255 - offset8;
365 g = K255;
366 b = 0;
368 }
369 } else {
370 // 011
371 // case 3: // G -> A
372 r = 0;
374 g = K255 - third;
375 b = third;
376 }
377 }
378 } else {
379 // section 4-7
380 // 1XX
381 if( ! (hue & 0x40) ) {
382 // 10X
383 if( ! ( hue & 0x20) ) {
384 // 100
385 //case 4: // A -> B
386 r = 0;
388 //uint8_t twothirds = (third << 1);
389 fl::u8 twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170
390 g = K171 - twothirds; //K170?
391 b = K85 + twothirds;
392
393 } else {
394 // 101
395 //case 5: // B -> P
396 r = third;
397 g = 0;
399 b = K255 - third;
400
401 }
402 } else {
403 if( ! (hue & 0x20) ) {
404 // 110
405 //case 6: // P -- K
406 r = K85 + third;
407 g = 0;
409 b = K171 - third;
410
411 } else {
412 // 111
413 //case 7: // K -> R
414 r = K170 + third;
415 g = 0;
417 b = K85 - third;
418
419 }
420 }
421 }
422
423 // This is one of the good places to scale the green down,
424 // although the client can scale green down as well.
425 if( G2 ) g = g >> 1;
426 // Deliberately the cleaning variant. The _LEAVING_R1_DIRTY form only pays
427 // off when several calls are batched before one cleanup_R1(), and this is a
428 // lone call whose dirty R1 outlives it on three separate paths: sat == 255
429 // and val == 255 both skip their blocks entirely, and the sat == 0 branch
430 // below just assigns 255s. Any of those returns to the caller with AVR's
431 // __zero_reg__ non-zero. See issue #668.
432 if( Gscale ) g = scale8_video( g, Gscale);
433
434 // Scale down colors if we're desaturated at all
435 // and add the brightness_floor to r, g, and b.
436 if( sat != 255 ) {
437 if( sat == 0) {
438 r = 255; b = 255; g = 255;
439 } else {
440 fl::u8 desat = 255 - sat;
441 desat = scale8_video( desat, desat);
442
443 fl::u8 satscale = 255 - desat;
444 //satscale = sat; // uncomment to revert to pre-2021 saturation behavior
445
446 //nscale8x3_video( r, g, b, sat);
447#if (FASTLED_SCALE8_FIXED==1)
448 r = scale8_LEAVING_R1_DIRTY( r, satscale);
449 asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943
450 g = scale8_LEAVING_R1_DIRTY( g, satscale);
451 asm volatile("");
452 b = scale8_LEAVING_R1_DIRTY( b, satscale);
453 asm volatile("");
454 cleanup_R1();
455#else
456 if( r ) r = scale8( r, satscale) + 1;
457 if( g ) g = scale8( g, satscale) + 1;
458 if( b ) b = scale8( b, satscale) + 1;
459#endif
460 fl::u8 brightness_floor = desat;
461 r += brightness_floor;
462 g += brightness_floor;
463 b += brightness_floor;
464 }
465 }
466
467 // Now scale everything down if we're at value < 255.
468 if( val != 255 ) {
469
470 // This call is followed by branching before any other scale8 batch,
471 // so use the cleaning form rather than carrying dirty R1 through C++.
472 val = scale8_video( val, val);
473 if( val == 0 ) {
474 r=0; g=0; b=0;
475 } else {
476 // nscale8x3_video( r, g, b, val);
477#if (FASTLED_SCALE8_FIXED==1)
478 r = scale8_LEAVING_R1_DIRTY( r, val);
479 asm volatile(""); // Fixes jumping red pixel: https://github.com/FastLED/FastLED/pull/943
480 g = scale8_LEAVING_R1_DIRTY( g, val);
481 asm volatile("");
482 b = scale8_LEAVING_R1_DIRTY( b, val);
483 asm volatile("");
484 cleanup_R1();
485#else
486 if( r ) r = scale8( r, val) + 1;
487 if( g ) g = scale8( g, val) + 1;
488 if( b ) b = scale8( b, val) + 1;
489#endif
490 }
491 }
492
493 // Here we have the old AVR "missing std X+n" problem again
494 // It turns out that fixing it winds up costing more than
495 // not fixing it.
496 // To paraphrase Dr Bronner, profile! profile! profile!
497 //asm volatile( "" : : : "r26", "r27" );
498 //asm volatile (" movw r30, r26 \n" : : : "r30", "r31");
499 rgb.r = r;
500 rgb.g = g;
501 rgb.b = b;
502}
503
505 const auto f = [](const int n, const fl::u8 h) -> fl::u32 {
506 constexpr int kZero = 0 << 8;
507 constexpr int kOne = 1 << 8;
508 constexpr int kFour = 4 << 8;
509 constexpr int kSix = 6 << 8;
510
511 const int k = ((n << 8) + 6*h) % kSix;
512 const int k2 = kFour - k;
513 return fl::max(kZero, fl::min(kOne, fl::min(k, k2)));
514 };
515
516 const fl::u32 chroma = hsv.v * hsv.s / 255;
517 rgb.r = hsv.v - ((chroma * f(5, hsv.h)) >> 8);
518 rgb.g = hsv.v - ((chroma * f(3, hsv.h)) >> 8);
519 rgb.b = hsv.v - ((chroma * f(1, hsv.h)) >> 8);
520}
521
524 CRGB rgb;
525 hsv2rgb_fullspectrum(hsv, rgb);
526 return rgb;
527}
528
529
530void hsv2rgb_raw(const CHSV * phsv, CRGB * prgb, int numLeds) FL_NO_EXCEPT {
531 for(int i = 0; i < numLeds; ++i) {
532 hsv2rgb_raw(phsv[i], prgb[i]);
533 }
534}
535
536void hsv2rgb_rainbow( const CHSV* phsv, CRGB * prgb, int numLeds) FL_NO_EXCEPT {
537 for(int i = 0; i < numLeds; ++i) {
538 hsv2rgb_rainbow(phsv[i], prgb[i]);
539 }
540}
541
542void hsv2rgb_spectrum( const CHSV* phsv, CRGB * prgb, int numLeds) FL_NO_EXCEPT {
543 for(int i = 0; i < numLeds; ++i) {
544 hsv2rgb_spectrum(phsv[i], prgb[i]);
545 }
546}
547
548void hsv2rgb_fullspectrum( const CHSV* phsv, CRGB * prgb, int numLeds) FL_NO_EXCEPT {
549 for (int i = 0; i < numLeds; ++i) {
550 hsv2rgb_fullspectrum(phsv[i], prgb[i]);
551 }
552}
553
555#define FIXFRAC8(N,D) (((N)*256)/(D))
556
557// This function is only an approximation, and it is not
558// nearly as fast as the normal HSV-to-RGB conversion.
559// See extended notes in the .h file.
562 // These channels can exceed 255 while saturation is being undone. Keep
563 // the intermediate values wide until they are normalized below.
564 fl::u16 r = rgb.r;
565 fl::u16 g = rgb.g;
566 fl::u16 b = rgb.b;
567 fl::u8 h, s, v;
568
569 // find desaturation
570 fl::u8 desat = 255;
571 if( r < desat) desat = r;
572 if( g < desat) desat = g;
573 if( b < desat) desat = b;
574
575 // remove saturation from all channels
576 r -= desat;
577 g -= desat;
578 b -= desat;
579
580 //Serial.print("desat="); Serial.print(desat); Serial.println("");
581
582 //uint8_t orig_desat = sqrt16( desat * 256);
583 //Serial.print("orig_desat="); Serial.print(orig_desat); Serial.println("");
584
585 // saturation is opposite of desaturation
586 s = 255 - desat;
587 //Serial.print("s.1="); Serial.print(s); Serial.println("");
588
589 if( s != 255 ) {
590 // undo 'dimming' of saturation
591 s = 255 - fl::sqrt16( (255-s) * 256);
592 }
593 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
594 // if( s != 255 ) s = (255 - (256.0 * sqrt( (float)(255-s) / 256.0)));
595 //Serial.print("s.2="); Serial.print(s); Serial.println("");
596
597
598 // at least one channel is now zero
599 // if all three channels are zero, we had a
600 // shade of gray.
601 if( (r + g + b) == 0) {
602 // we pick hue zero for no special reason
603 return CHSV( 0, 0, 255 - s);
604 }
605
606 // scale all channels up to compensate for desaturation
607 if( s < 255) {
608 if( s == 0) s = 1;
609 fl::u32 scaleup = 65535 / (s);
610 r = ((fl::u32)(r) * scaleup) / 256;
611 g = ((fl::u32)(g) * scaleup) / 256;
612 b = ((fl::u32)(b) * scaleup) / 256;
613 }
614 //Serial.print("r.2="); Serial.print(r); Serial.println("");
615 //Serial.print("g.2="); Serial.print(g); Serial.println("");
616 //Serial.print("b.2="); Serial.print(b); Serial.println("");
617
618 fl::u16 total = r + g + b;
619
620 //Serial.print("total="); Serial.print(total); Serial.println("");
621
622 // Normalize the channels to the range expected by the hue approximation.
623 // This scales low totals up as before, and also scales high totals down
624 // instead of letting wide intermediates wrap when stored in u8 channels.
625 if( total != 255) {
626 if( total == 0) total = 1;
627 fl::u32 scaleup = 65535 / (total);
628 r = ((fl::u32)(r) * scaleup) / 256;
629 g = ((fl::u32)(g) * scaleup) / 256;
630 b = ((fl::u32)(b) * scaleup) / 256;
631 }
632 //Serial.print("r.3="); Serial.print(r); Serial.println("");
633 //Serial.print("g.3="); Serial.print(g); Serial.println("");
634 //Serial.print("b.3="); Serial.print(b); Serial.println("");
635
636 if( total > 255 ) {
637 v = 255;
638 } else {
639 v = qadd8(desat,total);
640 // undo 'dimming' of brightness
641 if( v != 255) v = fl::sqrt16( v * 256);
642 // without lib8tion: float ... ew ... sqrt... double ew, or rather, ew ^ 0.5
643 // if( v != 255) v = (256.0 * sqrt( (float)(v) / 256.0));
644
645 }
646
647 //Serial.print("v="); Serial.print(v); Serial.println("");
648
649
650#if 0
651
652 //#else
653 if( v != 255) {
654 // this part could probably use refinement/rethinking,
655 // (but it doesn't overflow & wrap anymore)
656 fl::u16 s16;
657 s16 = (s * 256);
658 s16 /= v;
659 //Serial.print("s16="); Serial.print(s16); Serial.println("");
660 if( s16 < 256) {
661 s = s16;
662 } else {
663 s = 255; // clamp to prevent overflow
664 }
665 }
666#endif
667
668 //Serial.print("s.3="); Serial.print(s); Serial.println("");
669
670
671 // since this wasn't a pure shade of gray,
672 // the interesting question is what hue is it
673
674
675
676 // start with which channel is highest
677 // (ties don't matter)
678 fl::u16 highest = r;
679 if( g > highest) highest = g;
680 if( b > highest) highest = b;
681
682 if( highest == r ) {
683 // Red is highest.
684 // Hue could be Purple/Pink-Red,Red-Orange,Orange-Yellow
685 if( g == 0 ) {
686 // if green is zero, we're in Purple/Pink-Red
687 h = (HUE_PURPLE + HUE_PINK) / 2;
688 h += scale8( qsub8(r, 128), FIXFRAC8(48,128));
689 } else if ( (r - g) > g) {
690 // if R-G > G then we're in Red-Orange
691 h = HUE_RED;
692 h += scale8( g, FIXFRAC8(32,85));
693 } else {
694 // R-G < G, we're in Orange-Yellow.
695 //
696 // This used to read `(g - 85) + (171 - r)`. Reaching this branch
697 // requires g >= r/2, and r is the largest channel, so r > 171 is
698 // the normal case here -- making (171 - r) negative. The sum was
699 // then truncated to u8 by qsub8() and wrapped to a large value,
700 // sending orange out into the greens: rgb(255,153,0) returned hue
701 // 122 instead of ~26. See issue #436.
702 //
703 // (255 - r) is the same measure of "how far r has fallen" but
704 // cannot go negative, and the sum (g - 85) + (255 - r) has a
705 // minimum of ~42 over this branch's own entry condition, so the
706 // wrap is structurally impossible rather than merely unlikely.
707 // Spans ~42..170, hence FIXFRAC8(32,170) to land the top of the
708 // range on HUE_YELLOW.
709 h = HUE_ORANGE;
710 h += scale8( qsub8((g - 85) + (255 - r), 4), FIXFRAC8(32,170));
711 }
712
713 } else if ( highest == g) {
714 // Green is highest
715 // Hue could be Yellow-Green, Green-Aqua
716 if( b == 0) {
717 // if Blue is zero, we're in Yellow-Green
718 // G = 171..255
719 // R = 171.. 0
720 h = HUE_YELLOW;
721 fl::u8 radj = scale8( qsub8(171,r), 47); //171..0 -> 0..171 -> 0..31
722 fl::u8 gadj = scale8( qsub8(g,171), 96); //171..255 -> 0..84 -> 0..31;
723 fl::u8 rgadj = radj + gadj;
724 fl::u8 hueadv = rgadj / 2;
725 h += hueadv;
726 //h += scale8( qadd8( 4, qadd8((g - 128), (128 - r))),
727 // FIXFRAC8(32,255)); //
728 } else {
729 // if Blue is nonzero we're in Green-Aqua
730 if( (g-b) > b) {
731 h = HUE_GREEN;
732 h += scale8( b, FIXFRAC8(32,85));
733 } else {
734 h = HUE_AQUA;
735 h += scale8( qsub8(b, 85), FIXFRAC8(8,42));
736 }
737 }
738
739 } else /* highest == b */ {
740 // Blue is highest
741 // Hue could be Aqua/Blue-Blue, Blue-Purple, Purple-Pink
742 if( r == 0) {
743 // if red is zero, we're in Aqua/Blue-Blue
744 h = HUE_AQUA + ((HUE_BLUE - HUE_AQUA) / 4);
745 h += scale8( qsub8(b, 128), FIXFRAC8(24,128));
746 } else if ( (b-r) > r) {
747 // B-R > R, we're in Blue-Purple
748 h = HUE_BLUE;
749 h += scale8( r, FIXFRAC8(32,85));
750 } else {
751 // B-R < R, we're in Purple-Pink
752 h = HUE_PURPLE;
753 h += scale8( qsub8(r, 85), FIXFRAC8(32,85));
754 }
755 }
756
757 h += 1;
758 return CHSV( h, s, v);
759}
760
761// Examples that need work:
762// 0,192,192
763// 192,64,64
764// 224,32,32
765// 252,0,126
766// 252,252,0
767// 252,252,126
uint8_t hue
Definition advanced.h:94
@ HUE_BLUE
Blue (225°)
Definition hsv.h:129
@ HUE_RED
Red (0°)
Definition hsv.h:124
@ HUE_AQUA
Aqua (180°)
Definition hsv.h:128
@ HUE_PINK
Pink (315°)
Definition hsv.h:131
@ HUE_GREEN
Green (135°)
Definition hsv.h:127
@ HUE_YELLOW
Yellow (90°)
Definition hsv.h:126
@ HUE_ORANGE
Orange (45°)
Definition hsv.h:125
@ HUE_PURPLE
Purple (270°)
Definition hsv.h:130
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
Internal FastLED header for implementation files.
fl::hsv8 CHSV
Definition chsv.h:11
void hsv2rgb_fullspectrum(const CHSV &hsv, CRGB &rgb) FL_NO_EXCEPT
Converts an HSV value to RGB using the algorithm from here: https://en.wikipedia.org/wiki/HSL_and_HSV...
void hsv2rgb_raw(const CHSV &hsv, CRGB &rgb) FL_NO_EXCEPT
Convert an HSV value to RGB using a mathematically straight spectrum.
CHSV rgb2hsv_approximate(const CRGB &rgb) FL_NO_EXCEPT
Recover approximate HSV values from RGB.
fl::CRGB CRGB
Definition crgb.h:25
CRGB hsv2rgb_rainbow(const CHSV &hsv) FL_NO_EXCEPT
CRGB hsv2rgb_spectrum(const CHSV &hsv) FL_NO_EXCEPT
Inline version of hsv2rgb_spectrum which returns a CRGB object.
#define APPLY_DIMMING(X)
Apply dimming compensation to values.
#define FIXFRAC8(N, D)
Convert a fractional input into a constant.
#define FORCE_REFERENCE(var)
Force a variable reference to avoid compiler over-optimization.
void hsv2rgb_raw_avr(const CHSV &hsv, CRGB &rgb) FL_NO_EXCEPT
HSV to RGB implementation in raw C, for the AVR platform only.
void hsv2rgb_raw_C(const CHSV &hsv, CRGB &rgb) FL_NO_EXCEPT
HSV to RGB implementation in raw C, platform independent.
#define HSV_SECTION_3
Divide the color wheel into four sections, 64 elements each.
Functions to convert from the HSV colorspace to the RGB colorspace.
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
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NO_EXCEPT
Definition math.h:71
#define FL_NO_EXCEPT