From fce6c8cc7e17c0f09118ed06b9063de3ff762f74 Mon Sep 17 00:00:00 2001 From: qodelicious <162776175+qodelicious@users.noreply.github.com> Date: Thu, 5 Nov 2026 05:23:14 +0000 Subject: [PATCH 1/2] Fix converting images with transparency to PA --- Tests/test_image_convert.py | 14 ++++++++++++++ src/PIL/Image.py | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index a4c5490102f..f8b28eb86c4 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -317,6 +317,20 @@ def test_p2pa_palette() -> None: assert im_pa.getpalette(None) == im.getpalette() +@pytest.mark.parametrize( + "mode, value", + (("1", 255), ("L", 128), ("I", 0x1234), ("I;16", 0x1234), ("RGB", (1, 2, 3))), +) +def test_trns_to_pa(mode: str, value: int | tuple[int, int, int]) -> None: + im = Image.new(mode, (2, 1)) + im.putpixel((1, 0), value) + im.info["transparency"] = value + + im_pa = im.convert("PA") + assert "transparency" not in im_pa.info + assert im_pa.getchannel("A").get_flattened_data() == (255, 0) + + rgb2xyz_matrix = ( 0.412453, 0.357580, 0.180423, 0, 0.212671, 0.715160, 0.072169, 0, diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c76bebcfb94..72f57904578 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1198,6 +1198,14 @@ def convert_transparency( else: msg = "Transparency for P mode should be bytes or int" raise ValueError(msg) + elif self.mode in {"1", "L", "I", "I;16", "RGB"} and mode == "PA": + # Use the transparency for the alpha channel + alpha = self.convert("RGBA").getchannel("A") + im_without_trns = self.copy() + del im_without_trns.info["transparency"] + new_im = im_without_trns.convert(mode, dither=dither) + new_im.putalpha(alpha) + return new_im if mode == "P" and palette == Palette.ADAPTIVE: im = self.im.quantize(colors) From c798abce587786e6b39c5103abf7c4991e4c2b2c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 6 Nov 2026 14:05:58 +1100 Subject: [PATCH 2/2] Rearrange code --- Tests/test_image_convert.py | 2 +- src/PIL/Image.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index f8b28eb86c4..7a8a9e78c89 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -319,7 +319,7 @@ def test_p2pa_palette() -> None: @pytest.mark.parametrize( "mode, value", - (("1", 255), ("L", 128), ("I", 0x1234), ("I;16", 0x1234), ("RGB", (1, 2, 3))), + (("1", 255), ("L", 128), ("RGB", (1, 2, 3)), ("I;16", 0x1234), ("I", 0x12345)), ) def test_trns_to_pa(mode: str, value: int | tuple[int, int, int]) -> None: im = Image.new(mode, (2, 1)) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 72f57904578..8c837a5f661 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1199,12 +1199,10 @@ def convert_transparency( msg = "Transparency for P mode should be bytes or int" raise ValueError(msg) elif self.mode in {"1", "L", "I", "I;16", "RGB"} and mode == "PA": - # Use the transparency for the alpha channel - alpha = self.convert("RGBA").getchannel("A") im_without_trns = self.copy() del im_without_trns.info["transparency"] new_im = im_without_trns.convert(mode, dither=dither) - new_im.putalpha(alpha) + new_im.putalpha(self.convert("RGBA").getchannel("A")) return new_im if mode == "P" and palette == Palette.ADAPTIVE: