From 6b2da2f1236f774ac6d6526cf479020b68776f95 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Apr 2022 21:04:22 +1100 Subject: [PATCH 1/3] Consider transparency from each frame when saving --- Tests/test_file_gif.py | 11 ++++++++++- src/PIL/GifImagePlugin.py | 6 +++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index dffd1006f2f..9caea84a85c 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -619,7 +619,7 @@ def test_dispose2_background(tmp_path): assert im.getpixel((0, 0)) == (255, 0, 0) -def test_transparency_in_second_frame(): +def test_transparency_in_second_frame(tmp_path): with Image.open("Tests/images/different_transparency.gif") as im: assert im.info["transparency"] == 0 @@ -629,6 +629,15 @@ def test_transparency_in_second_frame(): assert_image_equal_tofile(im, "Tests/images/different_transparency_merged.png") + out = str(tmp_path / "temp.gif") + im.save(out, save_all=True) + + with Image.open(out) as reread: + reread.seek(reread.tell() + 1) + assert_image_equal_tofile( + reread, "Tests/images/different_transparency_merged.png" + ) + def test_no_transparency_in_second_frame(): with Image.open("Tests/images/iss634.gif") as img: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b798bb96991..fad6cdf1bbe 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -573,10 +573,14 @@ def _write_multiple_frames(im, fp, palette): im_frame = _normalize_mode(im_frame.copy()) if frame_count == 0: for k, v in im_frame.info.items(): + if k == "transparency": + continue im.encoderinfo.setdefault(k, v) - im_frame = _normalize_palette(im_frame, palette, im.encoderinfo) encoderinfo = im.encoderinfo.copy() + if "transparency" in im_frame.info: + encoderinfo.setdefault("transparency", im_frame.info["transparency"]) + im_frame = _normalize_palette(im_frame, palette, encoderinfo) if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] if isinstance(disposal, (list, tuple)): From 46a80d144a16836af304a7aaa8e620962d91ac23 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 16:35:01 +1000 Subject: [PATCH 2/3] Update transparency when remapping the palette --- Tests/test_file_gif.py | 28 ++++++++++++++++++++++------ src/PIL/GifImagePlugin.py | 2 +- src/PIL/Image.py | 3 +++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 9caea84a85c..49dc3d841bc 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -620,6 +620,7 @@ def test_dispose2_background(tmp_path): def test_transparency_in_second_frame(tmp_path): + out = str(tmp_path / "temp.gif") with Image.open("Tests/images/different_transparency.gif") as im: assert im.info["transparency"] == 0 @@ -629,14 +630,13 @@ def test_transparency_in_second_frame(tmp_path): assert_image_equal_tofile(im, "Tests/images/different_transparency_merged.png") - out = str(tmp_path / "temp.gif") im.save(out, save_all=True) - with Image.open(out) as reread: - reread.seek(reread.tell() + 1) - assert_image_equal_tofile( - reread, "Tests/images/different_transparency_merged.png" - ) + with Image.open(out) as reread: + reread.seek(reread.tell() + 1) + assert_image_equal_tofile( + reread, "Tests/images/different_transparency_merged.png" + ) def test_no_transparency_in_second_frame(): @@ -649,6 +649,22 @@ def test_no_transparency_in_second_frame(): assert img.histogram()[255] == 0 +def test_remapped_transparency(tmp_path): + out = str(tmp_path / "temp.gif") + + im = Image.new("P", (1, 2)) + im2 = im.copy() + + # Add transparency at a higher index + # so that it will be optimized to a lower index + im.putpixel((0, 1), 5) + im.info["transparency"] = 5 + im.save(out, save_all=True, append_images=[im2]) + + with Image.open(out) as reloaded: + assert reloaded.info["transparency"] == reloaded.getpixel((0, 1)) + + def test_duration(tmp_path): duration = 1000 diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index fad6cdf1bbe..cead48c19c0 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -578,9 +578,9 @@ def _write_multiple_frames(im, fp, palette): im.encoderinfo.setdefault(k, v) encoderinfo = im.encoderinfo.copy() + im_frame = _normalize_palette(im_frame, palette, encoderinfo) if "transparency" in im_frame.info: encoderinfo.setdefault("transparency", im_frame.info["transparency"]) - im_frame = _normalize_palette(im_frame, palette, encoderinfo) if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] if isinstance(disposal, (list, tuple)): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 813ac52aab4..086b2b196f9 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1934,6 +1934,9 @@ def remap_palette(self, dest_map, source_palette=None): m_im.putpalette(new_palette_bytes) m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) + if "transparency" in self.info: + m_im.info["transparency"] = new_positions[self.info["transparency"]] + return m_im def _get_safe_box(self, size, resample, box): From 99f4623a8d4ca0b2117497ac5c13d4832527f2bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 17:38:44 +1000 Subject: [PATCH 3/3] Remove transparency if it cannot be remapped --- Tests/test_image.py | 14 ++++++++++++++ src/PIL/Image.py | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 07cf6eb92b3..0a951af8aaf 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -607,6 +607,20 @@ def test_remap_palette(self): with pytest.raises(ValueError): im.remap_palette(None) + def test_remap_palette_transparency(self): + im = Image.new("P", (1, 2)) + im.putpixel((0, 1), 1) + im.info["transparency"] = 0 + + im_remapped = im.remap_palette([1, 0]) + assert im_remapped.info["transparency"] == 1 + + # Test unused transparency + im.info["transparency"] = 2 + + im_remapped = im.remap_palette([1, 0]) + assert "transparency" not in im_remapped.info + def test__new(self): im = hopper("RGB") im_p = hopper("P") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 086b2b196f9..e226f140089 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1935,7 +1935,11 @@ def remap_palette(self, dest_map, source_palette=None): m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) if "transparency" in self.info: - m_im.info["transparency"] = new_positions[self.info["transparency"]] + try: + m_im.info["transparency"] = dest_map.index(self.info["transparency"]) + except ValueError: + if "transparency" in m_im.info: + del m_im.info["transparency"] return m_im