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): Correctly read PNGs with partial alpha #4315

Merged
merged 2 commits into from
Jun 27, 2024

Conversation

lgritz
Copy link
Collaborator

@lgritz lgritz commented Jun 26, 2024

TL;DR: when turning unassociated alpha into associated for gamma-corrected PNG images, we got the exponent wrong for the linearization step, and when doing the same for sRGB images, we didn't linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha (i.e. premultiply the colors) for PNG pixels with partial alpha. The correct thing to do is linearize the unassociated pixel value first, then associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma correction (with a particular gamma), no gamma correction / linear, and explicitly sRGB. Guess what? We were neglecting the case of pngs tagged as sRGB and not doing the linearize/delinearize round trip for those images.

But if that's not enough, also for the gamma case, we were, ugh, swapping the gamma and 1/gamma, resulting in those partial alpha pixels ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or where alpha was 1.0. It only affected "edge" or "partially transparent" pixels with 0 < alpha < 1. But it was definitely wrong before, for which I apologize and hope you'll understand why those pixels are going to change now (hopefully, always always for the better).

While I was at it, I also made the color space handling a little more robust -- instead of just a straight string compare for color space names, use the ColorConfig to check equivalent, which should make us a lot more robust against aliases and whatnot.

Fixes #4314
Closes #4054

TL;DR: when turning unassociated alpha into associated for
gamma-corrected PNG images, we got the exponent wrong for the
linearization step, and when doing the same for sRGB images, we didn't
linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha
(i.e. premultiply the colors) for PNG pixels with partial alpha. The
correct thing to do is linearize the unassociated pixel value first,
then associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma
correction (with a particular gamma), no gamma correction / linear,
and explicitly sRGB. Guess what? We were neglecting the case of pngs
tagged as sRGB and not doing the linearize/delinearize round trip for
those images.

But if that's not enough, also for the gamma case, we were, ugh,
swapping the gamma and 1/gamma, resulting in those partial alpha
pixels ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or
where alpha was 1.0. It only affected "edge" or "partially
transparent" pixels with 0 < alpha < 1. But it was definitely wrong
before, for which I apologize and hope you'll understand why those
pixels are going to change now (hopefully, always always for the
better).

While I was at it, I also made the color space handling a little more
robust -- instead of just a straight string compare for color space
names, use the ColorConfig to check `equivalent`, which should make us
a lot more robust against aliases and whatnot.

Fixes 4314
Closes 4054

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

lgritz commented Jun 27, 2024

Any concerns? I'm going to merge this because I need to do builds with this fix at work. If anybody finds fault with it we can amend with another PR to fix anything remaining. I won't backport it to a release branch until after the patch release on Jul 1, since it's relatively untested. It should see enough use in master to feel safe backporting it for the Aug 1 release.

@lgritz lgritz merged commit 8ae48b2 into AcademySoftwareFoundation:master Jun 27, 2024
26 checks passed
lgritz added a commit to lgritz/OpenImageIO that referenced this pull request Jun 27, 2024
…dation#4315)

TL;DR: when turning unassociated alpha into associated for
gamma-corrected PNG images, we got the exponent wrong for the
linearization step, and when doing the same for sRGB images, we didn't
linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha (i.e.
premultiply the colors) for PNG pixels with partial alpha. The correct
thing to do is linearize the unassociated pixel value first, then
associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma
correction (with a particular gamma), no gamma correction / linear, and
explicitly sRGB. Guess what? We were neglecting the case of pngs tagged
as sRGB and not doing the linearize/delinearize round trip for those
images.

But if that's not enough, also for the gamma case, we were, ugh,
swapping the gamma and 1/gamma, resulting in those partial alpha pixels
ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or where
alpha was 1.0. It only affected "edge" or "partially transparent" pixels
with 0 < alpha < 1. But it was definitely wrong before, for which I
apologize and hope you'll understand why those pixels are going to
change now (hopefully, always always for the better).

While I was at it, I also made the color space handling a little more
robust -- instead of just a straight string compare for color space
names, use the ColorConfig to check `equivalent`, which should make us a
lot more robust against aliases and whatnot.

Fixes AcademySoftwareFoundation#4314
Closes AcademySoftwareFoundation#4054

---------

Signed-off-by: Larry Gritz <lg@larrygritz.com>
lgritz added a commit to lgritz/OpenImageIO that referenced this pull request Jun 27, 2024
…dation#4315)

TL;DR: when turning unassociated alpha into associated for
gamma-corrected PNG images, we got the exponent wrong for the
linearization step, and when doing the same for sRGB images, we didn't
linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha (i.e.
premultiply the colors) for PNG pixels with partial alpha. The correct
thing to do is linearize the unassociated pixel value first, then
associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma
correction (with a particular gamma), no gamma correction / linear, and
explicitly sRGB. Guess what? We were neglecting the case of pngs tagged
as sRGB and not doing the linearize/delinearize round trip for those
images.

But if that's not enough, also for the gamma case, we were, ugh,
swapping the gamma and 1/gamma, resulting in those partial alpha pixels
ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or where
alpha was 1.0. It only affected "edge" or "partially transparent" pixels
with 0 < alpha < 1. But it was definitely wrong before, for which I
apologize and hope you'll understand why those pixels are going to
change now (hopefully, always always for the better).

While I was at it, I also made the color space handling a little more
robust -- instead of just a straight string compare for color space
names, use the ColorConfig to check `equivalent`, which should make us a
lot more robust against aliases and whatnot.

Fixes AcademySoftwareFoundation#4314
Closes AcademySoftwareFoundation#4054

---------

Signed-off-by: Larry Gritz <lg@larrygritz.com>
lgritz added a commit to lgritz/OpenImageIO that referenced this pull request Jun 27, 2024
…dation#4315)

TL;DR: when turning unassociated alpha into associated for
gamma-corrected PNG images, we got the exponent wrong for the
linearization step, and when doing the same for sRGB images, we didn't
linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha (i.e.
premultiply the colors) for PNG pixels with partial alpha. The correct
thing to do is linearize the unassociated pixel value first, then
associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma
correction (with a particular gamma), no gamma correction / linear, and
explicitly sRGB. Guess what? We were neglecting the case of pngs tagged
as sRGB and not doing the linearize/delinearize round trip for those
images.

But if that's not enough, also for the gamma case, we were, ugh,
swapping the gamma and 1/gamma, resulting in those partial alpha pixels
ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or where
alpha was 1.0. It only affected "edge" or "partially transparent" pixels
with 0 < alpha < 1. But it was definitely wrong before, for which I
apologize and hope you'll understand why those pixels are going to
change now (hopefully, always always for the better).

While I was at it, I also made the color space handling a little more
robust -- instead of just a straight string compare for color space
names, use the ColorConfig to check `equivalent`, which should make us a
lot more robust against aliases and whatnot.

Fixes AcademySoftwareFoundation#4314
Closes AcademySoftwareFoundation#4054

---------

Signed-off-by: Larry Gritz <lg@larrygritz.com>
@lgritz lgritz deleted the lg-pngsrgb branch June 28, 2024 20:09
lgritz added a commit to lgritz/OpenImageIO that referenced this pull request Jul 16, 2024
…dation#4315)

TL;DR: when turning unassociated alpha into associated for
gamma-corrected PNG images, we got the exponent wrong for the
linearization step, and when doing the same for sRGB images, we didn't
linearize at all.

WARNING: will change appearance of PNG files with partial alpha.

Details:

Ugh, two separate problems related to how we associate alpha (i.e.
premultiply the colors) for PNG pixels with partial alpha. The correct
thing to do is linearize the unassociated pixel value first, then
associate, then go back to the nonlinear space.

First problem: PNGs have three possible transfer functions: gamma
correction (with a particular gamma), no gamma correction / linear, and
explicitly sRGB. Guess what? We were neglecting the case of pngs tagged
as sRGB and not doing the linearize/delinearize round trip for those
images.

But if that's not enough, also for the gamma case, we were, ugh,
swapping the gamma and 1/gamma, resulting in those partial alpha pixels
ending up a whole lot darker than they should have been.

None of this affected most ordinary PNGs with no alpha channel, or where
alpha was 1.0. It only affected "edge" or "partially transparent" pixels
with 0 < alpha < 1. But it was definitely wrong before, for which I
apologize and hope you'll understand why those pixels are going to
change now (hopefully, always always for the better).

While I was at it, I also made the color space handling a little more
robust -- instead of just a straight string compare for color space
names, use the ColorConfig to check `equivalent`, which should make us a
lot more robust against aliases and whatnot.

Fixes AcademySoftwareFoundation#4314
Closes AcademySoftwareFoundation#4054

---------

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.

PNG alpha premultiplied in texture color space or linear color space?
1 participant