From 0f8d66bc96dcd7ecfcdce54c1a2558d8b6ed63e5 Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Tue, 19 Apr 2016 09:31:15 +0200 Subject: [PATCH 1/4] add failing test for #1838; extend tests for current transparency behavior --- Tests/test_file_png.py | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index f6a47c464..7a061b09f 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -203,6 +203,7 @@ class TestFilePng(PillowTestCase): def test_load_transparent_rgb(self): test_file = "Tests/images/rgb_trns.png" im = Image.open(test_file) + self.assertEqual(im.info["transparency"], (0, 255, 52)) self.assert_image(im, "RGB", (64, 64)) im = im.convert("RGBA") @@ -215,16 +216,65 @@ class TestFilePng(PillowTestCase): in_file = "Tests/images/pil123p.png" im = Image.open(in_file) + # 'transparency' contains a byte string with the opacity for each palett entry + self.assertEqual(len(im.info["transparency"]), 256) + test_file = self.tempfile("temp.png") im.save(test_file) + # check if saved image contains same transparency + im = Image.open(test_file) + self.assertEqual(len(im.info["transparency"]), 256) + + self.assert_image(im, "P", (162, 150)) + im = im.convert("RGBA") + self.assert_image(im, "RGBA", (162, 150)) + + # image has 124 uniqe qlpha values + self.assertEqual(len(im.split()[3].getcolors()), 124) + def test_save_p_single_transparency(self): in_file = "Tests/images/p_trns_single.png" im = Image.open(in_file) + # pixel value 164 is full transparent + self.assertEqual(im.info["transparency"], 164) + self.assertEqual(im.getpixel((31, 31)), 164) + test_file = self.tempfile("temp.png") im.save(test_file) + # check if saved image contains same transparency + im = Image.open(test_file) + self.assertEqual(im.info["transparency"], 164) + self.assertEqual(im.getpixel((31, 31)), 164) + self.assert_image(im, "P", (64, 64)) + im = im.convert("RGBA") + self.assert_image(im, "RGBA", (64, 64)) + + self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0)) + + # image has 876 transparent pixels + self.assertEqual(im.split()[3].getcolors()[0][0], 876) + + def test_save_p_transparent_black(self): + # check if solid black image with full transparency + # is supported (check for #1838) + im = Image.new("RGBA", (10, 10), (0, 0, 0, 0)) + self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))]) + + im = im.convert("P") + test_file = self.tempfile("temp.png") + im.save(test_file) + + # check if saved image contains same transparency + im = Image.open(test_file) + self.assertEqual(len(im.info["transparency"]), 256) + self.assert_image(im, "P", (10, 10)) + im = im.convert("RGBA") + self.assert_image(im, "RGBA", (10, 10)) + self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))]) + def test_save_l_transparency(self): in_file = "Tests/images/l_trns.png" im = Image.open(in_file) From c773688f37da26d997c9a70c5e53c4aee7d64b82 Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Tue, 19 Apr 2016 09:52:01 +0200 Subject: [PATCH 2/4] improve simple palette detection for PNG - reverts change #1239 which could remove transparency from valid images (see test_save_p_transparent_black test case) - improves simple palette detection to handle images from #1238 --- PIL/PngImagePlugin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index cc58fbbc4..7734fdeda 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -74,8 +74,7 @@ _MODES = { } -_simple_palette = re.compile(b'^\xff+\x00\xff*$') -_null_palette = re.compile(b'^\x00*$') +_simple_palette = re.compile(b'^\xff*\x00\xff*$') # Maximum decompressed size for a iTXt or zTXt chunk. # Eliminates decompression bombs where compressed chunks can expand 1000x @@ -118,7 +117,7 @@ class ChunkStream(object): cid = s[4:] pos = self.fp.tell() length = i32(s) - + if not is_cid(cid): raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) @@ -359,12 +358,14 @@ class PngStream(ChunkStream): s = ImageFile._safe_read(self.fp, length) if self.im_mode == "P": if _simple_palette.match(s): + # tRNS contains only one full-transparent entry, + # other entries are full opaque i = s.find(b"\0") if i >= 0: self.im_info["transparency"] = i - elif _null_palette.match(s): - self.im_info["transparency"] = 0 else: + # otherwise, we have a byte string with one alpha value + # for each palette entry self.im_info["transparency"] = s elif self.im_mode == "L": self.im_info["transparency"] = i16(s) From eb0f400e024ab6fe899438325a580403118275db Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Tue, 19 Apr 2016 10:21:19 +0200 Subject: [PATCH 3/4] extend documentation for PNG transparency --- docs/handbook/image-file-formats.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 5fe42fa41..f6368469b 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -370,8 +370,13 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following Gamma, given as a floating point number. **transparency** - Transparency color index. This key is omitted if the image is not a - transparent palette image. + For ``P`` images: Either the palette index for full transparent pixels, + or a byte string with alpha values for each palette entry. + + For ``L`` and ``RGB`` images, the color that represents full transparent + pixels in this image. + + This key is omitted if the image is not a transparent palette image. ``Open`` also sets ``Image.text`` to a list of the values of the ``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual @@ -392,6 +397,9 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: For ``P``, ``L``, and ``RGB`` images, this option controls what color image to mark as transparent. + For ``P`` images, this can be a either the palette index, + or a byte string with alpha values for each palette entry. + **dpi** A tuple of two numbers corresponding to the desired dpi in each direction. From b2800b57a77e73157ecfe5e36263eea97c911d18 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 3 May 2016 19:46:22 +0100 Subject: [PATCH 4/4] spelling typos --- Tests/test_file_png.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 7a061b09f..0ba51865c 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -216,7 +216,8 @@ class TestFilePng(PillowTestCase): in_file = "Tests/images/pil123p.png" im = Image.open(in_file) - # 'transparency' contains a byte string with the opacity for each palett entry + # 'transparency' contains a byte string with the opacity for + # each palette entry self.assertEqual(len(im.info["transparency"]), 256) test_file = self.tempfile("temp.png") @@ -230,7 +231,7 @@ class TestFilePng(PillowTestCase): im = im.convert("RGBA") self.assert_image(im, "RGBA", (162, 150)) - # image has 124 uniqe qlpha values + # image has 124 unique alpha values self.assertEqual(len(im.split()[3].getcolors()), 124) def test_save_p_single_transparency(self):