Optimized GifImagePlugin seek-related operations

- Add: Added `self.__prev_offset` to always hold the offset of the frame just before the current frame.
- Add: Seeking beyond the last frame automatically computes `GifImagePlugin.n_frames`.
- Change: Optimized `GifImagePlugin.seek()`, `GifImagePlugin.n_frames` and `GifImagePlugin.is_animated` when seeking back to the current frame before invoking the operation, after an `EOFError` is raised.
- Change: Replaced all internal calls to `GifImagePlugin.tell()` with direct references to `self.__frame`.
This commit is contained in:
AnonymouX47 2022-02-20 22:03:32 +01:00
parent b803b7c2a1
commit 5bdd0762f3

View File

@ -94,13 +94,17 @@ class GifImageFile(ImageFile.ImageFile):
@property @property
def n_frames(self): def n_frames(self):
if self._n_frames is None: if self._n_frames is None:
current = self.tell() prev_offset = self.__prev_offset
current = self.__frame
try: try:
while True: while True:
self.seek(self.tell() + 1) self._seek(self.__frame + 1)
except EOFError: except EOFError:
self._n_frames = self.tell() + 1 self._n_frames = self.__frame
self.seek(current)
self.__offset = prev_offset
self.__frame = current - 1
self._seek(current)
return self._n_frames return self._n_frames
@property @property
@ -109,7 +113,8 @@ class GifImageFile(ImageFile.ImageFile):
if self._n_frames is not None: if self._n_frames is not None:
self._is_animated = self._n_frames != 1 self._is_animated = self._n_frames != 1
else: else:
current = self.tell() prev_offset = self.__prev_offset
current = self.__frame
try: try:
self.seek(1) self.seek(1)
@ -117,7 +122,9 @@ class GifImageFile(ImageFile.ImageFile):
except EOFError: except EOFError:
self._is_animated = False self._is_animated = False
self.seek(current) self.__offset = prev_offset
self.__frame = current - 1
self._seek(current)
return self._is_animated return self._is_animated
def seek(self, frame): def seek(self, frame):
@ -127,12 +134,17 @@ class GifImageFile(ImageFile.ImageFile):
self.im = None self.im = None
self._seek(0) self._seek(0)
prev_offset = self.__prev_offset
last_frame = self.__frame last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1): for f in range(self.__frame + 1, frame + 1):
try: try:
self._seek(f) self._seek(f)
except EOFError as e: except EOFError as e:
self.seek(last_frame) if not self._n_frames:
self._n_frames = f
self.__offset = prev_offset
self.__frame = last_frame - 1
self._seek(last_frame)
raise EOFError("no more images in GIF file") from e raise EOFError("no more images in GIF file") from e
def _seek(self, frame): def _seek(self, frame):
@ -157,6 +169,7 @@ class GifImageFile(ImageFile.ImageFile):
self.tile = [] self.tile = []
self.fp = self.__fp self.fp = self.__fp
self.__prev_offset = self.__offset
if self.__offset: if self.__offset:
# backup to last frame # backup to last frame
self.fp.seek(self.__offset) self.fp.seek(self.__offset)