From 39c515261fccef73ac4931b7981cda3d3ea55419 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 29 Jul 2026 18:43:09 +0300 Subject: [PATCH 1/4] Add xfailing test for pa2p quirk --- Tests/test_image_convert.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index f57d83dac1a..a06421a308a 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -357,3 +357,16 @@ def test_matrix_identity() -> None: # Assert # No change assert_image_equal(converted_im, im) + + +@pytest.mark.xfail(reason="zombie colors from palette shouldn't reappear") +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" + rgba_data = im_p.convert("RGBA").get_flattened_data() + assert rgba_data == ((255, 0, 0, 255), (0, 255, 0, 255)) From 31693e6e68a7da52f7eb77ddb6376d65c0afd1ce Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 29 Jul 2026 18:44:24 +0300 Subject: [PATCH 2/4] Ensure Image and core palettes are in sync after PA conversion (alphas dropped) Co-authored-by: Andrew Murray --- Tests/test_image_convert.py | 1 - src/PIL/Image.py | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index a06421a308a..900d5102f3b 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -359,7 +359,6 @@ def test_matrix_identity() -> None: assert_image_equal(converted_im, im) -@pytest.mark.xfail(reason="zombie colors from palette shouldn't reappear") 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") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ddbaec20a05..1c1f5af0f01 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1242,9 +1242,8 @@ def convert_transparency( new_im = self._new(im) if mode in ("P", "PA") and palette != Palette.ADAPTIVE: - from . import ImagePalette - - new_im.palette = ImagePalette.ImagePalette("RGB", im.getpalette("RGB")) + # Install the original image's palette into the new copy + new_im.putpalette(im.getpalette("RGB")) if delete_trns: # crash fail if we leave a bytes transparency in an rgb/l mode. del new_im.info["transparency"] From b3d64e2aa24a8d0e413de72de1d18ad2c899c0b9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Aug 2026 16:32:55 +1000 Subject: [PATCH 3/4] Rearrange code --- Tests/test_image_convert.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 900d5102f3b..0acec3125de 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -290,6 +290,20 @@ def test_p2pa_palette() -> None: assert im_pa.getpalette() == im.getpalette() +def test_pa2p_palette() -> None: + im_pa = Image.frombytes("PA", (2, 1), bytes([0, 255, 1, 255])) + im_pa.putpalette(bytes([255, 0, 0, 7, 0, 255, 0, 9]), "RGBA") + + im_pa.putalpha(Image.frombytes("L", (2, 1), bytes([240, 220]))) + assert im_pa.get_flattened_data() == ((0, 240), (1, 220)) # Matches the alpha band + + im_p = im_pa.convert("P") + assert im_p.palette is not None + assert im_p.palette.mode == im_p.im.getpalettemode() == "RGB" + im_rgba = im_p.convert("RGBA") + assert im_rgba.get_flattened_data() == ((255, 0, 0, 255), (0, 255, 0, 255)) + + rgb2xyz_matrix = ( 0.412453, 0.357580, 0.180423, 0, 0.212671, 0.715160, 0.072169, 0, @@ -357,15 +371,3 @@ def test_matrix_identity() -> None: # Assert # No change assert_image_equal(converted_im, im) - - -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" - rgba_data = im_p.convert("RGBA").get_flattened_data() - assert rgba_data == ((255, 0, 0, 255), (0, 255, 0, 255)) From de315658699fce7dfdf306334dc9d9e2e21cf356 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Aug 2026 16:45:23 +1000 Subject: [PATCH 4/4] Test core image palette directly --- Tests/test_image_convert.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 0acec3125de..eee5127a845 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -298,10 +298,8 @@ def test_pa2p_palette() -> None: assert im_pa.get_flattened_data() == ((0, 240), (1, 220)) # Matches the alpha band im_p = im_pa.convert("P") - assert im_p.palette is not None - assert im_p.palette.mode == im_p.im.getpalettemode() == "RGB" - im_rgba = im_p.convert("RGBA") - assert im_rgba.get_flattened_data() == ((255, 0, 0, 255), (0, 255, 0, 255)) + assert im_p.im.getpalettemode() == "RGB" + assert im_p.im.getpalette("RGB") == bytes([255, 0, 0, 0, 255, 0]) rgb2xyz_matrix = (