From e6a521130e975f89529442597735a6a48cc3685f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 Feb 2024 19:47:09 +1100 Subject: [PATCH 1/2] If previous disposal was 2, do not fill identical pixels --- Tests/test_file_gif.py | 3 +++ src/PIL/GifImagePlugin.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index db9d3586c4d..0399c6b6798 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -647,6 +647,9 @@ def test_dispose2_palette(tmp_path: Path) -> None: # Center remains red every frame assert rgb_img.getpixel((50, 50)) == circle + # Check that frame transparency wasn't added unnecessarily + assert img._frame_transparency is None + def test_dispose2_diff(tmp_path: Path) -> None: out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index dc842d7a30b..73a5487d9a7 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -638,7 +638,11 @@ def _write_multiple_frames(im, fp, palette): background_im = Image.new("P", im_frame.size, background) background_im.putpalette(im_frames[0]["im"].palette) delta, bbox = _getbbox(background_im, im_frame) - if encoderinfo.get("optimize") and im_frame.mode != "1": + if ( + encoderinfo.get("optimize") + and im_frames[-1]["encoderinfo"].get("disposal") != 2 + and im_frame.mode != "1" + ): if "transparency" not in encoderinfo: try: encoderinfo["transparency"] = ( From 430f50606e2fc5620c6605cd43eb360be6bbd655 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 Feb 2024 21:33:32 +1100 Subject: [PATCH 2/2] Current delta is determined by previous disposal --- Tests/test_file_gif.py | 19 +++++++++++++++++++ src/PIL/GifImagePlugin.py | 10 +++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 0399c6b6798..6527d90de96 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -737,6 +737,25 @@ def test_dispose2_background_frame(tmp_path: Path) -> None: assert im.n_frames == 3 +def test_dispose2_previous_frame(tmp_path: Path) -> None: + out = str(tmp_path / "temp.gif") + + im = Image.new("P", (100, 100)) + im.info["transparency"] = 0 + d = ImageDraw.Draw(im) + d.rectangle([(0, 0), (100, 50)], 1) + im.putpalette((0, 0, 0, 255, 0, 0)) + + im2 = Image.new("P", (100, 100)) + im2.putpalette((0, 0, 0)) + + im.save(out, save_all=True, append_images=[im2], disposal=[0, 2]) + + with Image.open(out) as im: + im.seek(1) + assert im.getpixel((0, 0)) == (0, 0, 0, 255) + + def test_transparency_in_second_frame(tmp_path: Path) -> None: out = str(tmp_path / "temp.gif") with Image.open("Tests/images/different_transparency.gif") as im: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 73a5487d9a7..9368dd7e7c4 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -629,7 +629,7 @@ def _write_multiple_frames(im, fp, palette): "duration" ] continue - if encoderinfo.get("disposal") == 2: + if im_frames[-1]["encoderinfo"].get("disposal") == 2: if background_im is None: color = im.encoderinfo.get( "transparency", im.info.get("transparency", (0, 0, 0)) @@ -637,12 +637,8 @@ def _write_multiple_frames(im, fp, palette): background = _get_background(im_frame, color) background_im = Image.new("P", im_frame.size, background) background_im.putpalette(im_frames[0]["im"].palette) - delta, bbox = _getbbox(background_im, im_frame) - if ( - encoderinfo.get("optimize") - and im_frames[-1]["encoderinfo"].get("disposal") != 2 - and im_frame.mode != "1" - ): + bbox = _getbbox(background_im, im_frame)[1] + elif encoderinfo.get("optimize") and im_frame.mode != "1": if "transparency" not in encoderinfo: try: encoderinfo["transparency"] = (