You need triangular noise, and have it somehow be aware of sRGB. Why? Because otherwise your variance is not uniform in your dithering spectrum, and gives noticeable artifacts. So it's not correct to only choose from floor(c) and floor(c)+1, you actually need to choose from round(c) - 1, round(c) and round(c) + 1 with the right probabilities.
I've gotten stuck on this for days, and even tried to ask on stackexchange, but to no avail: https://math.stackexchange.com/questions/3200249/maintaining...
Yet this is important, as dithering with triangular noise gives vastly superior dithering. This is 5 grayscale values with my best found method (which is not fully mathematically correct, but a close approximation):
The yellow line indicates sRGB signal, the red the error, and the blue the variance. Now compare it to OP's method:
Notice the significant banding? It's because there are regions where the variance of the dither essentially hits zero, producing a (near) pure color band that's very noticeable.
also, I have uBlock origin, and the imgur .png link shows me the page without the png... (EDIT: I just used wget on the exact same URL and it just downloads the png, it boggles my mind why firefox doesn't just show the PNG, or is this imgur detecting user agent etc?)
Also, that's a good observation, that the spatial distribution of variance is noticable.
Would you mind sharing the code for the last 2 plots you generated?
For incorporating gamma, and using the original HN post terminology, what prevents you from generalizing your technique to the optical (linear) domain instead of the electronic (gamma) domain?
Is it because you need to devise a new family of probability functions so that the expectation value in the optical domain is linearly proportional to the input optical value while having the bins spread according to the gamma curve?
I just quickly edited my code to have OP's method, but didn't keep it after generating the plot. The code is generally a mess, because I kept experimenting with different methods, but this is the current version: https://www.shadertoy.com/view/wlX3RS
> For incorporating gamma, and using the original HN post terminology, what prevents you from generalizing your technique to the optical (linear) domain instead of the electronic (gamma) domain?
Because I believe it is only due to linear effects that 'dither + quantize' is a legal proper operation that does what you want. As soon as you enter a non-linear space this no longer holds, and you must see "dithered quantization" as a single operation, rather than adding independent noise followed by an oblivious quantization.
So the issue is that our quantization must happen in sRGB space - our output space. Yet the probability distribution according to which we'd like to quantize follows rules formulated in a linear space, which requires a non-trivial and non-linear conversion.
There is a very large family of probability distributions that have the appropriate mean (mean computed in linear space), but I believe if we also try to minimize the fluctuation in variance there should pop out a unique solution. In a fully linear world this is the triangular distribution.
What function family p(c,k) for integers k satisfies the following conditions:
ExpectationValue < p(c,k) * e2o(k) > = c (for nearly all c)
i.e. the expected variance should be constant in the perceptual (gamma compressed) domain, while the expected optical intensity should be the non-dithered original input optical intensity c.
May I ask how you came up with the current probability distribution? you call it triangular but it's not really a hat distribution.
I don't sum over all k, I only sum over k - 1, k and k + 1 where k is the closest integer to c.
I don't believe you can get constant variance, due to the endpoints, where if you wish to have a correct mean (non-negotiable) the variance must approach 0. Therefore I believe the optimal variance curve to be constant in the middle with a small curve towards 0 at the endpoints.
> May I ask how you came up with the current probability distribution? you call it triangular but it's not really a hat distribution.
The reason it's called this way is because in a linear space this effect can be achieved by a regular additive dither using a noise source that follows the [triangular distribution](https://en.wikipedia.org/wiki/Triangular_distribution). Why? I don't know I got from reading some audio dithering material. But plotting the results does give beautiful near-constant variance dithering, if we ignore sRGB and assume a linear space, except at the endpoints. This can be corrected, see this question: https://computergraphics.stackexchange.com/a/8777/10515. That's the origin of the plotting code as well.
You can recover the correct linear p(c, k) by doing a convolution of the triangular distribution with a unit box: https://i.imgur.com/2AKVS4B.png
I will try to write it out in LaTeX, and will link when finished.
Sadly the shadertoy site doesn't work on my smarthone (it complains that uint's are not available before mobile GLSL 3.0, I tried modifying them all to normal int's, but then got stuck on the hash function through which I assume you generate pseudo random numbers.
The full correct formula is:
Interestingly gamma correction also includes a linear component. For more info on why:
One possible theory why there's a linear term is so that negative values are well-defined. In consumer HDMI, reference black is 10 (as opposed to 0 in sRGB), and reference white is 235 (as opposed to 255 in sRGB).
Since there no doubt be experts here, I'd love to see an authoritative answer. (I'm considering writing an explanation of sRGB and if so would really like to get this right)
It took a lot of Googling, but I finally found an article that explains why, in case anyone else is interested:
"The 601 system allows colors that are darker than black and brighter than white. This is especially important for cameras, because you may occasionally shoot an object that has a bright spot that is “hotter” than legal white, and might want a way to later recover the detail in this hot spot. Going darker than broadcast black is also used at times for synchronization signals, as well as some primitive keying applications such as “superblack” where systems mask or key out areas of an image “blacker” than black."
Lots of consumer electronics is designed with these kind of rules.
> The effect of the above equations is to closely fit a straightforward gamma 2.2 curve with an slight offset to allow for invertability in integer math. Therefore, we are maintaining consistency with the gamma 2.2 legacy images and the video industry as described previously.
And to this I'll add another speculative answer, similar to others that have been advanced, but I'm not sure it's ever been explicitly expressed: it means that a piecewise linear approximation in either direction can be done with good accuracy and not a huge number of segments.
Of course, if you want, you can figure out the equivalent “chain rule” of your operation, but realistically sRGB also mostly exists to compress colors into 8-bits per channel which doesn’t allow for great manipulation. Fun fact: this is why LRBni had a “load 8-bit as sRGB and upconvert to linear floating point”.
Some more information here: https://www.colour-science.org/posts/srgb-eotf-pure-gamma-22...
Is your assumption that you're diffusing error linearly in Morton or Hilbert curves? Because I'm pretty sure that's going to be outperformed in quality by any common 2D error diffusion pattern.
If they 'fixed' the bug, and rendered with correct physical-aware alpha blending, a bunch of websites wouldn't look correct anymore.
ie. "It works great, has a positive benefit on the user experience, but adds so much system complexity, limits other operations, and is so hard to implement right, that it fell out of use"
Today subpixel anti-aliasing is implemented mostly for text only - we've given up implementing it for 2d/3d graphics.
The change would be fairly invasive inside the Blink/Webkit rendering engine if you wanted to support both physically aware and non-physically aware blending simultaneously in the same compositing layer (I suspect you'd have to split everything into multiple GPU surfaces - one for physical blending, one for 'wrong' blending, and more if any of those objects are interleaved in zIndex - that might use a lot of RAM in some cases).
A per-page blending mode would probably be easy to implement.