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