So, remember the eyes bleeding thing?

So, remember the eyes bleeding thing? Mark and I have been trying to tackle fixing the problem where scale8(255,255) == 254 instead of 255, but in a way that didn’t kill performance on avr. We’ve got a solution, and now we need to decide on the best way to introduce it into the world. In the meantime, I wrote up the path we went through and the things we tried, for those that are interested in what it’s like trying to squeeze blood from a stone:

(Why fix this? Well, we use scale8 internally under the hood for setBrightness, it meant that when you called setBrightness(255) all your 255’s were 254’s, likely to piss off POV folks[1]. We also use scale8 inside of our hsv conversion, as well as in a number of other places. The error would accumulate over those multiple layers in ways that are not fun).

[1] Again, don’t use WS2811’s for POV because even with out FastLED’s scale8 issue, guess what? WS2811’s gap between every pwm cycle. Assholes.
https://gist.github.com/focalintent/0d2de5159f6f9d47fcec

Quite impressive. I would have gone with the 10 cycle version, put it behind a flag, and drunk away my shame. Very glad there are people like you to give me excellent low level tools.

“One could argue that we don’t know when to let go – to good effect.”

You should see the chat log from when Mark and I were banging our heads about this.

It spans 16 hours.

Amazing write up and the work behind it is even better. Thanks guys.

Thanx for the very inspiring step by step explanation! Great work guys!

Fantastic work! Thanks for all the hard work you’ve put into this library.

Ow.

Realistically, 30% mystical speak reading through this for me.

Please don’t get out the Voodoo dolls and I promise to be good and appreciate all the hard work you gents do for this community!

This is quite interesting as I sometimes like to use foreground colours layered over background colours. The challenge is that rotating the hue of a background colour with a CHSV brightness <16 is not at all smooth (and may even be black). In discussions with Mark and Dan, they’ve demonstrated that CHSV(0,255,15) translates to CRGB(1,0,0) which gives you NO room for a smooth hue rotation. Dan’s tests showed that CHSV(0,255,32) gives you a CRGB(5,0,0), which at least allows you a bit of hue rotation. Just make sure that the foreground colours are BRIGHT.

@Andrew_Tuline and you’ve inadvertently stumbled on the reason why scale8’s “gap” (as I like to call it) has been itching at me for years. @Mark_Kriegsman and I have a couple of small things to wrap up for 3.1 so that we can push it over to master, and then fixing this across the board (now that I have a fast solution for avr) is likely to be the first thing on deck after that (intermixed w/full support for the m0 platforms - I feel like I just leveled up big time in my gcc asm foo this morning, so now this is a tackleable problem)

@Daniel_Garcia Oh, nice. I’d noticed the H quantisation at low V. Glad it’s on the radar.

Well, let’s get technical for a second here. There will always be some quantisation because the LED pixels themselves have, at best, 8 bits per channel of color data. If we decided to run at 1/2 brightness, now we only have 7 bits per channel; at 1/8th brightness, we only have 5 bits per channel, and so on.

And when we drop the brightness (or V, related issue) very low, like say “8”, then there are, at best, only three bits of color per channel: only eight levels of Red, eight levels of Green, and eight levels of Blue that can be mixed to make the resulting colors. And one of the levels in each case is “off”/“black”, so really there’s even less dynamic range to work with.

This is all exacerbated here by the fact that in order to provide a roughly linear scale for of apparent brightness for “V”, the FastLED HSV code has a built-in gamma correction of 2.0. And the practical upshot of that is that when you put medium-low “V” values (eg, less than 30) into an HSV value, it gets converted to a lower numeric brightness as an RGB value. Dan dumped some of the actual numbers here so you can see: https://github.com/FastLED/FastLED/issues/196#issuecomment-127462961

So there’s sort of an ‘upper limit’ on color rendering quality given the dual constraints of (1) 8-bit-per-channel digital LED outputs, and (2) gamma correction on the “V” axis to make apparent brightness’ more linear.

You can retain more useable bits if you draw your colors into the leds[] array at “full brightness” (high “V”), and then use the FastLED.setBrightness() control to dim the whole strip down. That retains roughly another two bits of dynamic range because of the FastLED temporal dithering.

But if you formulate HSV colors with low “V”, you’re going to get quantisation no matter what. Actually, for that matter, if you formulate RGB colors with low values, you’re going to get quantisation, too!

At a certain point it all comes down to the fact that when the LEDs are dim, your eye can see big differences between (1,0,0) and (2,0,0) and (3,0,0), and the color (0,0,3) looks very ‘far away’ from (1,0,2). The LED strips use integer steps, and at low brightness your eye can see them clearly.

Obviously, despite all that, Dan and I are always trying to make your animations come out as awesomely as we can… and that’s what started us down the “scale8” rabbit hole again!

I had no idea about the built-in gamma curve; I was actually using a CHSVGradient16 to simulate one! Said gradient is quite pointless now, but it’s not going anywhere unless I get my hands on a time machine…