Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(png): Improve png write with alpha is low #3985

Merged
merged 1 commit into from
Sep 22, 2023

Conversation

lgritz
Copy link
Collaborator

@lgritz lgritz commented Sep 13, 2023

We found that PNG output (which is required to be unassociated alpha) had precision problems with low alpha values because it quantized to the final bit depth before doing the alpha deassociation.

Explanation:

PNG requires associated alpha to be stored in the file. So if you have (unassociated) pixel value [0.00235, 0.00106, 0.00117, 0.0025], that means that we need to divide the colors by alpha before storing in the file, so that's [0.94, 0.424, 0.468, 0.0025] if you have full precision. But you don't, because PNG only supports uint8 and uint16. For uint8, this should end up as [240, 108, 119, 1].

But it turns out that our png writer does the integer conversion before the un-premultiplication, so actually we first quantize from [0.00235, 0.00106, 0.00117, 0.0025] to [1 0 0 1], and THEN we un-premultiply, giving us the [255, 0, 0, 1] that made you think "Why is that red value clipped? And where did green and blue go?"

The solution, then, is to deassociate as floats first, then quantize to the final integer data type.

Also add a test for this, as well as a test that verifies correctly writing of 16 bit files that I had some problems with in the last png PR.

We found that PNG output (which is required to be unassociated alpha)
had precision problems with low alpha values because it quantized to the
final bit depth before doing the alpha deassociation.

Explanation:

PNG requires associated alpha to be stored in the file. So if you have
(unassociated) pixel value [0.00235, 0.00106, 0.00117, 0.0025], that
means that we need to divide the colors by alpha before storing in the
file, so that's [0.94, 0.424, 0.468, 0.0025] if you have full
precision. But you don't, because PNG only supports uint8 and uint16.
For uint8, this should end up as [240, 108, 119, 1].

But it turns out that our png writer does the integer conversion
before the un-premultiplication, so actually we first quantize from
[0.00235, 0.00106, 0.00117, 0.0025] to [1 0 0 1], and THEN we
un-premultiply, giving us the [255, 0, 0, 1] that made you think "Why
is that red value clipped? And where did green and blue go?"

The solution, then, is to deassociate as floats first, then quantize
to the final integer data type.

Also add a test for this, as well as a test that verifies correctly
writing of 16 bit files that I had some problems with in the last
png PR.

Signed-off-by: Larry Gritz <lg@larrygritz.com>
@lgritz
Copy link
Collaborator Author

lgritz commented Sep 20, 2023

Any comments or objections?

@lgritz
Copy link
Collaborator Author

lgritz commented Sep 22, 2023

Merging.

@lgritz lgritz merged commit 5320fbd into AcademySoftwareFoundation:master Sep 22, 2023
23 checks passed
@lgritz lgritz deleted the lg-pngwrite branch September 25, 2023 04:29
lgritz added a commit to lgritz/OpenImageIO that referenced this pull request Sep 27, 2023
…ion#3985)

We found that PNG output (which is required to be unassociated alpha)
had precision problems with low alpha values because it quantized to the
final bit depth before doing the alpha deassociation.

Explanation:

PNG requires associated alpha to be stored in the file. So if you have
(unassociated) pixel value [0.00235, 0.00106, 0.00117, 0.0025], that
means that we need to divide the colors by alpha before storing in the
file, so that's [0.94, 0.424, 0.468, 0.0025] if you have full precision.
But you don't, because PNG only supports uint8 and uint16. For uint8,
this should end up as [240, 108, 119, 1].

But it turns out that our png writer does the integer conversion before
the un-premultiplication, so actually we first quantize from [0.00235,
0.00106, 0.00117, 0.0025] to [1 0 0 1], and THEN we un-premultiply,
giving us the [255, 0, 0, 1] that made you think "Why is that red value
clipped? And where did green and blue go?"

The solution, then, is to deassociate as floats first, then quantize to
the final integer data type.

Also add a test for this, as well as a test that verifies correctly
writing of 16 bit files that I had some problems with in the last png
PR.

Signed-off-by: Larry Gritz <lg@larrygritz.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant