Do not use first frame duration for other frames when saving

This commit is contained in:
Andrew Murray 2024-06-04 18:46:35 +10:00
parent 1794a946b7
commit e9c9f19c26
2 changed files with 19 additions and 9 deletions

View File

@ -706,10 +706,21 @@ def test_different_modes_in_later_frames(
assert reloaded.mode == mode assert reloaded.mode == mode
def test_apng_repeated_seeks_give_correct_info() -> None: def test_different_durations(tmp_path: Path) -> None:
test_file = str(tmp_path / "temp.png")
with Image.open("Tests/images/apng/different_durations.png") as im: with Image.open("Tests/images/apng/different_durations.png") as im:
for i in range(3): for _ in range(3):
im.seek(0) im.seek(0)
assert im.info["duration"] == 4000 assert im.info["duration"] == 4000
im.seek(1) im.seek(1)
assert im.info["duration"] == 1000 assert im.info["duration"] == 1000
im.save(test_file, save_all=True)
with Image.open(test_file) as reloaded:
assert reloaded.info["duration"] == 4000
reloaded.seek(1)
assert reloaded.info["duration"] == 1000

View File

@ -1105,7 +1105,7 @@ class _fdat:
def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images): def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images):
duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) duration = im.encoderinfo.get("duration")
loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0))
disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE))
blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE))
@ -1126,6 +1126,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
encoderinfo = im.encoderinfo.copy() encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)): if isinstance(duration, (list, tuple)):
encoderinfo["duration"] = duration[frame_count] encoderinfo["duration"] = duration[frame_count]
elif duration is None and "duration" in im_frame.info:
encoderinfo["duration"] = im_frame.info["duration"]
if isinstance(disposal, (list, tuple)): if isinstance(disposal, (list, tuple)):
encoderinfo["disposal"] = disposal[frame_count] encoderinfo["disposal"] = disposal[frame_count]
if isinstance(blend, (list, tuple)): if isinstance(blend, (list, tuple)):
@ -1160,15 +1162,12 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
not bbox not bbox
and prev_disposal == encoderinfo.get("disposal") and prev_disposal == encoderinfo.get("disposal")
and prev_blend == encoderinfo.get("blend") and prev_blend == encoderinfo.get("blend")
and "duration" in encoderinfo
): ):
previous["encoderinfo"]["duration"] += encoderinfo.get( previous["encoderinfo"]["duration"] += encoderinfo["duration"]
"duration", duration
)
continue continue
else: else:
bbox = None bbox = None
if "duration" not in encoderinfo:
encoderinfo["duration"] = duration
im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo})
if len(im_frames) == 1 and not default_image: if len(im_frames) == 1 and not default_image:
@ -1198,7 +1197,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images)
im_frame = im_frame.crop(bbox) im_frame = im_frame.crop(bbox)
size = im_frame.size size = im_frame.size
encoderinfo = frame_data["encoderinfo"] encoderinfo = frame_data["encoderinfo"]
frame_duration = int(round(encoderinfo["duration"])) frame_duration = int(round(encoderinfo.get("duration", 0)))
frame_disposal = encoderinfo.get("disposal", disposal) frame_disposal = encoderinfo.get("disposal", disposal)
frame_blend = encoderinfo.get("blend", blend) frame_blend = encoderinfo.get("blend", blend)
# frame control # frame control