Skip to content

Scale tRNS grey level for 2-bit and 4-bit grayscale PNGs - #10320

Open
LemonCondor wants to merge 2 commits into
python-pillow:mainfrom
LemonCondor:fix/png-low-bit-grayscale-trns
Open

LemonCondor wants to merge 2 commits into
python-pillow:mainfrom
LemonCondor:fix/png-low-bit-grayscale-trns

Conversation

@LemonCondor

Copy link
Copy Markdown
Contributor

Changes proposed in this pull request:

  • For 2-bit and 4-bit grayscale PNGs, the tRNS grey level is stored in sample units (0-3 / 0-15), but the image is unpacked to 8-bit L, so info["transparency"] never matched any pixel and transparency was lost on convert("LA"). Mode 1 was already scaled.
  • Mask the value to the bit depth, as the PNG spec requires, and scale it to 0-255. Files saved by Pillow with bits= still read back as 255.
  • Add tests for 2-bit and 4-bit images.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@Muguett-DBY Muguett-DBY left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the intermediate gray levels too: generated 2-bit and 4-bit grayscale PNGs containing every sample value, then used each possible value as the transparency key. All 20 cases produced the expected luminance and alpha, including black and intermediate values.

This used the exact chunk_tRNS method from d8fff8f with the existing Pillow 12.3 decoder on Windows; I did not build the branch or run its full test suite.

@radarhere

Copy link
Copy Markdown
Member

Do you have an image that you found in the wild that demonstrates this? It would be helpful to have one for the record.

@LemonCondor

Copy link
Copy Markdown
Contributor Author

Yes, PngSuite's tbbn0g04.png shows it: 4-bit greyscale, with a tRNS grey level of 15 (white).

With Pillow 13.0.0 it opens as mode L with info["transparency"] == 15. After unpacking to 8-bit no pixel has the value 15, since the 464 white pixels are 255. So convert("LA") leaves every pixel opaque (0 of 1024 have alpha 0), while the PngSuite reference renders that background as transparent. With the value scaled to 255 as in this PR, those 464 pixels become transparent.

PngSuite's licence allows redistribution, so I can add the file to Tests/images and use it in the test if you'd like.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖-assisted AI-assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants