2 from xml.dom
import minidom
4 from ...
import objects
5 from ...nvector
import NVector
6 from ...utils
import restructure
11 objects.BlendMode.Normal: api.BlendMethod.Composite,
12 objects.BlendMode.Multiply: api.BlendMethod.Multiply,
13 objects.BlendMode.Screen: api.BlendMethod.Screen,
14 objects.BlendMode.Overlay: api.BlendMethod.Overlay,
15 objects.BlendMode.Darken: api.BlendMethod.Darken,
16 objects.BlendMode.Lighten: api.BlendMethod.Lighten,
17 objects.BlendMode.HardLight: api.BlendMethod.HardLight,
18 objects.BlendMode.Difference: api.BlendMethod.Difference,
19 objects.BlendMode.Hue: api.BlendMethod.Hue,
20 objects.BlendMode.Saturation: api.BlendMethod.Saturation,
21 objects.BlendMode.Color: api.BlendMethod.Color,
22 objects.BlendMode.Luminosity: api.BlendMethod.Luminosity,
23 objects.BlendMode.Exclusion: api.BlendMethod.Difference,
24 objects.BlendMode.SoftLight: api.BlendMethod.Multiply,
25 objects.BlendMode.ColorDodge: api.BlendMethod.Composite,
26 objects.BlendMode.ColorBurn: api.BlendMethod.Composite,
33 @todo Add gamma option to lottie_convert.py
37 self.
canvas.version =
"1.2"
41 def _on_animation(self, animation: objects.Animation):
43 self.
canvas.name = animation.name
44 self.
canvas.width = animation.width
45 self.
canvas.height = animation.height
46 self.
canvas.xres = animation.width
47 self.
canvas.yres = animation.height
48 self.
canvas.view_box =
NVector(0, 0, animation.width, animation.height)
49 self.
canvas.fps = animation.frame_rate
50 self.
canvas.begin_time = api.FrameTime.frame(animation.in_point)
51 self.
canvas.end_time = api.FrameTime.frame(animation.out_point)
52 self.
canvas.antialias =
True
55 def _on_precomp(self, id, dom_parent, layers):
58 for layer_builder
in layers:
59 self.process_layer(layer_builder, g)
61 def _on_layer(self, layer_builder, dom_parent):
63 if not layer_builder.lottie.name:
64 layer.desc = layer_builder.lottie.__class__.__name__
66 bm = getattr(layer_builder.lottie,
"blend_mode",
None)
68 bm = objects.BlendMode.Normal
69 layer.blend_method = blend_modes[bm]
71 layer.time_drilation = getattr(layer_builder.lottie,
"stretch", 1)
or 1
73 in_point = getattr(layer_builder.lottie,
"in_point", 0)
74 layer.time_offset.value = api.FrameTime.frame(in_point)
80 g = dom_parent.add_layer(type())
83 g.active =
not lottie.hidden
84 transf = getattr(lottie,
"transform",
None)
93 def _get_scale(self, transform):
95 t = keyframe.time
if keyframe
else 0
96 scale_x, scale_y = transform.scale.get_value(t)[:2]
99 skew = transform.skew.get_value(t)
if transform.skew
else 0
100 c = math.cos(skew * math.pi / 180)
103 return NVector(scale_x, scale_y)
107 composite = group.transformation
109 if transform.position:
118 if transform.rotation:
121 if transform.opacity:
124 if transform.anchor_point:
128 composite.z_depth = 0
131 def getter(keyframe):
141 if kframes
is not None:
143 for i
in range(len(kframes)):
144 keyframe = kframes[i]
145 waypoint = wrap.add_keyframe(getter(keyframe), api.FrameTime.frame(keyframe.time))
150 waypoint.before = api.Interpolation.Constant
151 elif prev.in_value
and prev.in_value.x < 1:
152 waypoint.before = api.Interpolation.Ease
154 waypoint.before = api.Interpolation.Linear
156 waypoint.before = api.Interpolation.Linear
159 waypoint.after = api.Interpolation.Constant
160 elif keyframe.out_value
and keyframe.out_value.x > 0:
161 waypoint.after = api.Interpolation.Ease
163 waypoint.after = api.Interpolation.Linear
170 def getter(keyframe):
174 v = keyframe.start[0]
180 def _on_shape(self, shape, group, dom_parent):
182 if not hasattr(shape,
"to_bezier"):
188 layers.append(sif_shape)
192 layers.append(sif_shape)
197 def _merge_keyframes(self, props):
200 if prop
is not None and prop.animated:
201 keyframes.update({kf.time: kf
for kf
in prop.keyframes})
202 return list(sorted(keyframes.values(), key=
lambda kf: kf.time))
or None
205 if hasattr(lottie_shape,
"position"):
206 sif_shape.origin.value = lottie_shape.position.get_value()
208 sif_shape.origin.value = lottie_shape.bounding_box().center()
212 if hasattr(fill,
"colors"):
215 def getter(keyframe):
220 return self.
canvas.make_color(*v)
224 def get_op(keyframe):
226 v = fill.opacity.value
228 v = keyframe.start[0]
236 sif_shape.sharp_cusps.value = stroke.line_join == objects.LineJoin.Miter
237 round_cap = stroke.line_cap == objects.LineCap.Round
238 sif_shape.round_tip_0.value = round_cap
239 sif_shape.round_tip_1.value = round_cap
245 startbez = path.shape.get_value()
246 layer.bline.loop = startbez.closed
247 nverts = len(startbez.vertices)
248 for point
in range(nverts):
249 self.
bezier_point(path, point, layer.bline, layer.origin.value)
255 def get_point(keyframe):
257 bezier = lottie_path.shape.value
259 bezier = keyframe.start
263 vert = bezier.vertices[point_index]
264 return NVector(vert[0], vert[1]) - offset
267 composite.split.value =
True
268 composite.split_radius.value =
True
269 composite.split_angle.value =
True
271 def get_tangent(keyframe):
273 bezier = lottie_path.shape.value
275 bezier = keyframe.start
280 inp = getattr(bezier, which_point)[point_index]
281 return NVector(inp.x, inp.y) * 3 * mult
284 which_point =
"in_tangents"
288 which_point =
"out_tangents"
290 sif_parent.points.append(composite)
292 def _on_shapegroup(self, shape_group, dom_parent):
293 if shape_group.empty():
298 self.shapegroup_process_children(shape_group, layer)
300 def _modifier_inner_group(self, modifier, shapegroup, dom_parent):
302 self.shapegroup_process_child(modifier.child, shapegroup, layer)
305 def _on_shape_modifier(self, modifier, shapegroup, dom_parent):
307 if modifier.lottie.name:
308 layer.desc = modifier.lottie.name
314 def _build_repeater_defs(self, shape, name_id):
317 self.
canvas.defs.append(dup)
318 self.
canvas.register_as(dup, name_id)
320 def getter(keyframe):
322 v = shape.copies.value
324 v = keyframe.start[0]
333 def _build_repeater_transform_scale_component(self, shape, name_id, comp, scalecomposite):
335 setattr(scalecomposite,
"xy"[comp], power)
337 def getter(keyframe):
339 v = shape.transform.scale.value
350 power.power.rhs.value = 0.000001
352 def _build_repeater_transform(self, shape, inner, name_id):
353 offset_id = name_id +
"_origin"
355 self.
canvas.defs.append(origin)
356 self.
canvas.register_as(origin, offset_id)
357 inner.origin = origin
359 composite = inner.transformation
365 composite.offset.lhs.link = self.
process_vector(shape.transform.position)
369 composite.angle.link = self.
process_scalar(shape.transform.rotation)
375 def _build_repeater_amount(self, shape, inner, name_id):
377 inner.amount.lhs = self.
process_scalar(shape.transform.start_opacity, 0.01)
382 def getter(keyframe):
385 end = shape.transform.end_opacity.value
388 end = keyframe.start[0]
389 start = shape.transform.start_opacity.get_value(t)
390 n = shape.copies.get_value(t)
391 v = (start - end) / (n - 1) / 100
if n > 0
else 0
393 inner.amount.rhs.link = self.
process_vector_ext(shape.transform.end_opacity.keyframes, getter)
396 name_id =
"duplicate_%s" % next(self.
autoid)
400 inner.desc =
"Transformation for " + (dom_parent.desc
or "duplicate")
404 duplicate.index = dup
405 duplicate.desc = shape.name
410 builder.process(animation)
411 return builder.canvas