Fix loading XPM images with transparency - #9848
Conversation
The 11.3.0 palette refactor stopped adding an entry for 'c None' colours, so palette.index(key) raised ValueError and any XPM with a transparent colour failed to load. Give the transparent colour a real palette entry (black, matching pre-11.3.0 rendering) and store its palette index (P mode) or colour tuple (RGB mode) in info['transparency'].
|
#8967 wasn't merged. I believe you meant #8874.
If the palette in a P mode image can't accommodate a new entry purely for transparency, then I'd be inclined to load the image as PA. Just my personal feeling, I don't love RGB images with transparency. For some reason it seems unintuitive to me. I'd rather load an RGB image with transparency as an RGBA image.
For reference, this is mentioned in the specification.
|
|
Thanks — you're right, #8874 is the palette refactor I meant, apologies for the bad reference. Reworked the >256-colour path per your preference: a transparent XPM now opens as RGBA (opaque entries get alpha 255, the P mode is unchanged: the |
Any XPM that declares a transparent colour via a
c Noneentry — the standard way GIMP and ImageMagick store transparency — fails to load since 11.3.0:Root cause
#8967's palette refactor (7b459a8) replaced the fixed 256-entry black-filled palette list with a dict of only the declared colours, but the
c Nonebranch stores the character key ininfo["transparency"]without adding a palette entry. Decoding then callspalette_keys.index(key)for the transparent character and raises. The>256-colour RGB path (added in 395bd6b) hits the same missing entry.Before 11.3.0 this worked, if partly by accident: the transparent character's ordinal was a valid index into the black-filled palette, so the image loaded and
info["transparency"]was a usable integer index. Verified: 11.2.1 loads (transparency=32), 11.3.0 / 12.2.0 / current main all raise.Fix
Give the transparent colour a real palette entry of black — matching pre-11.3.0 rendering — and store a usable value in
info["transparency"]: the palette index in P mode, the colour tuple in RGB mode.One design point I'd like your view on: for the RGB path,
info["transparency"] = (0, 0, 0)follows the usual colour-key convention but would also key out genuine black pixels in a >256-colour transparent XPM. If you'd rather only fix the crash there and leave transparency unreported in RGB mode, I'm happy to drop that line and the corresponding assertion.Tests
Two tests added to
Tests/test_file_xpm.py, building XPM data in memory withBytesIO(same style astest_truncated_header) — no new binary fixtures:test_transparency: P mode; asserts the palette index ininfo["transparency"], and thatconvert("RGBA")yields(0, 0, 0, 0)for the transparent pixel and the correct opaque colours. Transparency also survives a PNG save/reload round-trip.test_transparency_rgb: 301 colours → RGB mode; asserts mode, transparency tuple and pixel values.Red on main (both fail with the error above), green with the fix (10 passed).
ruffandblackclean on both files;mypy src/PIL/XpmImagePlugin.pyclean.