-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnimation.py
More file actions
95 lines (79 loc) · 3.36 KB
/
Copy pathAnimation.py
File metadata and controls
95 lines (79 loc) · 3.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import math
import time
import logging
import imageio
from PIL import Image
class Animation:
"""
Represents an animation and provides playback utilities
"""
def __init__(self, filepath:str, cache_limit:int=128):
""" Creates an instance of Animation
Args:
filepath: The filepath to the animation to load
cache_limit: (OPTIONAL) Indicates the limit on frames to stop caching
"""
self._filepath = filepath
self._cache = []
self._frame_number = 0
self._current_frame = None
self._reader = None
self._frame_delay = 1
self._next_frame_timer = 0
self.frames = 0
self.playing = False
logging.debug(f"Reading expression file {filepath}")
reader = imageio.get_reader(filepath)
self.frames = reader.get_length()
if self.frames == math.inf:
self.frames = reader.count_frames()
meta = reader.get_meta_data()
if "fps" in meta:
self._frame_delay = 1000 / float(meta["fps"])
elif "duration" in meta:
self._frame_delay = int(meta["duration"])
logging.debug(f"\tExpression has {self.frames} frames and a rate of {self._frame_delay}s")
if self.frames < cache_limit:
logging.info(f"\tCaching expression frames...")
for _ in range(self.frames):
raw_frame = reader.get_next_data()
self._cache.append(Image.fromarray(raw_frame).crop((0, 0, 32, 16)))
else:
logging.debug("\tExpression frames are over cache limit, not storing!")
def start(self):
""" Starts the animation from the begining """
self._frame_number = 0
self._next_frame_timer = time.monotonic()
self.playing = True
if not self._cache:
self._reader = imageio.get_reader(self._filepath)
def get_frame(self) -> Image.Image:
""" Gets the current frame of the animation
Returns:
Image.Image: The pillow image representing the frame
"""
if time.monotonic() > self._next_frame_timer:
self._next_frame_timer += self._frame_delay / 1000
self._frame_number = (self._frame_number+1) % self.frames
if self._cache:
self._current_frame = self._cache[self._frame_number].copy()
else:
if self._frame_number == 0:
self._reader.set_image_index(0)
try:
raw_frame = self._reader.get_next_data()
except IndexError:
self._frame_number = 0
self._reader.set_image_index(0)
raw_frame = self._reader.get_next_data()
self._current_frame = Image.fromarray(raw_frame).crop((0, 0, 32, 16))
if self._current_frame is None:
return Image.new("RGB", (32, 16), "black")
else:
return self._current_frame
def stop(self):
""" Stops the current animation and shuts down any readers """
self.playing = False
if not self._cache and hasattr(self, "_reader"):
del self._reader
self._current_frame = None