I don't know if this has been discused before.

(Georg Moritz) #1

I don’t know if this has been discused before. If so, it must have been a long time ago :wink:

The spectrum 8bit color space has 255*3 hues, raising green, blue and red from 0 to 255, while red, green and blue are falling respectively from 255 to 0. Makes 765 hues.

The FastLED rainbow divides the color space into 8 sections, each of which has 255/3 = 85 hues. Makes 85 * 8 = 680 hues.
FastLED maps these 680 hues to 255, for the sake of speed and assembler optimized 8bit high speed math.

What would be the tradeoffs in speed, if the full 680 hues could be used, giving more richness in color?
The hue value would be an uint16_t. But the hsv2rgb conversion would just be addition, substraction and left shift (omitting scaling for saturation and brightness):

#define BITS 8
#define THIRD (1 << BITS - 1) / 3
#define RED 0 // THIRD * 0
#define ORANGE 85 // THIRD * 1
#define YELLOW 170 // THIRD * 2
#define GREEN 255 // THIRD * 3
#define AQUA 340 // THIRD * 4
#define BLUE 425 // THIRD * 5
#define PURPLE 510 // THIRD * 6
#define PINK 595 // THIRD * 7
#define END 680 // THIRD * 8

void hsv2rgb_rainbow( uint16_t hue, uint8_t r, uint8_t g, uint8_t blue)
uint8_t h;
if (hue < GREEN)
h = uint8_t(hue);
else if (hue < BLUE)
h = uint8_t(hue - GREEN);
h = uint8_t(hue - BLUE);

if (hue < ORANGE)
// case 0: R -> O
r = GREEN - h; g = h; b = 0;
else if (hue < YELLOW)
// case 1: O -> Y
// red remains constant
r = YELLOW; g = h; b = 0;
else if (hue < GREEN)
// case 2: Y -> G
// green up by one, red down by 2
d = (h - YELLOW) << 1;
r = YELLOW - d; g = h; b = 0;
else if (hue < AQUA)
// case 3: G -> A
r = 0; g = GREEN - h; b = h;
else if (hue < BLUE)
// case 4: : A -> B
// green down by 2; blue up by two
d = (h - ORANGE) << 1;
r = 0; g = YELLOW - d; b = ORANGE + d;
// case 5: B -> R (section 5,6,7)
r = h; g = 0; b = GREEN -h;

This aligns fine with any even number of BITS.
How would this code compare in speed and clock cycles with the current implementation?

(Mark Kriegsman) #2

There are way more than 680 hues :slight_smile:

You are of course welcome to use any HSV-to-RGB conversion you like! I have multiple that I use all the time!

The FastLED built-in on is discussed a bit further here: https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors. That page covers some of the design choices we made in choosing the default built-in conversions: 8-bit speed, equal power, equal brightness, etc.

(Mark Kriegsman) #3

Most recent discussion of HSV colors was about 2-3 days go, scroll back and check it out for longer discussions.

(Marc Edgar) #4

Shouldn’t that should be 255255255 = 255^3 ?

(Georg Moritz) #5

Mark Kriegsman: I know that page of course. I am not talking about all possible colors, but about the steps through the color space without dimming or desaturation. From red to green there are 255, since green raises from 0 to 255. From blue to red there are also 255 steps, since red raises from 0 to 255. And the space between green and blue is 2/3 of that between red and green, or between blue and red. Makes 170.

255 + 255 + 170 = 680 hues at maximum saturation and maximum even brightness (r+g+b =255 except orange->yellow).

Marc Edgar: 255^3 are all possible colors of the 8bit spectrum which includes dimming and desaturation. I talk about the possible positions on the color wheel .

(Mark Kriegsman) #6

Sorry @Georg_Moritz , I didn’t mean to suggest that you didn’t know that wiki page; obviously you’ve given this a fair amount of thought!

And yes, the code you have there definitely achieves what you’re shooting for, I think: more distinct hues “without dimming or desaturation”. I’d definitely encourage you to use it if you like it! As I mentioned above, I use a few different HSV-to-RGB conversions in my own code all the time. The built-in one is just a starting point – albeit a pretty good one.

As for performance of a 680-hue system, the ‘cost’ of having hue be a 16-bit number isn’t too bad by itself.

However, performance is likely to deteriorates because of all the “modulo 680” operations that code will have to do to keep hue in the required range. If you use an actual modulo operation ("%"), that calls the long division routine which takes (typically) a few hundred clock cycles. You can also, of course, use “if” or “while” blocks to keep modulo in range, like
while( hue > 680) hue -= 680;
while( hue < 0 ) hue += 680;
but this also has a direct time impact, as well as indirect performance impact (e.g., the 16-bit compares and math operations use an additional register, which may cause other parts of the code to slow down). Also, having to repeat that block of code frequently (or even a call to it as a function) increases your program size and clutters the source code a little.

So for these reasons, the FastLED built-in HSV color model uses an 8-bit hue. It’s not the most precise hue representation, but it’s a good (fast) balance of engineering trade-offs for people to get started with.

Oh, and here’s a link to the other recent discussion about the default HSV model – this time mostly about power and brightness. https://plus.google.com/u/0/+Techy4198/posts/X8HL5LDzgsk

I’m really glad you’re experimenting with different color spaces and conversions! All kinds of interesting stuff the play with, and lots of interesting design trade-offs to explore!

Please do let us know where you go with it, and do post videos if you can, too!

(Kasper Kamperman) #7

I had the need of 16bit hsv so I made this implementation: https://gist.github.com/kasperkamperman/990cd7424e12af5318f3 Works fast enough on a Teensy for my purposes.