diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 15689476c91..7fff8a906d4 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -562,6 +562,22 @@ def test_apng_save_large_duration(tmp_path: Path) -> None: with pytest.raises(ValueError, match="cannot write duration"): im.save(test_file, save_all=True, append_images=[im2], duration=65536000) + # Do not merge identical frames if the combined duration cannot be written + im.save( + test_file, + save_all=True, + append_images=[im.copy()], + duration=[32768000, 32768000], + ) + + with Image.open(test_file) as reloaded: + assert isinstance(reloaded, PngImagePlugin.PngImageFile) + assert reloaded.n_frames == 2 + assert reloaded.info["duration"] == 32768000 + + reloaded.seek(1) + assert reloaded.info["duration"] == 32768000 + def test_apng_save_disposal(tmp_path: Path) -> None: test_file = tmp_path / "temp.png" diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 56f64f927e4..de3e63ee9b9 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1261,8 +1261,13 @@ def _write_multiple_frames( and prev_blend == encoderinfo.get("blend") and "duration" in encoderinfo ): - previous.encoderinfo["duration"] += encoderinfo["duration"] - continue + new_duration = ( + previous.encoderinfo["duration"] + encoderinfo["duration"] + ) + delay = Fraction(new_duration / 1000).limit_denominator(65535) + if delay.numerator <= 65535: + previous.encoderinfo["duration"] = new_duration + continue else: bbox = None im_frames.append(_Frame(im_frame, bbox, encoderinfo))