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.
canvascanvas.version =
"1.2"
38 self.
canvascanvas.gamma_r = self.
canvascanvas.gamma_g = self.
canvascanvas.gamma_b = gamma
41 def _on_animation(self, animation: objects.Animation):
43 self.
canvascanvas.name = animation.name
44 self.
canvascanvas.width = animation.width
45 self.
canvascanvas.height = animation.height
46 self.
canvascanvas.xres = animation.width
47 self.
canvascanvas.yres = animation.height
48 self.
canvascanvas.view_box =
NVector(0, 0, animation.width, animation.height)
49 self.
canvascanvas.fps = animation.frame_rate
50 self.
canvascanvas.begin_time = api.FrameTime.frame(animation.in_point)
51 self.
canvascanvas.end_time = api.FrameTime.frame(animation.out_point)
52 self.
canvascanvas.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:
110 composite.offset = self.
process_vectorprocess_vector(transform.position)
113 keyframes = self.
_merge_keyframes_merge_keyframes([transform.scale, transform.skew])
118 if transform.rotation:
119 composite.angle = self.
process_scalarprocess_scalar(transform.rotation)
121 if transform.opacity:
122 group.amount = self.
process_scalarprocess_scalar(transform.opacity, 1/100)
124 if transform.anchor_point:
125 group.origin = self.
process_vectorprocess_vector(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.
canvascanvas.make_color(*v)
222 sif_shape.color = self.
process_vector_extprocess_vector_ext(fill.color.keyframes, getter)
224 def get_op(keyframe):
226 v = fill.opacity.value
228 v = keyframe.start[0]
232 sif_shape.amount = self.
process_vector_extprocess_vector_ext(fill.opacity.keyframes, get_op)
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
240 sif_shape.width = self.
process_scalarprocess_scalar(stroke.width, 0.5)
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_pointbezier_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
266 composite.point = self.
process_vector_extprocess_vector_ext(lottie_path.shape.keyframes, get_point)
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"
285 composite.t1 = self.
process_vector_extprocess_vector_ext(lottie_path.shape.keyframes, get_tangent)
288 which_point =
"out_tangents"
289 composite.t2 = self.
process_vector_extprocess_vector_ext(lottie_path.shape.keyframes, get_tangent)
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.
canvascanvas.defs.append(dup)
318 self.
canvascanvas.register_as(dup, name_id)
320 def getter(keyframe):
322 v = shape.copies.value
324 v = keyframe.start[0]
328 setattr(dup,
"from", self.
process_vector_extprocess_vector_ext(shape.copies.keyframes, getter))
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
345 power.base = self.
process_vector_extprocess_vector_ext(shape.transform.scale.keyframes, getter)
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.
canvascanvas.defs.append(origin)
356 self.
canvascanvas.register_as(origin, offset_id)
357 inner.origin = origin
359 composite = inner.transformation
365 composite.offset.lhs.link = self.
process_vectorprocess_vector(shape.transform.position)
369 composite.angle.link = self.
process_scalarprocess_scalar(shape.transform.rotation)
375 def _build_repeater_amount(self, shape, inner, name_id):
377 inner.amount.lhs = self.
process_scalarprocess_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_extprocess_vector_ext(shape.transform.end_opacity.keyframes, getter)
396 name_id =
"duplicate_%s" % next(self.
autoidautoid)
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
Simple iterator to generate increasing integers.
Layer with no data, useful to group layers together.
An animatable property that holds a float.
def _build_repeater_defs(self, shape, name_id)
def _build_repeater_transform_scale_component(self, shape, name_id, comp, scalecomposite)
def apply_origin(self, sif_shape, lottie_shape)
def bezier_point(self, lottie_path, point_index, sif_parent, offset)
def process_vector(self, multidim)
def __init__(self, gamma=1.0)
def _merge_keyframes(self, props)
def build_path(self, type, path, dom_parent, lottie_shape)
def process_vector_ext(self, kframes, getter)
def _build_repeater_transform(self, shape, inner, name_id)
def _get_scale(self, transform)
def apply_group_fill(self, sif_shape, fill)
def _modifier_inner_group(self, modifier, shapegroup, dom_parent)
def _build_repeater_amount(self, shape, inner, name_id)
def layer_from_lottie(self, type, lottie, dom_parent)
def process_scalar(self, value, mult=None)
def set_transform(self, group, transform)
def apply_group_stroke(self, sif_shape, stroke)
def build_repeater(self, shape, inner, dom_parent)