Closed RunDevelopment closed 3 months ago
Hi!
Thanks for submitting the issue. Yeah, I agree that interpolating the already rounded values will add more error to the output. I'll try to experiment with the values you've provided.
@RunDevelopment so I tested your "magic" values and they match the floating-point interpolation perfectly!
Also because we now avoid integer division by 3 - your version is even slightly faster.
I'm going to adopt your changes, just wanted to ask you - would you mind if I add you to the CREDITS:
section of the header next to Aras Pranckevicius ?
Also are these proper credentials to put there ? Michael Schmidt (@RunDevelopment)
Thank you! Yes, those credentials are correct. Just put me wherever you see fit :)
Also because we now avoid integer division by 3 - your version is even slightly faster.
Nice 🎉
3b71535 is in, thanks for your help!
Hi! While analyzing the Rust source port (
bcdec_rs
) of this library, I discovered that BC1 and BC4 are not rounded correctly compared to doing all calculations in float32 and converting the image to 8 bit ints at the very end. I then confirmed that the Rust source port simply faithfully recreated the issues from the original bcdec C library. While there's already an issue for BC4 (#12), there wasn't one for BC1 yet. (Here's the issue I made for the Rust source port for reference: https://github.com/ScanMountGoat/image_dds/issues/17)What's the issue?
Colors are not rounded correctly. I define "correct" here are doing all calculations in float32 and then converting the decoded float32 image to uint8 at the very end. I'm using this definition, because that's how DirectXTex is implemented, and it's how Paint.net and GIMP decode DDS images. So this definition of correct is widely-used in modern software.
How is it incorrect: the R, G, and B color channels can be off by to +-1. The error is only a single bit. While small, it's still noticeable. The problem is that the error is not the same between channels. So e.g. the R and B channels can have an error of -1 while the G channel can have an error of +1, which will cause a noticeable green tint.
Here's an example image showing this exact problem. All pixels in this image are supposed to have the color RGB=47 47 47, as is the case when opened in Paint.net and Gimp, but bcdec will decode all pixels as RGB=46 48 46. BC1_UNORM_SRGB-47.zip
These rounding errors are also not rare. Here's a test image and all error magnified so we can see them:
The cause of the issue
The cause is that the issue is that bcdec uses the already-rounded 8-bit values for
color0
andcolor1
to calculate the values forcolor2
andcolor3
.E.g. if the 5-bit value of
r
is 29, then the 8-bit value is 238.55 which is rounded to 239. That's not a problem forcolor0
andcolor1
, because that's just want correct rounding does, but it is a problem forcolor2
andcolor3
. This rounding forcolor0/1
created an error of 0.45 which is then multiplied by 2 when calculatingcolor2/3
giving us an error of 0.9. This error is capable of changing the rounding ofcolor2
andcolor3
.The fix
The fix is to use the original 5/6-bit values for interpolation instead of the rounded 8-bit values. E.g. if
r0_5
andr1_5
are the 5-bit values ofcolor0
andcolor1
respectively, then the RGB values ofcolor_2 = 2/3*color_0 + 1/3*color_1
can be calculated as(Note that
g
must be calculated with 32-bit integers, while 16-bit integers are enough forr
andb
).I got these constants using a script I made that uses a brute-forces search. This script also found the same constants bcdec uses for the 5/6 bit to 8 bit conversion used for
color0
andcolor1
.Note that BC1A mode requires different constants. Here
color_2 = 1/2*color_0 + 1/2*color_1
can be calculated as(Just as before, 32 bit for
g
and 16 bit forr
andb
.)