libjxl
libjxl copied to clipboard
apply 8x8 ordered dither to uint decode output
When converting the internal float sample values to uint ones (when JXL_TYPE_UINT8 or JXL_TYPE_UINT16 pixel formats are requested), currently this is done simply by picking the nearest integer value. This can be a problem, especially with 8-bit output on sufficiently bright displays, as noted by @Matt-Gore in https://github.com/libjxl/libjxl/issues/541. It can lead to quite noticeable banding in slow dark gradients.
This adds 8x8 ordered dithering when converting to uint output. Even just 2x2 or 4x4 dithering already alleviates most of the banding issues, but 8x8 is still very cheap. I couldn't measure any speed difference compared to non-dithered.
(btw this was inspired by @veluca93's code from a year ago, see https://gitlab.com/wg1/jpeg-xlm/-/merge_requests/3457)
(btw this was inspired by @veluca93's code from a year ago, see https://gitlab.com/wg1/jpeg-xlm/-/merge_requests/3457)
Unless we are very much in a hurry, I'd prefer to first refactor the Write stages to handle all the cases that are currently handled in decode.cc.
Also, I guess we should decide whether dithering should be mandatory, the default, or explicitly requested when writing to U8.
Finally, and it's a bit of an aside, we should explore how to allow people to use U8 output more for this to be really useful, because i.e. you don't want to dither before color conversions.
(btw this was inspired by @veluca93's code from a year ago, see https://gitlab.com/wg1/jpeg-xlm/-/merge_requests/3457)
Unless we are very much in a hurry, I'd prefer to first refactor the Write stages to handle all the cases that are currently handled in decode.cc.
Also, I guess we should decide whether dithering should be mandatory, the default, or explicitly requested when writing to U8.
Finally, and it's a bit of an aside, we should explore how to allow people to use U8 output more for this to be really useful, because i.e. you don't want to dither before color conversions.
Sure, no urgent need to merge this, I just wanted to put it here so people can try it if they want, and so we don't forget about it.
Probably we need a color management stage before conversion to u8 for u8 output to be more useful. That's true regardless of dithering, but indeed it makes more sense to do dithering after color conversion instead of before.
Dithering should always be used when decreasing the bit depth because this is way more accurate than dropping the least significant bits. If the original was lossless 8 bit, then dithering also won't harm since the pixels will round back to the same values.
And yes, color management should not be done at 8 bit precision if possible. So it should be unrelated to dither and happen earlier on the float values.
Does this need to be included in 1.0?
Does this need to be included in 1.0?
Yes, it's essential for (lossy) 8 bit output and color management. Dithering was also part of Pik and so it should definitely be included in 1.0.
I agree that dithering makes enough of a difference for the common use case of 8-bit sRGB output to consider it essential for 1.0. It's also simple enough to implement and doesn't really come at any decode speed cost, so I see no reason to not do it.
I suggest to use a blue noise matrix instead of an ordered matrix. Precomputed CC0 blue noise textures are available via http://momentsingraphics.de/BlueNoise.html
Superceded by #3090