mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 18:06:18 +03:00
Merge pull request #1849 from radarhere/frames
Common seek frame position check
This commit is contained in:
commit
3051178749
|
@ -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])
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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"]:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user