From 4d8d59534ffad69b31232726b6be632466805366 Mon Sep 17 00:00:00 2001 From: linnil1 Date: Tue, 31 Jan 2017 03:34:48 +0800 Subject: [PATCH 1/7] Add disposal option --- PIL/GifImagePlugin.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index f227211a2..b54d99012 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -503,15 +503,24 @@ def _write_local_header(fp, im, offset, flags): duration = int(im.encoderinfo["duration"] / 10) else: duration = 0 + + if "disposal" in im.encoderinfo: + disposal = int(im.encoderinfo['disposal']) + if not 0<= disposal <=3: + disposal = 2 + else: + disposal = 2 + if transparent_color_exists or duration != 0: - transparency_flag = 1 if transparent_color_exists else 0 + packed_flag = 1 if transparent_color_exists else 0 + packed_flag |= disposal << 2 if not transparent_color_exists: transparency = 0 fp.write(b"!" + o8(249) + # extension intro o8(4) + # length - o8(transparency_flag) + # packed fields + o8(packed_flag) + # packed fields o16(duration) + # duration o8(transparency) + # transparency index o8(0)) From 5a902bf8a2513be8a599a7cadb725b1365bdd432 Mon Sep 17 00:00:00 2001 From: linnil1 Date: Tue, 31 Jan 2017 03:55:49 +0800 Subject: [PATCH 2/7] Add dispoal description in documents --- docs/handbook/image-file-formats.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 130d15908..382942b46 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -132,6 +132,14 @@ are available:: the palette can be passed in as an :py:class:`PIL.ImagePalette.ImagePalette` object. +**disposal** + Indicates the way in which the graphic is to be treated after being displayed. + + * 0 - No disposal specified. + * 1 - Do not dispose. + * 2 - Restore to background color. + * 3 - Restore to previous content. + Reading local images ~~~~~~~~~~~~~~~~~~~~ From 5b5d47863efb7a553767885d7c0ce861541ffcb0 Mon Sep 17 00:00:00 2001 From: linnil1 Date: Tue, 31 Jan 2017 15:22:54 +0800 Subject: [PATCH 3/7] Add dispose test --- Tests/test_file_gif.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2ca234173..9e6db85f6 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -259,6 +259,28 @@ class TestFileGif(PillowTestCase): except EOFError: pass + def test_save_dispose(self): + out = self.tempfile('temp.gif') + im_list = [ + Image.new('L', (100, 100), '#000'), + Image.new('L', (100, 100), '#111'), + Image.new('L', (100, 100), '#222'), + ] + for method in range(0,4): + im_list[0].save( + out, + save_all=True, + append_images=im_list[1:], + disposal=method + ) + img = Image.open(out) + try: + while True: + img.seek(img.tell() + 1) + self.assertEqual(img.disposal_method, method) + except EOFError: + pass + def test_iss634(self): img = Image.open("Tests/images/iss634.gif") # seek to the second frame From 364a7b895ede6ff8607617caa9a183f189152eaa Mon Sep 17 00:00:00 2001 From: linnil1 Date: Tue, 31 Jan 2017 15:32:01 +0800 Subject: [PATCH 4/7] Thanks @wiredfool sugguest for disposal --- PIL/GifImagePlugin.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index b54d99012..078a2f24b 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -504,14 +504,9 @@ def _write_local_header(fp, im, offset, flags): else: duration = 0 - if "disposal" in im.encoderinfo: - disposal = int(im.encoderinfo['disposal']) - if not 0<= disposal <=3: - disposal = 2 - else: - disposal = 2 + disposal = int(im.encoderinfo.get('disposal', 0)) - if transparent_color_exists or duration != 0: + if transparent_color_exists or duration != 0 or disposal: packed_flag = 1 if transparent_color_exists else 0 packed_flag |= disposal << 2 if not transparent_color_exists: From 378f74672c96607ac68c083c050352f75b68d18b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 21 Dec 2017 22:26:58 +0000 Subject: [PATCH 5/7] Added per-frame disposal, tests --- PIL/GifImagePlugin.py | 3 +++ Tests/test_file_gif.py | 24 ++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 078a2f24b..71d8ce59a 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -392,6 +392,7 @@ def _write_single_frame(im, fp, palette): def _write_multiple_frames(im, fp, palette): duration = im.encoderinfo.get("duration", None) + disposal = im.encoderinfo.get('disposal', None) im_frames = [] frame_count = 0 @@ -404,6 +405,8 @@ def _write_multiple_frames(im, fp, palette): encoderinfo = im.encoderinfo.copy() if isinstance(duration, (list, tuple)): encoderinfo['duration'] = duration[frame_count] + if isinstance(disposal, (list, tuple)): + encoderinfo["disposal"] = disposal[frame_count] frame_count += 1 if im_frames: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 9e6db85f6..4a227e0cf 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -274,12 +274,24 @@ class TestFileGif(PillowTestCase): disposal=method ) img = Image.open(out) - try: - while True: - img.seek(img.tell() + 1) - self.assertEqual(img.disposal_method, method) - except EOFError: - pass + for _ in range(2): + img.seek(img.tell() + 1) + self.assertEqual(img.disposal_method, method) + + + # check per frame disposal + im_list[0].save( + out, + save_all=True, + append_images=im_list[1:], + disposal=range(len(im_list)) + ) + + img = Image.open(out) + + for i in range(2): + img.seek(img.tell() + 1) + self.assertEqual(img.disposal_method, i+1) def test_iss634(self): img = Image.open("Tests/images/iss634.gif") From f3ff2b4420e77026b29d76a27c0733c03c022174 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 21 Dec 2017 22:30:31 +0000 Subject: [PATCH 6/7] docs for per-frame disposal [ci skip] --- docs/handbook/image-file-formats.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 382942b46..3fb98e53e 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -140,6 +140,9 @@ are available:: * 2 - Restore to background color. * 3 - Restore to previous content. + Pass a single integer for a constant disposal, or a list or tuple + to set the disposal for each frame separately. + Reading local images ~~~~~~~~~~~~~~~~~~~~ From 48260643d71a6364ce03d2d98480ec5145e44885 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 21 Dec 2017 22:42:34 +0000 Subject: [PATCH 7/7] force range to a tuple, py3 --- Tests/test_file_gif.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 4a227e0cf..22a2c0072 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -284,7 +284,7 @@ class TestFileGif(PillowTestCase): out, save_all=True, append_images=im_list[1:], - disposal=range(len(im_list)) + disposal=tuple(range(len(im_list))) ) img = Image.open(out)