diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index fffbc54caf5..d0c81b5e9d7 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -350,7 +350,7 @@ def test_apng_save(tmp_path): im.load() assert not im.is_animated assert im.n_frames == 1 - assert im.get_format_mimetype() == "image/apng" + assert im.get_format_mimetype() == "image/png" assert im.info.get("default_image") is None assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) @@ -450,26 +450,29 @@ def test_apng_save_duration_loop(tmp_path): test_file, save_all=True, append_images=[frame, frame], duration=[500, 100, 150] ) with Image.open(test_file) as im: - im.load() assert im.n_frames == 1 - assert im.info.get("duration") == 750 + assert "duration" not in im.info - # test info duration - frame.info["duration"] = 750 - frame.save(test_file, save_all=True) + different_frame = Image.new("RGBA", (128, 64)) + frame.save( + test_file, + save_all=True, + append_images=[frame, different_frame], + duration=[500, 100, 150], + ) with Image.open(test_file) as im: - assert im.info.get("duration") == 750 - + assert im.n_frames == 2 + assert im.info["duration"] == 600 -def test_apng_save_duplicate_duration(tmp_path): - test_file = str(tmp_path / "temp.png") - frame = Image.new("RGB", (1, 1)) + im.seek(1) + assert im.info["duration"] == 150 - # Test a single duration is correctly combined across duplicate frames - frame.save(test_file, save_all=True, append_images=[frame, frame], duration=500) + # test info duration + frame.info["duration"] = 300 + frame.save(test_file, save_all=True, append_images=[frame, different_frame]) with Image.open(test_file) as im: - assert im.n_frames == 1 - assert im.info.get("duration") == 1500 + assert im.n_frames == 2 + assert im.info["duration"] == 600 def test_apng_save_disposal(tmp_path): @@ -674,7 +677,8 @@ def test_seek_after_close(): @pytest.mark.parametrize("mode", ("RGBA", "RGB", "P")) @pytest.mark.parametrize("default_image", (True, False)) -def test_different_modes_in_later_frames(mode, default_image, tmp_path): +@pytest.mark.parametrize("duplicate", (True, False)) +def test_different_modes_in_later_frames(mode, default_image, duplicate, tmp_path): test_file = str(tmp_path / "temp.png") im = Image.new("L", (1, 1)) @@ -682,7 +686,7 @@ def test_different_modes_in_later_frames(mode, default_image, tmp_path): test_file, save_all=True, default_image=default_image, - append_images=[Image.new(mode, (1, 1))], + append_images=[im.convert(mode) if duplicate else Image.new(mode, (1, 1), 1)], ) with Image.open(test_file) as reloaded: assert reloaded.mode == mode diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 1bd0f442f76..dbcdee1c2d7 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1156,6 +1156,9 @@ def _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) encoderinfo["duration"] = duration im_frames.append({"im": im_frame, "bbox": bbox, "encoderinfo": encoderinfo}) + if len(im_frames) == 1 and not default_image: + return im_frames[0]["im"] + # animation control chunk( fp, @@ -1391,8 +1394,10 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): chunk(fp, b"eXIf", exif) if save_all: - _write_multiple_frames(im, fp, chunk, rawmode, default_image, append_images) - else: + im = _write_multiple_frames( + im, fp, chunk, rawmode, default_image, append_images + ) + if im: ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)]) if info: