Skip to content

Mark Python palette as dirty after conversion to WEB palette - #10008

Open
radarhere wants to merge 1 commit into
python-pillow:mainfrom
radarhere:palette_web
Open

radarhere wants to merge 1 commit into
python-pillow:mainfrom
radarhere:palette_web

Conversation

@radarhere

@radarhere radarhere commented Sep 16, 2026

Copy link
Copy Markdown
Member

Alternative to #9834

#9834 found that after converting an image to P or PA with the WEB palette, the Python palette was updated, but the C palette was not. It solves the problem by load()ing the Python palette into C at once (via putpalette()).

This PR instead just marks the Python palette as dirty, and the loading of the palette into C is lazily deferred until a future load() call.

with Image.open("Tests/images/tiny.png") as im:
im_pa = im.convert("PA")
assert im_pa.getpalette() == im.getpalette()
assert im_pa.getpalette(None) == im.getpalette()

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.

What's the reason for this None? 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's returning the palette in the current palette mode, rather than the default of RGB - https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getpalette

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.

Right, and .getpalette() calls load no matter what. 👍

@akx

akx commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

With this, my test from #9834 fails:

    def test_pa2p_truly_drops_alpha() -> None:
        im = Image.frombytes("P", (2, 1), bytes([0, 1])).convert("PA")
        im.putpalette(bytes([255, 0, 0, 7, 0, 255, 0, 9]), "RGBA")
        im.putalpha(Image.frombytes("L", (2, 1), bytes([240, 220])))
        assert im.get_flattened_data() == ((0, 240), (1, 220))  # Matches the alpha band
        im_p = im.convert("P")
        assert im_p.palette is not None
>       assert im_p.im.getpalettemode() == im_p.palette.mode == "RGB"
E       AssertionError: assert 'RGBA' == 'RGB'
E
E         - RGB
E         + RGBA
E         ?    +

@radarhere

Copy link
Copy Markdown
Member Author

Yes, your test fails because you are accessing the core image directly - im_p.im. If you access it through any Python API, Pillow should call load() internally, and the core image will be updated.

@akx

akx commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Ah yeah, that was it.

assert im_p.palette.mode == "RGB"
print(im_p.im.getpalettemode())
im_p.load()
print(im_p.im.getpalettemode())

prints out RGBA and RGB.

... Matter of separate discussion (and I'm surprised if no one's thought about it or discussed before), but I wonder if im.im should become a deprecated property accessor and im._im the actual field so it's clear you're touching the internal image...

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants