81 lines
3.5 KiB
C
81 lines
3.5 KiB
C
#pragma once
|
|
|
|
#include "fl/stdint.h"
|
|
|
|
|
|
inline uint32_t Adafruit_DotStar_ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) {
|
|
|
|
uint8_t r, g, b;
|
|
|
|
// Remap 0-65535 to 0-1529. Pure red is CENTERED on the 64K rollover;
|
|
// 0 is not the start of pure red, but the midpoint...a few values above
|
|
// zero and a few below 65536 all yield pure red (similarly, 32768 is the
|
|
// midpoint, not start, of pure cyan). The 8-bit RGB hexcone (256 values
|
|
// each for red, green, blue) really only allows for 1530 distinct hues
|
|
// (not 1536, more on that below), but the full unsigned 16-bit type was
|
|
// chosen for hue so that one's code can easily handle a contiguous color
|
|
// wheel by allowing hue to roll over in either direction.
|
|
hue = (hue * 1530L + 32768) / 65536;
|
|
// Because red is centered on the rollover point (the +32768 above,
|
|
// essentially a fixed-point +0.5), the above actually yields 0 to 1530,
|
|
// where 0 and 1530 would yield the same thing. Rather than apply a
|
|
// costly modulo operator, 1530 is handled as a special case below.
|
|
|
|
// So you'd think that the color "hexcone" (the thing that ramps from
|
|
// pure red, to pure yellow, to pure green and so forth back to red,
|
|
// yielding six slices), and with each color component having 256
|
|
// possible values (0-255), might have 1536 possible items (6*256),
|
|
// but in reality there's 1530. This is because the last element in
|
|
// each 256-element slice is equal to the first element of the next
|
|
// slice, and keeping those in there this would create small
|
|
// discontinuities in the color wheel. So the last element of each
|
|
// slice is dropped...we regard only elements 0-254, with item 255
|
|
// being picked up as element 0 of the next slice. Like this:
|
|
// Red to not-quite-pure-yellow is: 255, 0, 0 to 255, 254, 0
|
|
// Pure yellow to not-quite-pure-green is: 255, 255, 0 to 1, 255, 0
|
|
// Pure green to not-quite-pure-cyan is: 0, 255, 0 to 0, 255, 254
|
|
// and so forth. Hence, 1530 distinct hues (0 to 1529), and hence why
|
|
// the constants below are not the multiples of 256 you might expect.
|
|
|
|
// Convert hue to R,G,B (nested ifs faster than divide+mod+switch):
|
|
if (hue < 510) { // Red to Green-1
|
|
b = 0;
|
|
if (hue < 255) { // Red to Yellow-1
|
|
r = 255;
|
|
g = hue; // g = 0 to 254
|
|
} else { // Yellow to Green-1
|
|
r = 510 - hue; // r = 255 to 1
|
|
g = 255;
|
|
}
|
|
} else if (hue < 1020) { // Green to Blue-1
|
|
r = 0;
|
|
if (hue < 765) { // Green to Cyan-1
|
|
g = 255;
|
|
b = hue - 510; // b = 0 to 254
|
|
} else { // Cyan to Blue-1
|
|
g = 1020 - hue; // g = 255 to 1
|
|
b = 255;
|
|
}
|
|
} else if (hue < 1530) { // Blue to Red-1
|
|
g = 0;
|
|
if (hue < 1275) { // Blue to Magenta-1
|
|
r = hue - 1020; // r = 0 to 254
|
|
b = 255;
|
|
} else { // Magenta to Red-1
|
|
r = 255;
|
|
b = 1530 - hue; // b = 255 to 1
|
|
}
|
|
} else { // Last 0.5 Red (quicker than % operator)
|
|
r = 255;
|
|
g = b = 0;
|
|
}
|
|
|
|
// Apply saturation and value to R,G,B, pack into 32-bit result:
|
|
uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255
|
|
uint16_t s1 = 1 + sat; // 1 to 256; same reason
|
|
uint8_t s2 = 255 - sat; // 255 to 0
|
|
return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) |
|
|
(((((g * s1) >> 8) + s2) * v1) & 0xff00) |
|
|
(((((b * s1) >> 8) + s2) * v1) >> 8);
|
|
}
|