Added append_images parameter to GIF saving

This commit is contained in:
Andrew Murray 2016-09-11 12:04:01 +10:00
parent be7a191b6e
commit b346ed36f1
3 changed files with 51 additions and 28 deletions

View File

@ -351,35 +351,38 @@ def _save(im, fp, filename, save_all=False):
previous = None previous = None
first_frame = None first_frame = None
for im_frame in ImageSequence.Iterator(im): append_images = im.encoderinfo.get("append_images", [])
im_frame = _convert_mode(im_frame) for imSequence in [im]+append_images:
for im_frame in ImageSequence.Iterator(imSequence):
encoderinfo = im.encoderinfo.copy()
im_frame = _convert_mode(im_frame)
# To specify duration, add the time in milliseconds to getdata(), # To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im_frame, duration=1000) # e.g. getdata(im_frame, duration=1000)
if not previous: if not previous:
# global header # global header
first_frame = getheader(im_frame, palette, im.encoderinfo)[0] first_frame = getheader(im_frame, palette, encoderinfo)[0]
first_frame += getdata(im_frame, (0, 0), **im.encoderinfo) first_frame += getdata(im_frame, (0, 0), **encoderinfo)
else:
if first_frame:
for s in first_frame:
fp.write(s)
first_frame = None
# delta frame
delta = ImageChops.subtract_modulo(im_frame, previous.copy())
bbox = delta.getbbox()
if bbox:
# compress difference
encoderinfo['include_color_table'] = True
for s in getdata(im_frame.crop(bbox),
bbox[:2], **im.encoderinfo):
fp.write(s)
else: else:
# FIXME: what should we do in this case? if first_frame:
pass for s in first_frame:
previous = im_frame fp.write(s)
first_frame = None
# delta frame
delta = ImageChops.subtract_modulo(im_frame, previous.copy())
bbox = delta.getbbox()
if bbox:
# compress difference
encoderinfo['include_color_table'] = True
for s in getdata(im_frame.crop(bbox),
bbox[:2], **encoderinfo):
fp.write(s)
else:
# FIXME: what should we do in this case?
pass
previous = im_frame
if first_frame: if first_frame:
save_all = False save_all = False
if not save_all: if not save_all:

View File

@ -306,6 +306,24 @@ class TestFileGif(PillowTestCase):
reread = Image.open(out) reread = Image.open(out)
self.assertEqual(reread.info["version"], b"GIF87a") self.assertEqual(reread.info["version"], b"GIF87a")
def test_append_images(self):
out = self.tempfile('temp.gif')
# Test appending single frame images
im = Image.new('RGB', (100, 100), '#f00')
ims = [Image.new('RGB', (100, 100), color) for color in ['#0f0', '#00f']]
im.save(out, save_all=True, append_images=ims)
reread = Image.open(out)
self.assertEqual(reread.n_frames, 3)
# Tests appending single and multiple frame images
im = Image.open("Tests/images/dispose_none.gif")
ims = [Image.open("Tests/images/dispose_prev.gif")]
im.save(out, save_all=True, append_images=ims)
reread = Image.open(out)
self.assertEqual(reread.n_frames, 10)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -103,7 +103,9 @@ Saving sequences
When calling :py:meth:`~PIL.Image.Image.save`, if a multiframe image is used, When calling :py:meth:`~PIL.Image.Image.save`, if a multiframe image is used,
by default only the first frame will be saved. To save all frames, the by default only the first frame will be saved. To save all frames, the
``save_all`` parameter must be present and set to ``True``. ``save_all`` parameter must be present and set to ``True``. To append
additional frames when saving, the ``append_images`` parameter can be set to a
list of images containing the extra frames.
If present, the ``loop`` parameter can be used to set the number of times If present, the ``loop`` parameter can be used to set the number of times
the GIF should loop, and the ``duration`` parameter can set the number of the GIF should loop, and the ``duration`` parameter can set the number of