Skip to content

Preserve palette and transparency in zero dimension ImageOps results - #10299

Merged
radarhere merged 4 commits into
python-pillow:mainfrom
jdotc1:fix/empty-contain-palette-20261023
Oct 24, 2026
Merged

radarhere merged 4 commits into
python-pillow:mainfrom
jdotc1:fix/empty-contain-palette-20261023

Conversation

@jdotc1

@jdotc1 jdotc1 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

ImageOps.contain() returns a newly created image when the result has zero width or height. For palette images this loses both palette state and transparency, so pad() can fill a very thin image with opaque black instead of the selected palette color.

Use a zero-area crop in the three empty-return branches. This preserves the existing output dimensions while carrying the source palette and metadata through the established copy path. Add synthetic regressions for P/PA images, RGB/RGBA palettes, transparent indices, full-palette allocation, and lazy PNG/GIF input, plus a release note.

Validation on Python 3.12 with the native core built from this source revision:

  • The new regression selection produces 11 expected failures and one passing control before the fix.
  • python selftest.py: 59 passed.
  • python -m pytest -q Tests/test_imageops.py: 72 passed.
  • Crop, palette, PNG and GIF modules: 219 passed.
  • Applicable pinned pre-commit hooks pass on all three changed files; mypy passes for the production and test modules.
  • The complete patch applies to a clean source checkout and the ImageOps tests pass there.

The empty paths now call load() through crop(), including for non-paletted sources. Lazy images can therefore incur decoding work or raise decoder errors that the previous empty shortcut bypassed. The full optional-codec/platform matrix was not executed locally.

CI follow-up: correct the release-note heading underline and move the MinGW job to MSYS2 UCRT64, including all dependency prefixes. The MINGW64 repository no longer supplies python-pyqt6; UCRT64 supplies it and all 18 other dependencies used by this job. This retains Qt test coverage. The changed release-note section builds with Sphinx warnings treated as errors, sphinx-lint passes, and the workflow passes GitHub Actions schema validation. Windows runtime validation remains with hosted CI.

@radarhere radarhere added the 🤖-assisted AI-assisted label Oct 24, 2026
@radarhere
radarhere force-pushed the fix/empty-contain-palette-20261023 branch from 4cbaaed to cc0c246 Compare October 24, 2026 01:38
@radarhere

Copy link
Copy Markdown
Member

This PR is a sequel to #9673. Your concerns about ImageOps.contain() also apply to fit() and cover(), so I've pushed fixes for them as well.

I don't think this needs release notes. Creating an empty image in ImageOps contain() is pretty niche, and doing anything afterwards that would use palettes or transparency is even more niche.

@radarhere
radarhere force-pushed the fix/empty-contain-palette-20261023 branch from cc0c246 to a75824c Compare October 24, 2026 01:53
@radarhere

Copy link
Copy Markdown
Member

I appreciate the attempt to fix MinGW, but it's a bit out of scope for this PR.

@radarhere radarhere changed the title Preserve palettes and transparency in empty contain results Preserve palettes and transparency in zero dimension ImageOps results Oct 24, 2026
@radarhere radarhere changed the title Preserve palettes and transparency in zero dimension ImageOps results Preserve palette and transparency in zero dimension ImageOps results Oct 24, 2026
@radarhere
radarhere merged commit 69f0a52 into python-pillow:main Oct 24, 2026
49 of 50 checks passed
@radarhere

Copy link
Copy Markdown
Member

I cherry-picked your MinGW commit into #10300

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.

2 participants