Coverage for lib/lottie/objects/layers.py: 69%

160 statements  

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

1import warnings 

2from .base import LottieObject, LottieProp, PseudoBool, LottieEnum, LottieValueConverter 

3from .effects import Effect 

4from .helpers import Transform, Mask, VisualObject, BlendMode 

5from .shapes import ShapeElement 

6from .text import TextAnimatorData 

7from .properties import Value, MultiDimensional 

8from ..utils.color import Color, color_from_hex, color_to_hex 

9from .styles import LayerStyle 

10 

11 

12## @ingroup Lottie 

13## @todo SVG masks 

14class MatteMode(LottieEnum): 

15 Normal = 0 

16 Alpha = 1 

17 InvertedAlpha = 2 

18 Luma = 3 

19 InvertedLuma = 4 

20 

21 

22#ingroup Lottie 

23class Layer(VisualObject): 

24 """! 

25 Base class for all layers 

26 """ 

27 _props = [ 

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

29 LottieProp("hidden", "hd", bool, False), 

30 LottieProp("type", "ty", int, False), 

31 LottieProp("index", "ind", int, False), 

32 LottieProp("parent_index", "parent", int, False), 

33 LottieProp("time_stretch", "sr", float, False), 

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

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

36 LottieProp("start_time", "st", float, False), 

37 ] 

38 ## %Layer type. 

39 ## @see https://github.com/bodymovin/bodymovin-extension/blob/master/bundle/jsx/enums/layerTypes.jsx 

40 type = None 

41 _classses = {} 

42 

43 def __init__(self): 

44 super().__init__() 

45 

46 ## Whether the layer is threedimensional 

47 self.threedimensional = 0 

48 ## Whether the layer is hidden 

49 self.hidden = None 

50 ## Index that can be used for parenting and referenced in expressions 

51 self.index = None 

52 ## Must be the `ind` property of another layer 

53 self.parent_index = None 

54 self.time_stretch = 1 

55 ## Frame when the layer becomes visible 

56 self.in_point = None 

57 ## Frame when the layer becomes invisible 

58 self.out_point = None 

59 ## Start Time of layer. Sets the start time of the layer. 

60 self.start_time = 0 

61 

62 @classmethod 

63 def _load_get_class(cls, lottiedict): 

64 if not Layer._classses: 

65 Layer._classses = { 

66 sc.type: sc 

67 for sc in Layer.__subclasses__() + VisualLayer.__subclasses__() 

68 if sc.type is not None 

69 } 

70 type_id = lottiedict["ty"] 

71 if type_id not in Layer._classses: 

72 warnings.warn("Unknown layer type: %s" % type_id) 

73 return Layer 

74 return Layer._classses[type_id] 

75 

76 

77## @ingroup Lottie 

78class VisualLayer(Layer): 

79 """! 

80 Base class for layers that have a visual component 

81 """ 

82 _props = [ 

83 LottieProp("collapse_transform", "cp", bool, False), 

84 LottieProp("transform", "ks", Transform, False), 

85 LottieProp("auto_orient", "ao", PseudoBool, False), 

86 

87 LottieProp("blend_mode", "bm", BlendMode, False), 

88 

89 LottieProp("matte_mode", "tt", MatteMode, False), 

90 LottieProp("css_class", "cl", str, False), 

91 LottieProp("layer_xml_id", "ln", str, False), 

92 LottieProp("layer_xml_tag_name", "tg", str, False), 

93 

94 LottieProp("motion_blur", "mb", bool, False), 

95 LottieProp("layer_style", "sy", LayerStyle, True), 

96 

97 LottieProp("has_masks", "hasMask", bool, False), 

98 LottieProp("masks", "masksProperties", Mask, True), 

99 LottieProp("effects", "ef", Effect, True), 

100 LottieProp("matte_target", "td", int, False), 

101 ] 

102 

103 @property 

104 def has_masks(self): 

105 """! 

106 Whether the layer has some masks applied 

107 """ 

108 return bool(self.masks) if getattr(self, "masks") is not None else None 

109 

110 def __init__(self): 

111 super().__init__() 

112 

113 self.collapse_transform = None 

114 ## Transform properties 

115 self.transform = Transform() 

116 ## Auto-Orient along path AE property. 

117 self.auto_orient = False 

118 

119 ## CSS class used by the SVG renderer 

120 self.css_class = None 

121 ## `id` attribute used by the SVG renderer 

122 self.layer_xml_id = None 

123 ## tag name used by the SVG renderer 

124 self.layer_xml_tag_name = None 

125 

126 ## Whether motion blur is enabled for the layer 

127 self.motion_blur = None 

128 ## Styling effects for this layer 

129 self.layer_style = None 

130 

131 ## List of Effects 

132 self.effects = None 

133 ## Layer Time Stretching 

134 self.stretch = 1 

135 ## List of Masks 

136 self.masks = None 

137 ## Blend Mode 

138 self.blend_mode = BlendMode.Normal 

139 ## Matte mode, the layer will inherit the transparency from the layer above 

140 self.matte_mode = None 

141 self.matte_target = None 

142 ## Composition owning the layer, set by add_layer 

143 self.composition = None 

144 

145 def add_child(self, layer): 

146 if not self.composition or self.index is None: 

147 raise Exception("Must set composition / index first") 

148 self._child_inout_auto(layer) 

149 self.composition.add_layer(layer) 

150 layer.parent_index = self.index 

151 return layer 

152 

153 def _child_inout_auto(self, layer): 

154 if layer.in_point is None: 154 ↛ 155line 154 didn't jump to line 155, because the condition on line 154 was never true

155 layer.in_point = self.in_point 

156 if layer.out_point is None: 156 ↛ 157line 156 didn't jump to line 157, because the condition on line 156 was never true

157 layer.out_point = self.out_point 

158 

159 @property 

160 def parent(self): 

161 if self.parent_index is None: 

162 return None 

163 return self.composition.layer(self.parent_index) 

164 

165 @parent.setter 

166 def parent(self, layer): 

167 if layer is None: 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true

168 self.parent_index = None 

169 else: 

170 self.parent_index = layer.index 

171 layer._child_inout_auto(self) 

172 

173 @property 

174 def children(self): 

175 for layer in self.composition.layers: 

176 if layer.parent_index == self.index: 

177 yield layer 

178 

179 def __repr__(self): 

180 return "<%s %s %s>" % (type(self).__name__, self.index, self.name) 

181 

182 def __str__(self): 

183 return "%s %s" % ( 

184 self.name or super().__str__(), 

185 self.index if self.index is not None else "" 

186 ) 

187 

188 def remove(self): 

189 """! 

190 @brief Removes this layer from the componsitin 

191 """ 

192 self.composition.remove_layer(self) 

193 

194 

195## @ingroup Lottie 

196class NullLayer(VisualLayer): 

197 """! 

198 Layer with no data, useful to group layers together 

199 """ 

200 ## %Layer type. 

201 type = 3 

202 

203 def __init__(self): 

204 super().__init__() 

205 

206 

207## @ingroup Lottie 

208class TextLayer(VisualLayer): 

209 _props = [ 

210 LottieProp("data", "t", TextAnimatorData, False), 

211 ] 

212 ## %Layer type. 

213 type = 5 

214 

215 def __init__(self): 

216 super().__init__() 

217 ## Text Data 

218 self.data = TextAnimatorData() 

219 

220 

221## @ingroup Lottie 

222class ShapeLayer(VisualLayer): 

223 """! 

224 Layer containing ShapeElement objects 

225 """ 

226 _props = [ 

227 LottieProp("shapes", "shapes", ShapeElement, True), 

228 ] 

229 ## %Layer type. 

230 type = 4 

231 

232 def __init__(self): 

233 super().__init__() 

234 ## Shape list of items 

235 self.shapes = [] # ShapeElement 

236 

237 def add_shape(self, shape): 

238 self.shapes.append(shape) 

239 return shape 

240 

241 def insert_shape(self, index, shape): 

242 self.shapes.insert(index, shape) 

243 return shape 

244 

245 

246## @ingroup Lottie 

247## @todo SIF I/O 

248class ImageLayer(VisualLayer): 

249 _props = [ 

250 LottieProp("image_id", "refId", str, False), 

251 ] 

252 ## %Layer type. 

253 type = 2 

254 

255 def __init__(self, image_id=""): 

256 super().__init__() 

257 ## id pointing to the source image defined on 'assets' object 

258 self.image_id = image_id 

259 

260 

261## @ingroup Lottie 

262class PreCompLayer(VisualLayer): 

263 _props = [ 

264 LottieProp("reference_id", "refId", str, False), 

265 LottieProp("time_remapping", "tm", Value, False), 

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

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

268 ] 

269 ## %Layer type. 

270 type = 0 

271 

272 def __init__(self, reference_id=""): 

273 super().__init__() 

274 ## id pointing to the source composition defined on 'assets' object 

275 self.reference_id = reference_id 

276 ## Comp's Time remapping 

277 self.time_remapping = None 

278 ## Width 

279 self.width = 512 

280 ## Height 

281 self.height = 512 

282 

283 

284ColorString = LottieValueConverter(color_from_hex, color_to_hex, "Color string") 

285 

286 

287## @ingroup Lottie 

288class SolidColorLayer(VisualLayer): 

289 """! 

290 Layer with a solid color rectangle 

291 """ 

292 _props = [ 

293 LottieProp("color", "sc", ColorString, False), 

294 LottieProp("height", "sh", float, False), 

295 LottieProp("width", "sw", float, False), 

296 ] 

297 ## %Layer type. 

298 type = 1 

299 

300 def __init__(self, color=Color(), width=512, height=512): 

301 super().__init__() 

302 ## Color of the layer as a @c \#rrggbb hex 

303 self.color = color 

304 ## Height of the layer. 

305 self.height = height 

306 ## Width of the layer. 

307 self.width = width 

308 

309 

310#ingroup Lottie 

311class CameraLayer(Layer): 

312 """! 

313 3D Camera 

314 """ 

315 type = 13 

316 _props = [ 

317 LottieProp("type", "ty", int, False), 

318 LottieProp("transform", "ks", Transform, False), 

319 LottieProp("perspective", "pe", Value, False), 

320 ] 

321 

322 def __init__(self): 

323 super().__init__() 

324 

325 ## Layer transform 

326 self.transform = None 

327 ## Distance from the Z=0 plane. 

328 ## Small values yield a higher perspective effect. 

329 self.perspective = None 

330 

331 

332#ingroup Lottie 

333class DataLayer(Layer): 

334 _props = [ 

335 LottieProp("type", "ty", int, False), 

336 LottieProp("data_source_id", "refId", str, False), 

337 ] 

338 type = 15 

339 

340 def __init__(self): 

341 super().__init__() 

342 

343 ## ID of the data source in assets 

344 self.data_source_id = None 

345 

346 

347#ingroup Lottie 

348class AudioSettings(LottieObject): 

349 _props = [ 

350 LottieProp("level", "lv", MultiDimensional, False), 

351 ] 

352 

353 def __init__(self): 

354 super().__init__() 

355 

356 self.level = MultiDimensional([0, 0]) 

357 

358 

359#ingroup Lottie 

360class AudioLayer(Layer): 

361 """! 

362 A layer playing sounds 

363 """ 

364 _props = [ 

365 LottieProp("type", "ty", int, False), 

366 LottieProp("audio_settings", "au", AudioSettings, False), 

367 LottieProp("sound_id", "refId", str, False), 

368 ] 

369 type = 6 

370 

371 def __init__(self): 

372 super().__init__() 

373 

374 self.audio_settings = None 

375 ## ID of the sound as specified in the assets 

376 self.sound_id = None