Coverage for lib/lottie/objects/animation.py: 67%

105 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-20 16:17 +0100

1from .base import LottieObject, LottieProp, PseudoBool, Index 

2from .layers import Layer, PreCompLayer 

3from .assets import Asset, Precomp 

4from .text import FontList, Chars 

5from .composition import Composition 

6from .helpers import VisualObject, Marker 

7 

8##\defgroup Lottie Lottie 

9# 

10# Objects of the lottie file structure. 

11 

12## \defgroup LottieCheck Lottie (to check) 

13# 

14# Lottie objects that have not been tested 

15 

16 

17## @ingroup Lottie 

18class Metadata(LottieObject): 

19 """! 

20 Document metadata 

21 """ 

22 _props = [ 

23 LottieProp("author", "a", str, False), 

24 LottieProp("generator", "g", str, False), 

25 LottieProp("keywords", "k", str, True), 

26 LottieProp("description", "d", str, False), 

27 LottieProp("theme_color", "tc", str, False), 

28 ] 

29 

30 def __init__(self): 

31 self.generator = None 

32 self.author = None 

33 self.keywords = None 

34 self.description = None 

35 self.theme_color = None 

36 

37 

38#ingroup Lottie 

39class UserMetadata(LottieObject): 

40 """! 

41 User-defined metadata 

42 """ 

43 _props = [ 

44 LottieProp("filename", "filename", str, False), 

45 LottieProp("custom_properties", "customProps", dict, False), 

46 ] 

47 

48 def __init__(self): 

49 super().__init__() 

50 

51 self.filename = None 

52 self.custom_properties = {} 

53 

54 

55#ingroup Lottie 

56class MotionBlur(LottieObject): 

57 """! 

58 Motion blur settings 

59 """ 

60 _props = [ 

61 LottieProp("shutter_angle", "sa", float, False), 

62 LottieProp("shutter_phase", "sp", float, False), 

63 LottieProp("samples_per_frame", "spf", float, False), 

64 LottieProp("adaptive_sample_limit", "asl", float, False), 

65 ] 

66 

67 def __init__(self): 

68 super().__init__() 

69 

70 ## Angle in degrees 

71 self.shutter_angle = None 

72 ## Angle in degrees 

73 self.shutter_phase = None 

74 self.samples_per_frame = None 

75 self.adaptive_sample_limit = None 

76 

77 

78## @ingroup Lottie 

79class Animation(Composition, VisualObject): 

80 """! 

81 Top level object, describing the animation 

82 

83 @see http://docs.aenhancers.com/items/compitem/ 

84 """ 

85 _props = [ 

86 LottieProp("version", "v", str, False), 

87 LottieProp("frame_rate", "fr", float, False), 

88 LottieProp("in_point", "ip", float, False), 

89 LottieProp("out_point", "op", float, False), 

90 LottieProp("width", "w", int, False), 

91 LottieProp("height", "h", int, False), 

92 LottieProp("threedimensional", "ddd", PseudoBool, False), 

93 LottieProp("assets", "assets", Asset, True), 

94 LottieProp("extra_compositions", "comps", Precomp, True), 

95 LottieProp("fonts", "fonts", FontList), 

96 LottieProp("chars", "chars", Chars, True), 

97 LottieProp("markers", "markers", Marker, True), 

98 LottieProp("motion_blur", "mb", MotionBlur, False), 

99 LottieProp("metadata", "meta", Metadata, False), 

100 LottieProp("user_metadata", "metadata", UserMetadata, False), 

101 ] 

102 _version = "5.5.2" 

103 

104 def __init__(self, n_frames=60, framerate=60): 

105 super().__init__() 

106 ## The time when the composition work area begins, in frames. 

107 self.in_point = 0 

108 ## The time when the composition work area ends. 

109 ## Sets the final Frame of the animation 

110 self.out_point = n_frames 

111 ## Frames per second 

112 self.frame_rate = framerate 

113 ## Composition Width 

114 self.width = 512 

115 ## Composition has 3-D layers 

116 self.threedimensional = False 

117 ## Composition Height 

118 self.height = 512 

119 ## Bodymovin Version 

120 self.version = self._version 

121 ## source items that can be used in multiple places. Comps and Images for now. 

122 self.assets = [] # Image, Precomp 

123 ## List of Extra compositions not referenced by anything 

124 self.extra_compositions = None 

125 ## source chars for text layers 

126 self.chars = None 

127 ## Available fonts 

128 self.fonts = None 

129 self.metadata = None 

130 self.user_metadata = None 

131 self.motion_blur = None 

132 self.markers = None 

133 

134 def precomp(self, name): 

135 for ass in self.assets: 

136 if isinstance(ass, Precomp) and ass.id == name: 

137 return ass 

138 return None 

139 

140 def _on_prepare_layer(self, layer): 

141 if layer.in_point is None: 

142 layer.in_point = self.in_point 

143 if layer.out_point is None: 

144 layer.out_point = self.out_point 

145 

146 def to_precomp(self): 

147 """! 

148 Turns the main comp into a precomp 

149 """ 

150 precomp = Precomp() 

151 #precomp.frame_rate = self.frame_rate 

152 precomp.layers = self.layers 

153 precomp.name = self.name 

154 name_id = 0 

155 base_name = self.name or "Animation" 

156 name = base_name 

157 index = 0 

158 while True: 

159 if index >= len(self.assets): 159 ↛ 162line 159 didn't jump to line 162, because the condition on line 159 was never false

160 break 

161 

162 while self.assets[index].id == name: 

163 name_id += 1 

164 name = "%s %s" % (base_name, name_id) 

165 index = -1 

166 

167 index += 1 

168 precomp.id = name 

169 self.assets.append(precomp) 

170 

171 precomp_layer = PreCompLayer() 

172 precomp_layer.width = self.width 

173 precomp_layer.height = self.height 

174 precomp_layer.in_point = self.in_point 

175 precomp_layer.out_point = self.out_point 

176 precomp_layer.reference_id = name 

177 self.layers = [precomp_layer] 

178 return precomp 

179 

180 def scale(self, width, height): 

181 """! 

182 Scales the animation so it fits in width/height 

183 """ 

184 if self.width != width or self.height != height: 

185 self.to_precomp() 

186 

187 scale = min(width/self.width, height/self.height) 

188 self.width = width 

189 self.height = height 

190 

191 self.layers[0].transform.scale.value *= scale 

192 

193 def tgs_sanitize(self): 

194 """! 

195 Cleans up some things to ensure it works as a telegram sticker 

196 """ 

197 self.scale(512, 512) 

198 

199 if self.frame_rate < 45: 

200 self.frame_rate = 30 

201 else: 

202 self.frame_rate = 60 

203 

204 def _fixup(self): 

205 super()._fixup() 

206 if self.assets: 

207 for ass in self.assets: 

208 if isinstance(ass, Precomp): 

209 ass.animation = self 

210 ass._fixup() 

211 

212 def __str__(self): 

213 return self.name or super().__str__()