Skip to content

Commit 6b3991b

Browse files
authored
Merge pull request #7146 from radarhere/apng_duration
Fixed combining single duration across duplicate APNG frames
2 parents 9264c3d + 97df237 commit 6b3991b

File tree

2 files changed

+17
-3
lines changed

2 files changed

+17
-3
lines changed

‎Tests/test_file_apng.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,17 @@ def test_apng_save_duration_loop(tmp_path):
447447
assert im.info.get("duration") == 750
448448

449449

450+
def test_apng_save_duplicate_duration(tmp_path):
451+
test_file = str(tmp_path / "temp.png")
452+
frame = Image.new("RGB", (1, 1))
453+
454+
# Test a single duration is correctly combined across duplicate frames
455+
frame.save(test_file, save_all=True, append_images=[frame, frame], duration=500)
456+
with Image.open(test_file) as im:
457+
assert im.n_frames == 1
458+
assert im.info.get("duration") == 1500
459+
460+
450461
def test_apng_save_disposal(tmp_path):
451462
test_file = str(tmp_path / "temp.png")
452463
size = (128, 64)

‎src/PIL/PngImagePlugin.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,11 +1146,14 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
11461146
and prev_disposal == encoderinfo.get("disposal")
11471147
and prev_blend == encoderinfo.get("blend")
11481148
):
1149-
if isinstance(duration, (list, tuple)):
1150-
previous["encoderinfo"]["duration"] += encoderinfo["duration"]
1149+
previous["encoderinfo"]["duration"] += encoderinfo.get(
1150+
"duration", duration
1151+
)
11511152
continue
11521153
else:
11531154
bbox = None
1155+
if "duration" not in encoderinfo:
1156+
encoderinfo["duration"] = duration
11541157
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
11551158

11561159
# animation control
@@ -1175,7 +1178,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
11751178
im_frame = im_frame.crop(bbox)
11761179
size = im_frame.size
11771180
encoderinfo = frame_data["encoderinfo"]
1178-
frame_duration = int(round(encoderinfo.get("duration", duration)))
1181+
frame_duration = int(round(encoderinfo["duration"]))
11791182
frame_disposal = encoderinfo.get("disposal", disposal)
11801183
frame_blend = encoderinfo.get("blend", blend)
11811184
# frame control

0 commit comments

Comments
 (0)