From b77578ebe5ca9ca4c96de31c2c7681161f1c3bc3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Aug 2022 14:09:18 +1000 Subject: [PATCH] Revert "Support saving multiple MPO frames" This reverts commit 2944ff18d6ac79b881e3a3ec8d656fe08a41d1a9. --- Tests/test_file_mpo.py | 41 +++----------------- docs/handbook/image-file-formats.rst | 11 ------ src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 57 ++-------------------------- 4 files changed, 10 insertions(+), 101 deletions(-) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 849857d31d6..d093f26ccd8 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -5,19 +5,15 @@ from PIL import Image -from .helper import ( - assert_image_equal, - assert_image_similar, - is_pypy, - skip_unless_feature, -) +from .helper import assert_image_similar, is_pypy, skip_unless_feature test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"] pytestmark = skip_unless_feature("jpg") -def roundtrip(im, **options): +def frame_roundtrip(im, **options): + # Note that for now, there is no MPO saving functionality out = BytesIO() im.save(out, "MPO", **options) test_bytes = out.tell() @@ -241,38 +237,13 @@ def test_image_grab(): def test_save(): + # Note that only individual frames can be saved at present for test_file in test_files: with Image.open(test_file) as im: assert im.tell() == 0 - jpg0 = roundtrip(im) + jpg0 = frame_roundtrip(im) assert_image_similar(im, jpg0, 30) im.seek(1) assert im.tell() == 1 - jpg1 = roundtrip(im) + jpg1 = frame_roundtrip(im) assert_image_similar(im, jpg1, 30) - - -def test_save_all(): - for test_file in test_files: - with Image.open(test_file) as im: - im_reloaded = roundtrip(im, save_all=True) - - im.seek(0) - assert_image_similar(im, im_reloaded, 30) - - im.seek(1) - im_reloaded.seek(1) - assert_image_similar(im, im_reloaded, 30) - - im = Image.new("RGB", (1, 1)) - im2 = Image.new("RGB", (1, 1), "#f00") - im_reloaded = roundtrip(im, save_all=True, append_images=[im2]) - - assert_image_equal(im, im_reloaded) - - im_reloaded.seek(1) - assert_image_similar(im2, im_reloaded, 1) - - # Test that a single frame image will not be saved as an MPO - jpg = roundtrip(im, save_all=True) - assert "mp" not in jpg.info diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1728c8e0579..30452c4a6c2 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1209,17 +1209,6 @@ image when first opened. The :py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL methods may be used to read other pictures from the file. The pictures are zero-indexed and random access is supported. -When calling :py:meth:`~PIL.Image.Image.save` to write an MPO file, by default -only the first frame of a multiframe image will be saved. If the ``save_all`` -argument is present and true, then all frames will be saved, and the following -option will also be available. - -**append_images** - A list of images to append as additional pictures. Each of the - images in the list can be single or multiframe images. - - .. versionadded:: 9.3.0 - PCD ^^^ diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index a6ed223bc6f..4efe6281ad6 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -711,7 +711,7 @@ def validate_qtables(qtables): qtables = getattr(im, "quantization", None) qtables = validate_qtables(qtables) - extra = info.get("extra", b"") + extra = b"" icc_profile = info.get("icc_profile") if icc_profile: diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 5bfd8efc1a6..27c30958c9c 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -18,66 +18,16 @@ # See the README file for information on usage and redistribution. # -import itertools -import os -import struct - -from . import Image, ImageFile, ImageSequence, JpegImagePlugin, TiffImagePlugin +from . import Image, ImageFile, JpegImagePlugin from ._binary import i16be as i16 -from ._binary import o32le # def _accept(prefix): # return JpegImagePlugin._accept(prefix) def _save(im, fp, filename): - JpegImagePlugin._save(im, fp, filename) - - -def _save_all(im, fp, filename): - append_images = im.encoderinfo.get("append_images", []) - if not append_images: - try: - animated = im.is_animated - except AttributeError: - animated = False - if not animated: - _save(im, fp, filename) - return - - offsets = [] - for imSequence in itertools.chain([im], append_images): - for im_frame in ImageSequence.Iterator(imSequence): - if not offsets: - # APP2 marker - im.encoderinfo["extra"] = ( - b"\xFF\xE2" + struct.pack(">H", 6 + 70) + b"MPF\0" + b" " * 70 - ) - JpegImagePlugin._save(im_frame, fp, filename) - offsets.append(fp.tell()) - else: - im_frame.save(fp, "JPEG") - offsets.append(fp.tell() - offsets[-1]) - - ifd = TiffImagePlugin.ImageFileDirectory_v2() - ifd[0xB001] = len(offsets) - - mpentries = b"" - data_offset = 0 - for i, size in enumerate(offsets): - if i == 0: - mptype = 0x030000 # Baseline MP Primary Image - else: - mptype = 0x000000 # Undefined - mpentries += struct.pack("