From b216b367ac91d5d8514d97d42b30805df7529c86 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Mar 2021 00:24:57 +1100 Subject: [PATCH] Only set info transparency on first frame --- Tests/images/different_transparency.gif | Bin 0 -> 4118 bytes .../images/different_transparency_merged.gif | Bin 0 -> 3046 bytes Tests/test_file_gif.py | 17 +++++++++-- Tests/test_file_webp_animated.py | 4 +-- src/PIL/GifImagePlugin.py | 27 +++++++++++++----- 5 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 Tests/images/different_transparency.gif create mode 100644 Tests/images/different_transparency_merged.gif diff --git a/Tests/images/different_transparency.gif b/Tests/images/different_transparency.gif new file mode 100644 index 0000000000000000000000000000000000000000..2d36bef9e360923246f7d29c705f39ef1d732021 GIT binary patch literal 4118 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1b2314d51..52d7f035d 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -468,12 +468,25 @@ def test_dispose2_background(tmp_path): assert im.getpixel((0, 0)) == 0 -def test_iss634(): +def test_transparency_in_second_frame(): + with Image.open("Tests/images/different_transparency.gif") as im: + assert im.info["transparency"] == 0 + + # Seek to the second frame + im.seek(im.tell() + 1) + assert im.info["transparency"] == 0 + + assert_image_equal_tofile(im, "Tests/images/different_transparency_merged.gif") + + +def test_no_transparency_in_second_frame(): with Image.open("Tests/images/iss634.gif") as img: # Seek to the second frame img.seek(img.tell() + 1) + assert "transparency" not in img.info + # All transparent pixels should be replaced with the color from the first frame - assert img.histogram()[img.info["transparency"]] == 0 + assert img.histogram()[255] == 0 def test_duration(tmp_path): diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 26e903488..25ebffe02 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -45,12 +45,12 @@ def test_write_animation_L(tmp_path): # Compare first and last frames to the original animated GIF orig.load() im.load() - assert_image_similar(im, orig.convert("RGBA"), 25.0) + assert_image_similar(im, orig.convert("RGBA"), 32.9) orig.seek(orig.n_frames - 1) im.seek(im.n_frames - 1) orig.load() im.load() - assert_image_similar(im, orig.convert("RGBA"), 25.0) + assert_image_similar(im, orig.convert("RGBA"), 32.9) @pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index bf2db4260..2f6d98204 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -173,6 +173,8 @@ class GifImageFile(ImageFile.ImageFile): self.palette = copy(self.global_palette) info = {} + frame_transparency = None + interlace = None while True: s = self.fp.read(1) @@ -191,7 +193,7 @@ class GifImageFile(ImageFile.ImageFile): # flags = block[0] if flags & 1: - info["transparency"] = block[3] + frame_transparency = block[3] info["duration"] = i16(block, 1) * 10 # disposal method - find the value of bits 4 - 6 @@ -249,10 +251,6 @@ class GifImageFile(ImageFile.ImageFile): # image data bits = self.fp.read(1)[0] self.__offset = self.fp.tell() - self.tile = [("gif", - (x0, y0, x1, y1), - self.__offset, - (bits, interlace, info.get("transparency", -1)))] break else: @@ -278,11 +276,26 @@ class GifImageFile(ImageFile.ImageFile): except (AttributeError, KeyError): pass - if not self.tile: + if interlace is not None: + transparency = -1 + if frame_transparency is not None: + if frame == 0: + self.info["transparency"] = frame_transparency + else: + transparency = frame_transparency + self.tile = [ + ( + "gif", + (x0, y0, x1, y1), + self.__offset, + (bits, interlace, transparency), + ) + ] + else: # self.__fp = None raise EOFError - for k in ["transparency", "duration", "comment", "extension", "loop"]: + for k in ["duration", "comment", "extension", "loop"]: if k in info: self.info[k] = info[k] elif k in self.info: