From 11be1631433f252b816802aef1a3cd109bd308c7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Jun 2022 22:47:58 +1000 Subject: [PATCH 1/2] Added apply_transparency() --- Tests/test_image.py | 29 +++++++++++++++++++++++++++++ src/PIL/Image.py | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index 7922505185b..24289bf517d 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -840,6 +840,35 @@ def test_zero_tobytes(self, size): im = Image.new("RGB", size) assert im.tobytes() == b"" + def test_apply_transparency(self): + im = Image.new("P", (1, 1)) + im.putpalette((0, 0, 0, 1, 1, 1)) + assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1} + + # Test that no transformation is applied without transparency + im.apply_transparency() + assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1} + + # Test that a transparency index is applied + im.info["transparency"] = 0 + im.apply_transparency() + assert "transparency" not in im.info + assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 255): 1} + + # Test that existing transparency is kept + im = Image.new("P", (1, 1)) + im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA") + im.info["transparency"] = 0 + im.apply_transparency() + assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1} + + # Test that transparency bytes are applied + with Image.open("Tests/images/pil123p.png") as im: + assert isinstance(im.info["transparency"], bytes) + assert im.palette.colors[(27, 35, 6)] == 24 + im.apply_transparency() + assert im.palette.colors[(27, 35, 6, 214)] == 24 + def test_categories_deprecation(self): with pytest.warns(DeprecationWarning): assert hopper().category == 0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0ba2808f851..7edb9a4c293 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1449,6 +1449,28 @@ def getpalette(self, rawmode="RGB"): rawmode = mode return list(self.im.getpalette(mode, rawmode)) + def apply_transparency(self): + """ + If a P mode image has a "transparency" key in the info dictionary, + remove the key and apply the transparency to the palette instead. + """ + if self.mode != "P" or "transparency" not in self.info: + return + + from . import ImagePalette + + palette = self.getpalette("RGBA") + transparency = self.info["transparency"] + if isinstance(transparency, bytes): + for i, alpha in enumerate(transparency): + palette[i * 4 + 3] = alpha + else: + palette[transparency * 4 + 3] = 0 + self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) + self.palette.dirty = 1 + + del self.info["transparency"] + def getpixel(self, xy): """ Returns the pixel value at a given position. From 70a060e7658469acf33485cb0a6ed198e2d0be2f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Jun 2022 22:31:07 +1000 Subject: [PATCH 2/2] Document apply_transparency() --- docs/reference/Image.rst | 1 + docs/releasenotes/9.2.0.rst | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 2613b6585b3..ed37521fdae 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -123,6 +123,7 @@ methods. Unless otherwise stated, all methods return a new instance of the .. automethod:: PIL.Image.Image.alpha_composite +.. automethod:: PIL.Image.Image.apply_transparency .. automethod:: PIL.Image.Image.convert The following example converts an RGB image (linearly calibrated according to diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index eddbd07a2d7..424fd487a29 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -57,10 +57,13 @@ TODO API Additions ============= -TODO -^^^^ +Image.apply_transparency +^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +Added :py:meth:`~PIL.Image.Image.apply_transparency`, a method to take a P mode image +with "transparency" in ``im.info``, and apply the transparency to the palette instead. +The image's palette mode will become "RGBA", and "transparency" will be removed from +``im.info``. Security ========