Skip to content

Commit

Permalink
Merge pull request #1849 from radarhere/frames
Browse files Browse the repository at this point in the history
Common seek frame position check
  • Loading branch information
wiredfool committed Oct 1, 2017
2 parents 0531dd1 + c8b65f4 commit 3051178
Show file tree
Hide file tree
Showing 17 changed files with 98 additions and 103 deletions.
5 changes: 3 additions & 2 deletions PIL/DcxImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def _open(self):
self._offset.append(offset)

self.__fp = self.fp
self.frame = None
self.seek(0)

@property
Expand All @@ -70,8 +71,8 @@ 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")
if not self._seek_check(frame):
return
self.frame = frame
self.fp = self.__fp
self.fp.seek(self._offset[frame])
Expand Down
4 changes: 1 addition & 3 deletions PIL/FliImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,10 @@ def is_animated(self):
return self.__framecount > 1

def seek(self, frame):
if frame == self.__frame:
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
if frame >= self.__framecount:
raise EOFError("no more images in FLI file")

for f in range(self.__frame + 1, frame + 1):
self._seek(f)
Expand Down
4 changes: 2 additions & 2 deletions PIL/GifImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class GifImageFile(ImageFile.ImageFile):
format = "GIF"
format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False

global_palette = None

def data(self):
Expand Down Expand Up @@ -117,7 +117,7 @@ def is_animated(self):
return self._is_animated

def seek(self, frame):
if frame == self.__frame:
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
Expand Down
7 changes: 1 addition & 6 deletions PIL/ImImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,7 @@ def is_animated(self):
return self.info[FRAMES] > 1

def seek(self, frame):

if frame < 0 or frame >= self.info[FRAMES]:
raise EOFError("seek outside sequence")

if self.frame == frame:
if not self._seek_check(frame):
return

self.frame = frame
Expand All @@ -292,7 +288,6 @@ def seek(self, frame):
self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]

def tell(self):

return self.frame

#
Expand Down
12 changes: 12 additions & 0 deletions PIL/ImageFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ class ImageFile(Image.Image):
def __init__(self, fp=None, filename=None):
Image.Image.__init__(self)

self._min_frame = 0

self.tile = None
self.readonly = 1 # until we know better

Expand Down Expand Up @@ -275,6 +277,16 @@ def load_end(self):
# def load_read(self, bytes):
# 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):
"""
Expand Down
5 changes: 3 additions & 2 deletions PIL/MicImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _open(self):
raise SyntaxError("not an MIC file; no image entries")

self.__fp = self.fp
self.frame = 0
self.frame = None

if len(self.images) > 1:
self.category = Image.CONTAINER
Expand All @@ -81,7 +81,8 @@ def is_animated(self):
return len(self.images) > 1

def seek(self, frame):

if not self._seek_check(frame):
return
try:
filename = self.images[frame]
except IndexError:
Expand Down
15 changes: 7 additions & 8 deletions PIL/MpoImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,13 @@ 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")
else:
self.fp = self.__fp
self.offset = self.__mpoffsets[frame]
self.tile = [
("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))
]
if not self._seek_check(frame):
return
self.fp = self.__fp
self.offset = self.__mpoffsets[frame]
self.tile = [
("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))
]
self.__frame = frame

def tell(self):
Expand Down
10 changes: 5 additions & 5 deletions PIL/PsdImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ def _open(self):

# keep the file open
self._fp = self.fp
self.frame = 0
self.frame = 1
self._min_frame = 1

@property
def n_frames(self):
Expand All @@ -135,12 +136,11 @@ def is_animated(self):
return len(self.layers) > 1

def seek(self, layer):
# seek to given layer (1..max)
if layer == self.frame:
if not self._seek_check(layer):
return

# seek to given layer (1..max)
try:
if layer <= 0:
raise IndexError
name, mode, bbox, tile = self.layers[layer-1]
self.mode = mode
self.tile = tile
Expand Down
4 changes: 2 additions & 2 deletions PIL/SpiderImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ def tell(self):
def seek(self, frame):
if self.istack == 0:
raise EOFError("attempt to seek in a non-stack file")
if frame >= self._nimages:
raise EOFError("attempt to seek past end of file")
if not self._seek_check(frame):
return
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self.__fp
self.fp.seek(self.stkoffset)
Expand Down
8 changes: 5 additions & 3 deletions PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,8 @@ def _setitem(self, tag, value, legacy_api):
# Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
# No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
# Don't mess with the legacy api, since it's frozen.
if ((info.length == 1) or
(info.length is None and len(values) == 1 and not legacy_api)):
if ((info.length == 1) or
(info.length is None and len(values) == 1 and not legacy_api)):
# Don't mess with the legacy api, since it's frozen.
if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
values = values,
Expand Down Expand Up @@ -980,7 +980,9 @@ def is_animated(self):

def seek(self, frame):
"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
# subsequent frames in the image. Image may be
# different size/mode.
Expand Down
15 changes: 7 additions & 8 deletions Tests/test_file_dcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@ def test_n_frames(self):

def test_eoferror(self):
im = Image.open(TEST_FILE)

n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertLess(im.tell(), n_frames)

# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_seek_too_far(self):
# Arrange
Expand Down
26 changes: 10 additions & 16 deletions Tests/test_file_fli.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,14 @@ def test_n_frames(self):

def test_eoferror(self):
im = Image.open(animated_test_file)

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 negative seek
im = Image.open(static_test_file)
im.seek(-1)
self.assertEqual(im.tell(), 0)

# Test seek past end of file
self.assertRaises(EOFError, im.seek, 2)
# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_seek_tell(self):
im = Image.open(animated_test_file)
Expand All @@ -91,6 +81,10 @@ def test_seek_tell(self):
layer_number = im.tell()
self.assertEqual(layer_number, 2)

im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)


if __name__ == '__main__':
unittest.main()
15 changes: 7 additions & 8 deletions Tests/test_file_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,14 @@ def test_n_frames(self):

def test_eoferror(self):
im = Image.open(TEST_GIF)

n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertLess(im.tell(), n_frames)

# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif")
Expand Down
15 changes: 7 additions & 8 deletions Tests/test_file_im.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ def test_n_frames(self):

def test_eoferror(self):
im = Image.open(TEST_IM)

n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertLess(im.tell(), n_frames)

# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_roundtrip(self):
for mode in ["RGB", "P"]:
Expand Down
15 changes: 7 additions & 8 deletions Tests/test_file_mpo.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,14 @@ def test_n_frames(self):

def test_eoferror(self):
im = Image.open("Tests/images/sugarshack.mpo")

n_frames = im.n_frames
while True:
n_frames -= 1
try:
im.seek(n_frames)
break
except EOFError:
self.assertLess(im.tell(), n_frames)

# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_image_grab(self):
for test_file in test_files:
Expand Down
23 changes: 10 additions & 13 deletions Tests/test_file_psd.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,23 @@ def test_n_frames(self):

def test_eoferror(self):
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
while True:
n_frames -= 1
try:
# PSD seek index starts at 1 rather than 0
im.seek(n_frames+1)
break
except EOFError:
self.assertLess(im.tell(), n_frames)
# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames-1)

def test_seek_tell(self):
im = Image.open(test_file)

layer_number = im.tell()
self.assertEqual(layer_number, 0)
self.assertEqual(layer_number, 1)

im.seek(0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
self.assertRaises(EOFError, im.seek, 0)

im.seek(1)
layer_number = im.tell()
Expand Down

0 comments on commit 3051178

Please sign in to comment.