Merge pull request #1849 from radarhere/frames

Common seek frame position check
This commit is contained in:
wiredfool 2017-10-01 20:20:30 +01:00 committed by GitHub
commit 3051178749
17 changed files with 97 additions and 102 deletions

View File

@ -59,6 +59,7 @@ class DcxImageFile(PcxImageFile):
self._offset.append(offset) self._offset.append(offset)
self.__fp = self.fp self.__fp = self.fp
self.frame = None
self.seek(0) self.seek(0)
@property @property
@ -70,8 +71,8 @@ class DcxImageFile(PcxImageFile):
return len(self._offset) > 1 return len(self._offset) > 1
def seek(self, frame): def seek(self, frame):
if frame >= len(self._offset): if not self._seek_check(frame):
raise EOFError("attempt to seek outside DCX directory") return
self.frame = frame self.frame = frame
self.fp = self.__fp self.fp = self.__fp
self.fp.seek(self._offset[frame]) self.fp.seek(self._offset[frame])

View File

@ -118,12 +118,10 @@ class FliImageFile(ImageFile.ImageFile):
return self.__framecount > 1 return self.__framecount > 1
def seek(self, frame): def seek(self, frame):
if frame == self.__frame: if not self._seek_check(frame):
return return
if frame < self.__frame: if frame < self.__frame:
self._seek(0) self._seek(0)
if frame >= self.__framecount:
raise EOFError("no more images in FLI file")
for f in range(self.__frame + 1, frame + 1): for f in range(self.__frame + 1, frame + 1):
self._seek(f) self._seek(f)

View File

@ -48,7 +48,7 @@ class GifImageFile(ImageFile.ImageFile):
format = "GIF" format = "GIF"
format_description = "Compuserve GIF" format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False _close_exclusive_fp_after_loading = False
global_palette = None global_palette = None
def data(self): def data(self):
@ -117,7 +117,7 @@ class GifImageFile(ImageFile.ImageFile):
return self._is_animated return self._is_animated
def seek(self, frame): def seek(self, frame):
if frame == self.__frame: if not self._seek_check(frame):
return return
if frame < self.__frame: if frame < self.__frame:
self._seek(0) self._seek(0)

View File

@ -270,11 +270,7 @@ class ImImageFile(ImageFile.ImageFile):
return self.info[FRAMES] > 1 return self.info[FRAMES] > 1
def seek(self, frame): def seek(self, frame):
if not self._seek_check(frame):
if frame < 0 or frame >= self.info[FRAMES]:
raise EOFError("seek outside sequence")
if self.frame == frame:
return return
self.frame = frame self.frame = frame
@ -292,7 +288,6 @@ class ImImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]
def tell(self): def tell(self):
return self.frame return self.frame
# #

View File

@ -78,6 +78,8 @@ class ImageFile(Image.Image):
def __init__(self, fp=None, filename=None): def __init__(self, fp=None, filename=None):
Image.Image.__init__(self) Image.Image.__init__(self)
self._min_frame = 0
self.tile = None self.tile = None
self.readonly = 1 # until we know better self.readonly = 1 # until we know better
@ -275,6 +277,16 @@ class ImageFile(Image.Image):
# def load_read(self, bytes): # def load_read(self, bytes):
# pass # pass
def _seek_check(self, frame):
if (frame < self._min_frame or
# Only check upper limit on frames if additional seek operations
# are not required to do so
(not (hasattr(self, "_n_frames") and self._n_frames is None) and
frame >= self.n_frames+self._min_frame)):
raise EOFError("attempt to seek outside sequence")
return self.tell() != frame
class StubImageFile(ImageFile): class StubImageFile(ImageFile):
""" """

View File

@ -65,7 +65,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
raise SyntaxError("not an MIC file; no image entries") raise SyntaxError("not an MIC file; no image entries")
self.__fp = self.fp self.__fp = self.fp
self.frame = 0 self.frame = None
if len(self.images) > 1: if len(self.images) > 1:
self.category = Image.CONTAINER self.category = Image.CONTAINER
@ -81,7 +81,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
return len(self.images) > 1 return len(self.images) > 1
def seek(self, frame): def seek(self, frame):
if not self._seek_check(frame):
return
try: try:
filename = self.images[frame] filename = self.images[frame]
except IndexError: except IndexError:

View File

@ -72,14 +72,13 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
return self.__framecount > 1 return self.__framecount > 1
def seek(self, frame): def seek(self, frame):
if frame < 0 or frame >= self.__framecount: if not self._seek_check(frame):
raise EOFError("no more images in MPO file") return
else: self.fp = self.__fp
self.fp = self.__fp self.offset = self.__mpoffsets[frame]
self.offset = self.__mpoffsets[frame] self.tile = [
self.tile = [ ("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))
("jpeg", (0, 0) + self.size, self.offset, (self.mode, "")) ]
]
self.__frame = frame self.__frame = frame
def tell(self): def tell(self):

View File

@ -124,7 +124,8 @@ class PsdImageFile(ImageFile.ImageFile):
# keep the file open # keep the file open
self._fp = self.fp self._fp = self.fp
self.frame = 0 self.frame = 1
self._min_frame = 1
@property @property
def n_frames(self): def n_frames(self):
@ -135,12 +136,11 @@ class PsdImageFile(ImageFile.ImageFile):
return len(self.layers) > 1 return len(self.layers) > 1
def seek(self, layer): def seek(self, layer):
# seek to given layer (1..max) if not self._seek_check(layer):
if layer == self.frame:
return return
# seek to given layer (1..max)
try: try:
if layer <= 0:
raise IndexError
name, mode, bbox, tile = self.layers[layer-1] name, mode, bbox, tile = self.layers[layer-1]
self.mode = mode self.mode = mode
self.tile = tile self.tile = tile

View File

@ -174,8 +174,8 @@ class SpiderImageFile(ImageFile.ImageFile):
def seek(self, frame): def seek(self, frame):
if self.istack == 0: if self.istack == 0:
raise EOFError("attempt to seek in a non-stack file") raise EOFError("attempt to seek in a non-stack file")
if frame >= self._nimages: if not self._seek_check(frame):
raise EOFError("attempt to seek past end of file") return
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self.__fp self.fp = self.__fp
self.fp.seek(self.stkoffset) self.fp.seek(self.stkoffset)

View File

@ -555,8 +555,8 @@ class ImageFileDirectory_v2(collections.MutableMapping):
# Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
# No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
# Don't mess with the legacy api, since it's frozen. # Don't mess with the legacy api, since it's frozen.
if ((info.length == 1) or if ((info.length == 1) or
(info.length is None and len(values) == 1 and not legacy_api)): (info.length is None and len(values) == 1 and not legacy_api)):
# Don't mess with the legacy api, since it's frozen. # Don't mess with the legacy api, since it's frozen.
if legacy_api and self.tagtype[tag] in [5, 10]: # rationals if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
values = values, values = values,
@ -980,7 +980,9 @@ class TiffImageFile(ImageFile.ImageFile):
def seek(self, frame): def seek(self, frame):
"Select a given frame as current image" "Select a given frame as current image"
self._seek(max(frame, 0)) # Questionable backwards compatibility. if not self._seek_check(frame):
return
self._seek(frame)
# Create a new core image object on second and # Create a new core image object on second and
# subsequent frames in the image. Image may be # subsequent frames in the image. Image may be
# different size/mode. # different size/mode.

View File

@ -42,15 +42,14 @@ class TestFileDcx(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1 # Test seeking past the last frame
try: self.assertRaises(EOFError, im.seek, n_frames)
im.seek(n_frames) self.assertLess(im.tell(), n_frames)
break
except EOFError: # Test that seeking to the last frame does not raise an error
self.assertLess(im.tell(), n_frames) im.seek(n_frames-1)
def test_seek_too_far(self): def test_seek_too_far(self):
# Arrange # Arrange

View File

@ -54,24 +54,14 @@ class TestFileFli(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open(animated_test_file) im = Image.open(animated_test_file)
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertLess(im.tell(), n_frames)
def test_seek_outside(self): # Test seeking past the last frame
# Test negative seek self.assertRaises(EOFError, im.seek, n_frames)
im = Image.open(static_test_file) self.assertLess(im.tell(), n_frames)
im.seek(-1)
self.assertEqual(im.tell(), 0)
# Test seek past end of file # Test that seeking to the last frame does not raise an error
self.assertRaises(EOFError, im.seek, 2) im.seek(n_frames-1)
def test_seek_tell(self): def test_seek_tell(self):
im = Image.open(animated_test_file) im = Image.open(animated_test_file)
@ -91,6 +81,10 @@ class TestFileFli(PillowTestCase):
layer_number = im.tell() layer_number = im.tell()
self.assertEqual(layer_number, 2) self.assertEqual(layer_number, 2)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -223,15 +223,14 @@ class TestFileGif(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1 # Test seeking past the last frame
try: self.assertRaises(EOFError, im.seek, n_frames)
im.seek(n_frames) self.assertLess(im.tell(), n_frames)
break
except EOFError: # Test that seeking to the last frame does not raise an error
self.assertLess(im.tell(), n_frames) im.seek(n_frames-1)
def test_dispose_none(self): def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif") img = Image.open("Tests/images/dispose_none.gif")

View File

@ -32,15 +32,14 @@ class TestFileIm(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open(TEST_IM) im = Image.open(TEST_IM)
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1 # Test seeking past the last frame
try: self.assertRaises(EOFError, im.seek, n_frames)
im.seek(n_frames) self.assertLess(im.tell(), n_frames)
break
except EOFError: # Test that seeking to the last frame does not raise an error
self.assertLess(im.tell(), n_frames) im.seek(n_frames-1)
def test_roundtrip(self): def test_roundtrip(self):
for mode in ["RGB", "P"]: for mode in ["RGB", "P"]:

View File

@ -102,15 +102,14 @@ class TestFileMpo(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open("Tests/images/sugarshack.mpo") im = Image.open("Tests/images/sugarshack.mpo")
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1 # Test seeking past the last frame
try: self.assertRaises(EOFError, im.seek, n_frames)
im.seek(n_frames) self.assertLess(im.tell(), n_frames)
break
except EOFError: # Test that seeking to the last frame does not raise an error
self.assertLess(im.tell(), n_frames) im.seek(n_frames-1)
def test_image_grab(self): def test_image_grab(self):
for test_file in test_files: for test_file in test_files:

View File

@ -34,26 +34,23 @@ class TestImagePsd(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open(test_file) im = Image.open(test_file)
# PSD seek index starts at 1 rather than 0
n_frames = im.n_frames+1
n_frames = im.n_frames # Test seeking past the last frame
while True: self.assertRaises(EOFError, im.seek, n_frames)
n_frames -= 1 self.assertLess(im.tell(), n_frames)
try:
# PSD seek index starts at 1 rather than 0 # Test that seeking to the last frame does not raise an error
im.seek(n_frames+1) im.seek(n_frames-1)
break
except EOFError:
self.assertLess(im.tell(), n_frames)
def test_seek_tell(self): def test_seek_tell(self):
im = Image.open(test_file) im = Image.open(test_file)
layer_number = im.tell() layer_number = im.tell()
self.assertEqual(layer_number, 0) self.assertEqual(layer_number, 1)
im.seek(0) self.assertRaises(EOFError, im.seek, 0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(1) im.seek(1)
layer_number = im.tell() layer_number = im.tell()

View File

@ -250,15 +250,14 @@ class TestFileTiff(PillowTestCase):
def test_eoferror(self): def test_eoferror(self):
im = Image.open('Tests/images/multipage-lastframe.tif') im = Image.open('Tests/images/multipage-lastframe.tif')
n_frames = im.n_frames n_frames = im.n_frames
while True:
n_frames -= 1 # Test seeking past the last frame
try: self.assertRaises(EOFError, im.seek, n_frames)
im.seek(n_frames) self.assertLess(im.tell(), n_frames)
break
except EOFError: # Test that seeking to the last frame does not raise an error
self.assertLess(im.tell(), n_frames) im.seek(n_frames-1)
def test_multipage(self): def test_multipage(self):
# issue #862 # issue #862
@ -349,13 +348,14 @@ class TestFileTiff(PillowTestCase):
def test_seek(self): def test_seek(self):
filename = "Tests/images/pil136.tiff" filename = "Tests/images/pil136.tiff"
im = Image.open(filename) im = Image.open(filename)
im.seek(-1) im.seek(0)
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
def test_seek_eof(self): def test_seek_eof(self):
filename = "Tests/images/pil136.tiff" filename = "Tests/images/pil136.tiff"
im = Image.open(filename) im = Image.open(filename)
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, 1) self.assertRaises(EOFError, im.seek, 1)
def test__limit_rational_int(self): def test__limit_rational_int(self):