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
« 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
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
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 = {}
43 def __init__(self):
44 super().__init__()
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
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]
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),
87 LottieProp("blend_mode", "bm", BlendMode, False),
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),
94 LottieProp("motion_blur", "mb", bool, False),
95 LottieProp("layer_style", "sy", LayerStyle, True),
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 ]
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
110 def __init__(self):
111 super().__init__()
113 self.collapse_transform = None
114 ## Transform properties
115 self.transform = Transform()
116 ## Auto-Orient along path AE property.
117 self.auto_orient = False
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
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
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
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
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
159 @property
160 def parent(self):
161 if self.parent_index is None:
162 return None
163 return self.composition.layer(self.parent_index)
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)
173 @property
174 def children(self):
175 for layer in self.composition.layers:
176 if layer.parent_index == self.index:
177 yield layer
179 def __repr__(self):
180 return "<%s %s %s>" % (type(self).__name__, self.index, self.name)
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 )
188 def remove(self):
189 """!
190 @brief Removes this layer from the componsitin
191 """
192 self.composition.remove_layer(self)
195## @ingroup Lottie
196class NullLayer(VisualLayer):
197 """!
198 Layer with no data, useful to group layers together
199 """
200 ## %Layer type.
201 type = 3
203 def __init__(self):
204 super().__init__()
207## @ingroup Lottie
208class TextLayer(VisualLayer):
209 _props = [
210 LottieProp("data", "t", TextAnimatorData, False),
211 ]
212 ## %Layer type.
213 type = 5
215 def __init__(self):
216 super().__init__()
217 ## Text Data
218 self.data = TextAnimatorData()
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
232 def __init__(self):
233 super().__init__()
234 ## Shape list of items
235 self.shapes = [] # ShapeElement
237 def add_shape(self, shape):
238 self.shapes.append(shape)
239 return shape
241 def insert_shape(self, index, shape):
242 self.shapes.insert(index, shape)
243 return shape
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
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
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
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
284ColorString = LottieValueConverter(color_from_hex, color_to_hex, "Color string")
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
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
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 ]
322 def __init__(self):
323 super().__init__()
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
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
340 def __init__(self):
341 super().__init__()
343 ## ID of the data source in assets
344 self.data_source_id = None
347#ingroup Lottie
348class AudioSettings(LottieObject):
349 _props = [
350 LottieProp("level", "lv", MultiDimensional, False),
351 ]
353 def __init__(self):
354 super().__init__()
356 self.level = MultiDimensional([0, 0])
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
371 def __init__(self):
372 super().__init__()
374 self.audio_settings = None
375 ## ID of the sound as specified in the assets
376 self.sound_id = None