From 63c3b26f6aa91740dff5ea041fc713cfb53aee51 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 13 Jul 2021 23:02:23 +1000 Subject: [PATCH 1/2] Fixed using info dictionary when writing multiple frames --- Tests/test_file_apng.py | 24 ++++++++++++++++++++++++ src/PIL/PngImagePlugin.py | 19 +++++++++++-------- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 15e007ca131..d48e5ce07f3 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -441,6 +441,12 @@ def test_apng_save_duration_loop(tmp_path): assert im.n_frames == 1 assert im.info.get("duration") == 750 + # test info duration + frame.info["duration"] = 750 + frame.save(test_file, save_all=True) + with Image.open(test_file) as im: + assert im.info.get("duration") == 750 + def test_apng_save_disposal(tmp_path): test_file = str(tmp_path / "temp.png") @@ -531,6 +537,17 @@ def test_apng_save_disposal(tmp_path): assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) + # test info disposal + red.info["disposal"] = PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND + red.save( + test_file, + save_all=True, + append_images=[Image.new("RGBA", (10, 10), (0, 255, 0, 255))], + ) + with Image.open(test_file) as im: + im.seek(1) + assert im.getpixel((64, 32)) == (0, 0, 0, 0) + def test_apng_save_disposal_previous(tmp_path): test_file = str(tmp_path / "temp.png") @@ -611,3 +628,10 @@ def test_apng_save_blend(tmp_path): im.seek(2) assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) + + # test info blend + red.info["blend"] = PngImagePlugin.APNG_BLEND_OP_OVER + red.save(test_file, save_all=True, append_images=[green, transparent]) + with Image.open(test_file) as im: + im.seek(2) + assert im.getpixel((0, 0)) == (0, 255, 0, 255) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index a91393726d6..0c466da512f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1061,8 +1061,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode): default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) - disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) - blend = im.encoderinfo.get("blend", im.info.get("blend")) + disposal = im.encoderinfo.get( + "disposal", im.info.get("disposal", APNG_DISPOSE_OP_NONE) + ) + blend = im.encoderinfo.get("blend", im.info.get("blend", APNG_BLEND_OP_SOURCE)) if default_image: chain = itertools.chain(im.encoderinfo.get("append_images", [])) @@ -1149,9 +1151,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode): bbox = frame_data["bbox"] im_frame = im_frame.crop(bbox) size = im_frame.size - duration = int(round(frame_data["encoderinfo"].get("duration", 0))) - disposal = frame_data["encoderinfo"].get("disposal", APNG_DISPOSE_OP_NONE) - blend = frame_data["encoderinfo"].get("blend", APNG_BLEND_OP_SOURCE) + encoderinfo = frame_data["encoderinfo"] + frame_duration = int(round(encoderinfo.get("duration", duration))) + frame_disposal = encoderinfo.get("disposal", disposal) + frame_blend = encoderinfo.get("blend", blend) # frame control chunk( fp, @@ -1161,10 +1164,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode): o32(size[1]), # height o32(bbox[0]), # x_offset o32(bbox[1]), # y_offset - o16(duration), # delay_numerator + o16(frame_duration), # delay_numerator o16(1000), # delay_denominator - o8(disposal), # dispose_op - o8(blend), # blend_op + o8(frame_disposal), # dispose_op + o8(frame_blend), # blend_op ) seq_num += 1 # frame data From e766ddbc399b30164a9ce195d072cce0a0054507 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 13 Jul 2021 22:59:49 +1000 Subject: [PATCH 2/2] Removed unnecessary code --- src/PIL/PngImagePlugin.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0c466da512f..0f596f1fdb3 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1119,12 +1119,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode): and prev_disposal == encoderinfo.get("disposal") and prev_blend == encoderinfo.get("blend") ): - frame_duration = encoderinfo.get("duration", 0) - if frame_duration: - if "duration" in previous["encoderinfo"]: - previous["encoderinfo"]["duration"] += frame_duration - else: - previous["encoderinfo"]["duration"] = frame_duration + if isinstance(duration, (list, tuple)): + previous["encoderinfo"]["duration"] += encoderinfo["duration"] continue else: bbox = None