Skip to content

Read all non-zero transparency from mode 1 PNG images as 255 - #9282

Merged
hugovk merged 1 commit into
python-pillow:mainfrom
radarhere:png_trns
Nov 30, 2025
Merged

hugovk merged 1 commit into
python-pillow:mainfrom
radarhere:png_trns

Conversation

@radarhere

Copy link
Copy Markdown
Member

Resolves #9281

The image in the issue has the transparency index saved as 1, which makes sense for a 1-bit image. However, non-zero values in 1-bit Pillow images have a value of 255.

This changes the read transparency to 255.

@hugovk
hugovk merged commit 2150f08 into python-pillow:main Nov 30, 2025
50 checks passed
@radarhere
radarhere deleted the png_trns branch November 30, 2025 21:09
luketainton pushed a commit to luketainton/repos_webexmemebot that referenced this pull request Jan 5, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [pillow](https://github.com/python-pillow/Pillow) ([changelog](https://github.com/python-pillow/Pillow/releases)) | `<12.0.1,>=12.0.0` → `<12.1.1,>=12.1.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/pillow/12.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pillow/12.0.0/12.1.0?slim=true) |

---

### Release Notes

<details>
<summary>python-pillow/Pillow (pillow)</summary>

### [`v12.1.0`](https://github.com/python-pillow/Pillow/releases/tag/12.1.0)

[Compare Source](python-pillow/Pillow@12.0.0...12.1.0)

<https://pillow.readthedocs.io/en/stable/releasenotes/12.1.0.html>

#### Deprecations

- Deprecate getdata(), in favour of new get\_flattened\_data() [#&#8203;9292](python-pillow/Pillow#9292) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Documentation

- Specify APNG duration type when opening [#&#8203;9368](python-pillow/Pillow#9368) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added release notes for [#&#8203;9350](python-pillow/Pillow#9350) [#&#8203;9366](python-pillow/Pillow#9366) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update ImageMorph documentation [#&#8203;9349](python-pillow/Pillow#9349) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Docs: update major bump cadence [#&#8203;9334](python-pillow/Pillow#9334) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Add release notes for [#&#8203;9070](python-pillow/Pillow#9070) [#&#8203;9320](python-pillow/Pillow#9320) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated Ubuntu version [#&#8203;9306](python-pillow/Pillow#9306) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update macOS tested Pillow versions [#&#8203;9265](python-pillow/Pillow#9265) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Dependencies

- Update harfbuzz to 12.3.0 [#&#8203;9355](python-pillow/Pillow#9355) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update xz to 5.8.2 [#&#8203;9343](python-pillow/Pillow#9343) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated libjpeg-turbo to 3.1.3 [#&#8203;9333](python-pillow/Pillow#9333) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated zlib-ng to 2.3.2 [#&#8203;9324](python-pillow/Pillow#9324) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated libpng to 1.6.53 [#&#8203;9325](python-pillow/Pillow#9325) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update actions/checkout action to v6 [#&#8203;9323](python-pillow/Pillow#9323) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]
- Update dependency mypy to v1.19.0 [#&#8203;9322](python-pillow/Pillow#9322) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]
- Updated libpng to 1.6.51 [#&#8203;9305](python-pillow/Pillow#9305) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated brotli to 1.2.0 [#&#8203;9284](python-pillow/Pillow#9284) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update libimagequant to 4.4.1 [#&#8203;9301](python-pillow/Pillow#9301) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update zlib-ng to 2.3.1, except on manylinux2014 aarch64 [#&#8203;9312](python-pillow/Pillow#9312) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Updated harfbuzz to 12.2.0 [#&#8203;9289](python-pillow/Pillow#9289) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Update github-actions [#&#8203;9277](python-pillow/Pillow#9277) \[@&#8203;[renovate\[bot\]](https://github.com/apps/renovate)]

#### Testing

- Replace pre-commit with prek [#&#8203;9360](python-pillow/Pillow#9360) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Test PyQt6 on Python 3.14 on Windows [#&#8203;9353](python-pillow/Pillow#9353) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Test 32-bit Windows on Windows Server 2022 [#&#8203;9345](python-pillow/Pillow#9345) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct variable type [#&#8203;9335](python-pillow/Pillow#9335) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix `ResourceWarning`s in `selftest.py` [#&#8203;9332](python-pillow/Pillow#9332) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Fix testing good P mode BMP images [#&#8203;9319](python-pillow/Pillow#9319) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Test Python 3.15 pre-release [#&#8203;9331](python-pillow/Pillow#9331) \[[@&#8203;hugovk](https://github.com/hugovk)]
- Test ImageFont.ImageFont, in case freetype2 is not supported [#&#8203;9287](python-pillow/Pillow#9287) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Add Fedora 43 [#&#8203;9290](python-pillow/Pillow#9290) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove Fedora 41 [#&#8203;9260](python-pillow/Pillow#9260) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Type hints

- Add ImageFile context manager [#&#8203;9367](python-pillow/Pillow#9367) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Assert fp is not None [#&#8203;8617](python-pillow/Pillow#8617) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added return type to ImageFile \_close\_fp() [#&#8203;9356](python-pillow/Pillow#9356) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use different variables for Image and ImageFile instances [#&#8203;9316](python-pillow/Pillow#9316) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct variable type [#&#8203;9335](python-pillow/Pillow#9335) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Improve type hints [#&#8203;9317](python-pillow/Pillow#9317) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use different variables for Image and ImageFile instances [#&#8203;9268](python-pillow/Pillow#9268) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Added type hints [#&#8203;9269](python-pillow/Pillow#9269) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct **getitem** return type [#&#8203;9264](python-pillow/Pillow#9264) \[[@&#8203;radarhere](https://github.com/radarhere)]

#### Other changes

- Simplify band splitting [#&#8203;9291](python-pillow/Pillow#9291) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Support saving APNG float durations [#&#8203;9365](python-pillow/Pillow#9365) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Allow 1 mode images in MorphOp [#&#8203;9348](python-pillow/Pillow#9348) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use minimum supported Python version for Lint [#&#8203;9364](python-pillow/Pillow#9364) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Allow for duplicate font variation styles [#&#8203;9362](python-pillow/Pillow#9362) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Call parent verify method [#&#8203;9357](python-pillow/Pillow#9357) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Return LUT from LutBuilder build\_default\_lut() [#&#8203;9350](python-pillow/Pillow#9350) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Simplify WebP code [#&#8203;9329](python-pillow/Pillow#9329) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Use unsigned long for DWORD [#&#8203;9352](python-pillow/Pillow#9352) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Cast to UINT32 before shifting bits [#&#8203;9347](python-pillow/Pillow#9347) \[[@&#8203;radarhere](https://github.com/radarhere)]
- \[pre-commit.ci] pre-commit autoupdate [#&#8203;9318](python-pillow/Pillow#9318) \[@&#8203;[pre-commit-ci\[bot\]](https://github.com/apps/pre-commit-ci)]
- Allow window ID to be passed to ImageGrab.grab() on macOS [#&#8203;9070](python-pillow/Pillow#9070) \[[@&#8203;yankeguo](https://github.com/yankeguo)]
- Apply encoder options when saving multiple PNG frames [#&#8203;9300](python-pillow/Pillow#9300) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Read all non-zero transparency from mode 1 PNG images as 255 [#&#8203;9282](python-pillow/Pillow#9282) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Support writing IFD, SIGNED\_RATIONAL and InkNames TIFF tags [#&#8203;9276](python-pillow/Pillow#9276) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove unused modes [#&#8203;9275](python-pillow/Pillow#9275) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Correct allocating new color to RGBA palette [#&#8203;9313](python-pillow/Pillow#9313) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Close image on ImageFont exception [#&#8203;9304](python-pillow/Pillow#9304) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Reapply "Use macos-latest for iOS arm64 simulator" [#&#8203;9259](python-pillow/Pillow#9259) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Escape period in pre-commit-config [#&#8203;9036](python-pillow/Pillow#9036) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Add Apache-2.0 notice to IcoImagePlugin [#&#8203;8947](python-pillow/Pillow#8947) \[[@&#8203;stefan6419846](https://github.com/stefan6419846)]
- \[pre-commit.ci] pre-commit autoupdate [#&#8203;9288](python-pillow/Pillow#9288) \[@&#8203;[pre-commit-ci\[bot\]](https://github.com/apps/pre-commit-ci)]
- Simplify code now that I;16\* modes are the only IMAGING\_TYPE\_SPECIAL [#&#8203;9263](python-pillow/Pillow#9263) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Remove BytesIO from DdsImagePlugin [#&#8203;9273](python-pillow/Pillow#9273) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix ZeroDivisionError in DdsImagePlugin [#&#8203;9272](python-pillow/Pillow#9272) \[[@&#8203;radarhere](https://github.com/radarhere)]
- Fix warnings [#&#8203;9257](python-pillow/Pillow#9257) \[[@&#8203;radarhere](https://github.com/radarhere)]

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi42OS4yIiwidXBkYXRlZEluVmVyIjoiNDIuNjkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsidHlwZS9kZXBlbmRlbmNpZXMiXX0=-->

Reviewed-on: https://git.tainton.uk/repos/webexmemebot/pulls/542
Co-authored-by: renovate[bot] <[email protected]>
Co-committed-by: renovate[bot] <[email protected]>
marcelometal added a commit to thumbor/thumbor that referenced this pull request Oct 1, 2026
resize() converted 1-bit images to RGB, carrying a tRNS colorkey along as
an integer in info["transparency"], which Pillow's PNG writer rejects for
RGB ("transparency for RGB must be list or tuple"), so a bilevel PNG with
a colorkey answered 500 on any resized PNG request whenever
PILLOW_PRESERVE_INDEXED_MODE was off, and lost its transparency in WebP
output. With the setting on, the image went back to "1" mode with the old
colorkey, so the transparency survived only as a hard edge. A 1-bit image
with a colorkey is now converted to RGBA instead, so the alpha is
resampled with the pixels, and read() quantizes it back like a palette
source, because "1" cannot hold alpha. Pillow before 12.1 also keeps the
tRNS sample of a 1-bit PNG as read (0 or 1) while the pixels are 0 or
255, so convert() never matched a white colorkey; create_image() now
reads it as 0 or 255, as Pillow 12.1 does (python-pillow/Pillow#9282).

The indexed-mode output for such a source changes from a bilevel image
with a colorkey to a palette image with alpha, which is what a palette
source with transparency already produces; a 1-bit source without a
colorkey still comes back as "1", unless a filter added an alpha channel,
which is now kept the same way instead of being dropped. Tests cover the
resized alpha against a direct RGBA resize, the quantized PNG, the RGBA
PNG that used to fail, a white colorkey as older Pillow loads it, and the
plain bilevel round trip.
marcelometal added a commit to thumbor/thumbor that referenced this pull request Oct 1, 2026
resize() converted 1-bit images to RGB, carrying a tRNS colorkey along as
an integer in info["transparency"], which Pillow's PNG writer rejects for
RGB ("transparency for RGB must be list or tuple"), so a bilevel PNG with
a colorkey answered 500 on any resized PNG request whenever
PILLOW_PRESERVE_INDEXED_MODE was off, and lost its transparency in WebP
output. With the setting on, the image went back to "1" mode with the old
colorkey, so the transparency survived only as a hard edge. A 1-bit image
with a colorkey is now converted to RGBA instead, so the alpha is
resampled with the pixels, and read() quantizes it back like a palette
source, because "1" cannot hold alpha. Pillow before 12.1 also keeps the
tRNS sample of a 1-bit PNG as read (0 or 1) while the pixels are 0 or
255, so convert() never matched a white colorkey; create_image() now
reads it as 0 or 255, as Pillow 12.1 does (python-pillow/Pillow#9282).

Without a resize the colorkey was lost in WebP as well: read() converted
every non-RGB(A) image other than P to RGB before encoding, so it now
keeps the alpha whenever the image carries transparency data. The
indexed-mode output for a resized 1-bit colorkey source changes from a
bilevel image with a colorkey to a palette image with alpha, which is
what a palette source with transparency already produces; a 1-bit source
without a colorkey still comes back as "1", unless a filter added an
alpha channel, which is now kept the same way instead of being dropped.
Tests cover the resized alpha against a direct RGBA resize, the quantized
PNG, the RGBA PNG that used to fail, WebP with and without a resize, a
white colorkey as older Pillow loads it, and the plain bilevel round trip.
marcelometal added a commit to thumbor/thumbor that referenced this pull request Oct 1, 2026
resize() converted 1-bit images to RGB, carrying a tRNS colorkey along as
an integer in info["transparency"], which Pillow's PNG writer rejects for
RGB ("transparency for RGB must be list or tuple"), so a bilevel PNG with
a colorkey answered 500 on any resized PNG request whenever
PILLOW_PRESERVE_INDEXED_MODE was off, and lost its transparency in WebP
output. With the setting on, the image went back to "1" mode with the old
colorkey, so the transparency survived only as a hard edge. A 1-bit image
with a colorkey is now converted to RGBA instead, so the alpha is
resampled with the pixels, and read() quantizes it back like a palette
source, because "1" cannot hold alpha. Pillow before 12.1 also keeps the
tRNS sample of a 1-bit PNG as read (0 or 1) while the pixels are 0 or
255, so convert() never matched a white colorkey; create_image() now
reads it as 0 or 255, as Pillow 12.1 does (python-pillow/Pillow#9282).

Without a resize the colorkey was lost in WebP as well: read() converted
every non-RGB(A) image other than P to RGB before encoding, so it now
keeps the alpha whenever the image carries transparency data. The
indexed-mode output for a resized 1-bit colorkey source changes from a
bilevel image with a colorkey to a palette image with alpha, which is
what a palette source with transparency already produces; a 1-bit source
without a colorkey still comes back as "1", unless a filter added an
alpha channel, which is now kept the same way instead of being dropped.
Tests cover the resized alpha against a direct RGBA resize, the quantized
PNG, the RGBA PNG that used to fail, WebP with and without a resize, a
white colorkey as older Pillow loads it, and the plain bilevel round trip.
@aclark4life aclark4life added the PNG label Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Transparent 1-bit PNG images treated as opaque (alpha component 255)

3 participants