diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index 90759cdb551..fb47b53533e 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -353,6 +353,27 @@ def test_apng_save_different_palettes(tmp_path: Path) -> None: assert reloaded.mode == "RGBA" +@pytest.mark.parametrize("mode", ("I;16", "I;16B")) +def test_apng_save_i16(mode: str, tmp_path: Path) -> None: + test_file = tmp_path / "temp.png" + im = Image.new(mode, (1, 1), 1000) + second = Image.new(mode, (1, 1), 2000) + im.save( + test_file, + save_all=True, + append_images=[second], + duration=100, + ) + + with Image.open(test_file) as reloaded: + assert isinstance(reloaded, PngImagePlugin.PngImageFile) + assert reloaded.n_frames == 2 + assert reloaded.getpixel((0, 0)) == 1000 + + reloaded.seek(1) + assert reloaded.getpixel((0, 0)) == 2000 + + def test_apng_chunk_errors() -> None: with Image.open("Tests/images/apng/chunk_no_actl.png") as im: assert isinstance(im, PngImagePlugin.PngImageFile) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index fdbd0bb659c..597931afcc9 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1233,30 +1233,15 @@ def _write_multiple_frames( if im_frames: previous = im_frames[-1] - prev_disposal = previous.encoderinfo.get("disposal") - prev_blend = previous.encoderinfo.get("blend") - if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: - prev_disposal = Disposal.OP_BACKGROUND - - if prev_disposal == Disposal.OP_BACKGROUND: - base_im = previous.im.convert("RGBA") - box = previous.bbox or (0, 0, *im.size) - dispose = Image.new("RGBA", (box[2] - box[0], box[3] - box[1])) - base_im.paste(dispose, box) - elif prev_disposal == Disposal.OP_PREVIOUS: - base_im = im_frames[-2].im + if mode.startswith("I;16"): + delta = ImageChops.subtract_modulo( + Image.frombytes("LA", im.size, im_frame.tobytes()), + Image.frombytes("LA", im.size, previous.im.tobytes()), + ) else: - base_im = previous.im - delta = ImageChops.subtract_modulo( - im_frame.convert("RGBA"), base_im.convert("RGBA") - ) + delta = ImageChops.subtract_modulo(im_frame, previous.im) bbox = delta.getbbox(alpha_only=False) - if ( - not bbox - and prev_disposal == encoderinfo.get("disposal") - and prev_blend == encoderinfo.get("blend") - and "duration" in encoderinfo - ): + if not bbox and "duration" in encoderinfo: new_duration = ( previous.encoderinfo["duration"] + encoderinfo["duration"] )