| 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 |