Merge pull request #1882 from wiredfool/pr_1840

Fix Transparency for PNG images
This commit is contained in:
wiredfool 2016-05-03 19:51:04 +01:00
commit a0602fb68c
3 changed files with 67 additions and 7 deletions

View File

@ -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)

View File

@ -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,66 @@ class TestFilePng(PillowTestCase):
in_file = "Tests/images/pil123p.png"
im = Image.open(in_file)
# '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")
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 unique alpha 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)

View File

@ -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.