diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index b3f43b4cf..c6c9b8773 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -66,6 +66,10 @@ class DcxImageFile(PcxImageFile): def n_frames(self): return len(self._offset) + @property + def is_animated(self): + return len(self._offset) > 1 + def seek(self, frame): if frame >= len(self._offset): raise EOFError("attempt to seek outside DCX directory") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 772140076..e3eaff970 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -90,6 +90,7 @@ class FliImageFile(ImageFile.ImageFile): self.__fp = self.fp self.__rewind = self.fp.tell() self._n_frames = None + self._is_animated = None self.seek(0) def _palette(self, palette, shift): @@ -122,6 +123,20 @@ class FliImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): if frame == self.__frame: return diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 35fb95164..4e8ba9f58 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -88,6 +88,7 @@ class GifImageFile(ImageFile.ImageFile): self.__fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() self._n_frames = None + self._is_animated = None self._seek(0) # get ready to read first frame @property @@ -102,6 +103,20 @@ class GifImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): if frame == self.__frame: return diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 589928d0e..0a0a666ce 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -264,6 +264,10 @@ class ImImageFile(ImageFile.ImageFile): def n_frames(self): return self.info[FRAMES] + @property + def is_animated(self): + return self.info[FRAMES] > 1 + def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index aa41bf359..8e3e1b11d 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -75,6 +75,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): def n_frames(self): return len(self.images) + @property + def is_animated(self): + return len(self.images) > 1 + def seek(self, frame): try: diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index 9d21728b9..b7e6c5756 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -66,6 +66,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): def n_frames(self): return self.__framecount + @property + def is_animated(self): + return self.__framecount > 1 + def seek(self, frame): if frame < 0 or frame >= self.__framecount: raise EOFError("no more images in MPO file") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index d30695adb..030f5144c 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -136,6 +136,10 @@ class PsdImageFile(ImageFile.ImageFile): def n_frames(self): return len(self.layers) + @property + def is_animated(self): + return len(self.layers) > 1 + def seek(self, layer): # seek to given layer (1..max) if layer == self.frame: diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index 7de5156b1..f306538ae 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -158,6 +158,10 @@ class SpiderImageFile(ImageFile.ImageFile): def n_frames(self): return self._nimages + @property + def is_animated(self): + return self._nimages > 1 + # 1st image index is zero (although SPIDER imgnumber starts at 1) def tell(self): if self.imgnumber < 1: diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 83bbd3f93..99c890459 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -650,6 +650,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__fp = self.fp self._frame_pos = [] self._n_frames = None + self._is_animated = None if Image.DEBUG: print("*** TiffImageFile._open ***") @@ -671,6 +672,20 @@ class TiffImageFile(ImageFile.ImageFile): self.seek(current) return self._n_frames + @property + def is_animated(self): + if self._is_animated is None: + current = self.tell() + + try: + self.seek(1) + self._is_animated = True + except EOFError: + self._is_animated = False + + self.seek(current) + return self._is_animated + def seek(self, frame): "Select a given frame as current image" self._seek(max(frame, 0)) # Questionable backwards compatibility. diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index d59a5a785..7f804eb89 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -33,6 +33,7 @@ class TestFileDcx(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_FILE) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(TEST_FILE) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 1b95f793f..a0ea6af04 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -21,6 +21,7 @@ class TestFileFli(PillowTestCase): def test_n_frames(self): im = Image.open(test_file) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(test_file) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index a38c360c9..abf7d547b 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -137,9 +137,11 @@ class TestFileGif(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_GIF) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open("Tests/images/iss634.gif") self.assertEqual(im.n_frames, 42) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open(TEST_GIF) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 76bf250a0..602db4b1d 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -18,6 +18,7 @@ class TestFileIm(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_IM) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_eoferror(self): im = Image.open(TEST_IM) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index c5562097d..b1d3b7413 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -98,6 +98,7 @@ class TestFileMpo(PillowTestCase): def test_n_frames(self): im = Image.open("Tests/images/sugarshack.mpo") self.assertEqual(im.n_frames, 2) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open("Tests/images/sugarshack.mpo") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index ea3856fce..6492787ec 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -19,9 +19,11 @@ class TestImagePsd(PillowTestCase): def test_n_frames(self): im = Image.open("Tests/images/hopper_merged.psd") self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open(test_file) self.assertEqual(im.n_frames, 2) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open(test_file) diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index 7d24b2fe5..1ddecd365 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -45,6 +45,7 @@ class TestImageSpider(PillowTestCase): def test_n_frames(self): im = Image.open(TEST_FILE) self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) def test_loadImageSeries(self): # Arrange diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index a88b49d7f..1a1bbaef7 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -153,9 +153,11 @@ class TestFileTiff(PillowTestCase): def test_n_frames(self): im = Image.open('Tests/images/multipage-lastframe.tif') self.assertEqual(im.n_frames, 1) + self.assertFalse(im.is_animated) im = Image.open('Tests/images/multipage.tiff') self.assertEqual(im.n_frames, 3) + self.assertTrue(im.is_animated) def test_eoferror(self): im = Image.open('Tests/images/multipage-lastframe.tif')