Skip to content

Commit f83f197

Browse files
authored
Merge pull request #4552 from radarhere/animation
Simplified animation code
2 parents ddebe27 + 574b0ee commit f83f197

7 files changed

Lines changed: 20 additions & 69 deletions

File tree

src/PIL/DcxImagePlugin.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,10 @@ def _open(self):
5959

6060
self.__fp = self.fp
6161
self.frame = None
62+
self.n_frames = len(self._offset)
63+
self.is_animated = self.n_frames > 1
6264
self.seek(0)
6365

64-
@property
65-
def n_frames(self):
66-
return len(self._offset)
67-
68-
@property
69-
def is_animated(self):
70-
return len(self._offset) > 1
71-
7266
def seek(self, frame):
7367
if not self._seek_check(frame):
7468
return

src/PIL/FliImagePlugin.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ def _open(self):
5151
raise SyntaxError("not an FLI/FLC file")
5252

5353
# frames
54-
self.__framecount = i16(s[6:8])
54+
self.n_frames = i16(s[6:8])
55+
self.is_animated = self.n_frames > 1
5556

5657
# image characteristics
5758
self.mode = "P"
@@ -110,14 +111,6 @@ def _palette(self, palette, shift):
110111
palette[i] = (r, g, b)
111112
i += 1
112113

113-
@property
114-
def n_frames(self):
115-
return self.__framecount
116-
117-
@property
118-
def is_animated(self):
119-
return self.__framecount > 1
120-
121114
def seek(self, frame):
122115
if not self._seek_check(frame):
123116
return

src/PIL/MicImagePlugin.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,14 @@ def _open(self):
6464

6565
self.__fp = self.fp
6666
self.frame = None
67+
self._n_frames = len(self.images)
68+
self.is_animated = self._n_frames > 1
6769

6870
if len(self.images) > 1:
6971
self.category = Image.CONTAINER
7072

7173
self.seek(0)
7274

73-
@property
74-
def n_frames(self):
75-
return len(self.images)
76-
77-
@property
78-
def is_animated(self):
79-
return len(self.images) > 1
80-
8175
def seek(self, frame):
8276
if not self._seek_check(frame):
8377
return

src/PIL/MpoImagePlugin.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,16 @@ def _open(self):
4848

4949
def _after_jpeg_open(self, mpheader=None):
5050
self.mpinfo = mpheader if mpheader is not None else self._getmp()
51-
self.__framecount = self.mpinfo[0xB001]
51+
self.n_frames = self.mpinfo[0xB001]
5252
self.__mpoffsets = [
5353
mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002]
5454
]
5555
self.__mpoffsets[0] = 0
5656
# Note that the following assertion will only be invalid if something
5757
# gets broken within JpegImagePlugin.
58-
assert self.__framecount == len(self.__mpoffsets)
58+
assert self.n_frames == len(self.__mpoffsets)
5959
del self.info["mpoffset"] # no longer needed
60+
self.is_animated = self.n_frames > 1
6061
self.__fp = self.fp # FIXME: hack
6162
self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame
6263
self.__frame = 0
@@ -67,14 +68,6 @@ def _after_jpeg_open(self, mpheader=None):
6768
def load_seek(self, pos):
6869
self.__fp.seek(pos)
6970

70-
@property
71-
def n_frames(self):
72-
return self.__framecount
73-
74-
@property
75-
def is_animated(self):
76-
return self.__framecount > 1
77-
7871
def seek(self, frame):
7972
if not self._seek_check(frame):
8073
return

src/PIL/PsdImagePlugin.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def _open(self):
119119
if size:
120120
self.layers = _layerinfo(self.fp)
121121
self.fp.seek(end)
122+
self.n_frames = len(self.layers)
123+
self.is_animated = self.n_frames > 1
122124

123125
#
124126
# image descriptor
@@ -130,14 +132,6 @@ def _open(self):
130132
self.frame = 1
131133
self._min_frame = 1
132134

133-
@property
134-
def n_frames(self):
135-
return len(self.layers)
136-
137-
@property
138-
def is_animated(self):
139-
return len(self.layers) > 1
140-
141135
def seek(self, layer):
142136
if not self._seek_check(layer):
143137
return

src/PIL/TiffImagePlugin.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,10 +1015,6 @@ def n_frames(self):
10151015
self.seek(current)
10161016
return self._n_frames
10171017

1018-
@property
1019-
def is_animated(self):
1020-
return self._is_animated
1021-
10221018
def seek(self, frame):
10231019
"""Select a given frame as current image"""
10241020
if not self._seek_check(frame):
@@ -1052,7 +1048,7 @@ def _seek(self, frame):
10521048
if self.__next == 0:
10531049
self._n_frames = frame + 1
10541050
if len(self._frame_pos) == 1:
1055-
self._is_animated = self.__next != 0
1051+
self.is_animated = self.__next != 0
10561052
self.__frame += 1
10571053
self.fp.seek(self._frame_pos[frame])
10581054
self.tag_v2.load(self.fp)
@@ -1087,7 +1083,7 @@ def load_end(self):
10871083

10881084
# allow closing if we're on the first frame, there's no next
10891085
# This is the ImageFile.load path only, libtiff specific below.
1090-
if not self._is_animated:
1086+
if not self.is_animated:
10911087
self._close_exclusive_fp_after_loading = True
10921088

10931089
def _load_libtiff(self):
@@ -1133,7 +1129,7 @@ def _load_libtiff(self):
11331129
except ValueError:
11341130
raise OSError("Couldn't set the image")
11351131

1136-
close_self_fp = self._exclusive_fp and not self._is_animated
1132+
close_self_fp = self._exclusive_fp and not self.is_animated
11371133
if hasattr(self.fp, "getvalue"):
11381134
# We've got a stringio like thing passed in. Yay for all in memory.
11391135
# The decoder needs the entire file in one shot, so there's not

src/PIL/WebPImagePlugin.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def _open(self):
5454
self._size = width, height
5555
self.fp = BytesIO(data)
5656
self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
57-
self._n_frames = 1
57+
self.n_frames = 1
58+
self.is_animated = False
5859
return
5960

6061
# Use the newer AnimDecoder API to parse the (possibly) animated file,
@@ -72,7 +73,8 @@ def _open(self):
7273
bgcolor & 0xFF,
7374
)
7475
self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
75-
self._n_frames = frame_count
76+
self.n_frames = frame_count
77+
self.is_animated = self.n_frames > 1
7678
self.mode = "RGB" if mode == "RGBX" else mode
7779
self.rawmode = mode
7880
self.tile = []
@@ -90,30 +92,15 @@ def _open(self):
9092

9193
# Initialize seek state
9294
self._reset(reset=False)
93-
self.seek(0)
9495

9596
def _getexif(self):
9697
if "exif" not in self.info:
9798
return None
9899
return dict(self.getexif())
99100

100-
@property
101-
def n_frames(self):
102-
return self._n_frames
103-
104-
@property
105-
def is_animated(self):
106-
return self._n_frames > 1
107-
108101
def seek(self, frame):
109-
if not _webp.HAVE_WEBPANIM:
110-
return super().seek(frame)
111-
112-
# Perform some simple checks first
113-
if frame >= self._n_frames:
114-
raise EOFError("attempted to seek beyond end of sequence")
115-
if frame < 0:
116-
raise EOFError("negative frame index is not valid")
102+
if not self._seek_check(frame):
103+
return
117104

118105
# Set logical frame to requested position
119106
self.__logical_frame = frame

0 commit comments

Comments
 (0)