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

1from .. import objects 

2 

3 

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 

14 

15 def add(self, child): 

16 c = self.children_pre if self.structured else self.children_post 

17 c.insert(0, child) 

18 

19 

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 

29 

30 def empty(self): 

31 return not self.children 

32 

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 

42 

43 @property 

44 def subgroups(self): 

45 for g in self.children: 

46 if isinstance(g, RestructuredShapeGroup): 

47 yield g 

48 

49 def add(self, child): 

50 self.children.insert(0, child) 

51 

52 

53class RestructuredModifier: 

54 def __init__(self, lottie, child): 

55 self.child = child 

56 self.lottie = lottie 

57 

58 

59class RestructuredPathMerger: 

60 def __init__(self): 

61 self.paths = [] 

62 

63 def append(self, path): 

64 self.paths.append(path) 

65 

66 

67class RestructuredAnimation: 

68 def __init__(self): 

69 self.layers = [] 

70 self.precomp = {} 

71 

72 

73class AbstractBuilder: 

74 merge_paths = False 

75 

76 def _on_animation(self, animation): 

77 raise NotImplementedError() 

78 

79 def _on_shapegroup(self, shapegroup, out_parent): 

80 raise NotImplementedError() 

81 

82 def _on_shape(self, shape, shapegroup, out_parent): 

83 raise NotImplementedError() 

84 

85 def _on_merged_path(self, shape, shapegroup, out_parent): 

86 raise NotImplementedError() 

87 

88 def _on_shape_modifier(self, shape, shapegroup, out_parent): 

89 raise NotImplementedError() 

90 

91 def process(self, animation: objects.Animation): 

92 out_parent = self._on_animation(animation) 

93 

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) 

97 

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) 

100 

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) 

104 

105 for layer_builder in restructured.layers: 

106 self.process_layer(layer_builder, out_parent) 

107 

108 def _on_layer(self, layer_builder, out_parent): 

109 raise NotImplementedError() 

110 

111 def _on_precomp(self, id, out_parent, layers): 

112 raise NotImplementedError() 

113 

114 def _on_asset(self, asset): 

115 pass 

116 

117 def _on_font(self, font): 

118 pass 

119 

120 def process_layer(self, layer_builder, out_parent): 

121 out_layer = self._on_layer(layer_builder, out_parent) 

122 

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 

125 

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) 

128 

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) 

132 

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) 

135 

136 self._on_layer_end(out_layer) 

137 

138 def _on_layer_end(self, out_layer): 

139 pass 

140 

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) 

150 

151 def shapegroup_process_children(self, shapegroup, out_parent): 

152 for shape in shapegroup.children: 

153 self.shapegroup_process_child(shape, shapegroup, out_parent) 

154 

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 

163 

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) 

171 

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 

174 

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() 

181 

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 

186 

187 prev = laybuilder 

188 

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) 

196 

197 return top_layers 

198 

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) 

234 

235 def _custom_object_supported(self, shape): 

236 return False