Coverage for lib/lottie/utils/restructure.py: 71%
180 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
1from .. import objects
4class RestructuredLayer:
5 def __init__(self, lottie):
6 self.lottie = lottie
7 self.children_pre = []
8 self.children_post = []
9 self.structured = False
10 self.shapegroup = None
11 self.matte_target = False
12 self.matte_source = None
13 self.matte_id = None
15 def add(self, child):
16 c = self.children_pre if self.structured else self.children_post
17 c.insert(0, child)
20class RestructuredShapeGroup:
21 def __init__(self, lottie):
22 self.lottie = lottie
23 self.children = []
24 self.fill = None
25 self.stroke = None
26 self.layer = False
27 self.paths = None
28 self.stroke_above = False
30 def empty(self):
31 return not self.children
33 def finalize(self, thresh=6):
34 for g in self.subgroups:
35 if g.layer: 35 ↛ 36line 35 didn't jump to line 36, because the condition on line 35 was never true
36 self.layer = True
37 for gg in self.subgroups:
38 gg.layer = True
39 return
40 nchild = len(self.children)
41 self.layer = nchild > thresh and self.lottie.name
43 @property
44 def subgroups(self):
45 for g in self.children:
46 if isinstance(g, RestructuredShapeGroup):
47 yield g
49 def add(self, child):
50 self.children.insert(0, child)
53class RestructuredModifier:
54 def __init__(self, lottie, child):
55 self.child = child
56 self.lottie = lottie
59class RestructuredPathMerger:
60 def __init__(self):
61 self.paths = []
63 def append(self, path):
64 self.paths.append(path)
67class RestructuredAnimation:
68 def __init__(self):
69 self.layers = []
70 self.precomp = {}
73class AbstractBuilder:
74 merge_paths = False
76 def _on_animation(self, animation):
77 raise NotImplementedError()
79 def _on_shapegroup(self, shapegroup, out_parent):
80 raise NotImplementedError()
82 def _on_shape(self, shape, shapegroup, out_parent):
83 raise NotImplementedError()
85 def _on_merged_path(self, shape, shapegroup, out_parent):
86 raise NotImplementedError()
88 def _on_shape_modifier(self, shape, shapegroup, out_parent):
89 raise NotImplementedError()
91 def process(self, animation: objects.Animation):
92 out_parent = self._on_animation(animation)
94 restructured = self.restructure_animation(animation, self.merge_paths)
95 for id, layers in restructured.precomp.items(): 95 ↛ 96line 95 didn't jump to line 96, because the loop on line 95 never started
96 self._on_precomp(id, out_parent, layers)
98 for asset in animation.assets or []: 98 ↛ 99line 98 didn't jump to line 99, because the loop on line 98 never started
99 self._on_asset(asset)
101 if animation.fonts and animation.fonts.list: 101 ↛ 102line 101 didn't jump to line 102, because the condition on line 101 was never true
102 for font in animation.fonts.list:
103 self._on_font(font)
105 for layer_builder in restructured.layers:
106 self.process_layer(layer_builder, out_parent)
108 def _on_layer(self, layer_builder, out_parent):
109 raise NotImplementedError()
111 def _on_precomp(self, id, out_parent, layers):
112 raise NotImplementedError()
114 def _on_asset(self, asset):
115 pass
117 def _on_font(self, font):
118 pass
120 def process_layer(self, layer_builder, out_parent):
121 out_layer = self._on_layer(layer_builder, out_parent)
123 if out_layer is None: 123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true
124 return
126 for c in layer_builder.children_pre: 126 ↛ 127line 126 didn't jump to line 127, because the loop on line 126 never started
127 self.process_layer(c, out_layer)
129 shapegroup = getattr(layer_builder, "shapegroup", None)
130 if shapegroup: 130 ↛ 133line 130 didn't jump to line 133, because the condition on line 130 was never false
131 self.shapegroup_process_children(shapegroup, out_layer)
133 for c in layer_builder.children_post: 133 ↛ 134line 133 didn't jump to line 134, because the loop on line 133 never started
134 self.process_layer(c, out_layer)
136 self._on_layer_end(out_layer)
138 def _on_layer_end(self, out_layer):
139 pass
141 def shapegroup_process_child(self, shape, shapegroup, out_parent):
142 if isinstance(shape, RestructuredShapeGroup):
143 return self._on_shapegroup(shape, out_parent)
144 elif isinstance(shape, RestructuredPathMerger): 144 ↛ 145line 144 didn't jump to line 145, because the condition on line 144 was never true
145 return self._on_merged_path(shape, shapegroup, out_parent)
146 elif isinstance(shape, RestructuredModifier):
147 return self._on_shape_modifier(shape, shapegroup, out_parent)
148 else:
149 return self._on_shape(shape, shapegroup, out_parent)
151 def shapegroup_process_children(self, shapegroup, out_parent):
152 for shape in shapegroup.children:
153 self.shapegroup_process_child(shape, shapegroup, out_parent)
155 def restructure_animation(self, animation, merge_paths):
156 restr = RestructuredAnimation()
157 restr.layers = self.restructure_layer_list(animation.layers, merge_paths)
158 if animation.assets: 158 ↛ 159line 158 didn't jump to line 159, because the condition on line 158 was never true
159 for asset in animation.assets:
160 if isinstance(asset, objects.Precomp):
161 restr.precomp[asset.id] = self.restructure_layer_list(asset.layers, merge_paths)
162 return restr
164 def restructure_layer_list(self, layer_list, merge_paths):
165 layers = {}
166 flat_layers = []
167 prev = None
168 for layer in layer_list:
169 laybuilder = RestructuredLayer(layer)
170 flat_layers.append(laybuilder)
172 if layer.index is not None: 172 ↛ 175line 172 didn't jump to line 175, because the condition on line 172 was never false
173 layers[layer.index] = laybuilder
175 if isinstance(layer, objects.ShapeLayer): 175 ↛ 182line 175 didn't jump to line 182, because the condition on line 175 was never false
176 laybuilder.shapegroup = RestructuredShapeGroup(layer)
177 laybuilder.layer = True
178 for shape in layer.shapes:
179 self.restructure_shapegroup(shape, laybuilder.shapegroup, merge_paths)
180 laybuilder.shapegroup.finalize()
182 if layer.matte_mode not in {None, objects.MatteMode.Normal}: 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true
183 laybuilder.matte_source = prev
184 if prev:
185 prev.matte_target = laybuilder
187 prev = laybuilder
189 top_layers = []
190 for layer in flat_layers:
191 layer.structured = True
192 if layer.lottie.parent_index is not None: 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true
193 layers[layer.lottie.parent_index].add(layer)
194 else:
195 top_layers.insert(0, layer)
197 return top_layers
199 def restructure_shapegroup(self, shape, shape_group, merge_paths):
200 if isinstance(shape, (objects.Fill, objects.GradientFill)):
201 if not shape_group.fill: 201 ↛ exitline 201 didn't return from function 'restructure_shapegroup', because the condition on line 201 was never false
202 shape_group.fill = shape
203 elif isinstance(shape, objects.BaseStroke):
204 if not shape_group.stroke or shape_group.stroke.width.get_value(0) < shape.width.get_value(0): 204 ↛ exitline 204 didn't return from function 'restructure_shapegroup', because the condition on line 204 was never false
205 shape_group.stroke = shape
206 if not shape_group.fill:
207 shape_group.stroke_above = True
208 elif isinstance(shape, (objects.Path)):
209 if merge_paths: 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true
210 if not shape_group.paths:
211 shape_group.paths = RestructuredPathMerger()
212 shape_group.add(shape_group.paths)
213 shape_group.paths.append(shape)
214 else:
215 shape_group.add(shape)
216 elif isinstance(shape, (objects.Group)):
217 subgroup = RestructuredShapeGroup(shape)
218 shape_group.add(subgroup)
219 merge_paths = self.merge_paths and not any(isinstance(p, objects.Group) for p in shape.shapes) 219 ↛ exitline 219 didn't run the generator expression on line 219
220 for subshape in shape.shapes:
221 self.restructure_shapegroup(subshape, subgroup, merge_paths)
222 subgroup.finalize()
223 elif isinstance(shape, (objects.Modifier)):
224 if shape_group.children: 224 ↛ exitline 224 didn't return from function 'restructure_shapegroup', because the condition on line 224 was never false
225 ch = shape_group.children.pop(0)
226 shape_group.add(RestructuredModifier(shape, ch))
227 elif isinstance(shape, (objects.ShapeElement)): 227 ↛ 229line 227 didn't jump to line 229, because the condition on line 227 was never false
228 shape_group.add(shape)
229 elif isinstance(shape, (objects.base.CustomObject)):
230 if self._custom_object_supported(shape):
231 shape_group.add(shape)
232 else:
233 self.restructure_shapegroup(shape.wrapped, shape_group, self.merge_paths)
235 def _custom_object_supported(self, shape):
236 return False