blob: 093d7f68e4ac2e6c80bc730328f9b1b65621f289 [file] [log] [blame]
Florin Malita094ccde2017-12-30 12:27:00 -05001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "Skotty.h"
9
10#include "SkCanvas.h"
11#include "SkottyAnimator.h"
12#include "SkottyPriv.h"
13#include "SkottyProperties.h"
14#include "SkData.h"
15#include "SkMakeUnique.h"
16#include "SkPaint.h"
17#include "SkPath.h"
18#include "SkPoint.h"
19#include "SkSGColor.h"
20#include "SkSGDraw.h"
21#include "SkSGInvalidationController.h"
22#include "SkSGGroup.h"
Florin Malitae6345d92018-01-03 23:37:54 -050023#include "SkSGMerge.h"
Florin Malita094ccde2017-12-30 12:27:00 -050024#include "SkSGPath.h"
Florin Malita2e1d7e22018-01-02 10:40:00 -050025#include "SkSGRect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050026#include "SkSGTransform.h"
27#include "SkStream.h"
28#include "SkTArray.h"
29#include "SkTHash.h"
30
31#include <cmath>
Florin Malitae6345d92018-01-03 23:37:54 -050032#include <vector>
33
Florin Malita094ccde2017-12-30 12:27:00 -050034#include "stdlib.h"
35
36namespace skotty {
37
38namespace {
39
40using AssetMap = SkTHashMap<SkString, const Json::Value*>;
41
42struct AttachContext {
43 const AssetMap& fAssets;
44 SkTArray<std::unique_ptr<AnimatorBase>>& fAnimators;
45};
46
47bool LogFail(const Json::Value& json, const char* msg) {
48 const auto dump = json.toStyledString();
49 LOG("!! %s: %s", msg, dump.c_str());
50 return false;
51}
52
53// This is the workhorse for binding properties: depending on whether the property is animated,
54// it will either apply immediately or instantiate and attach a keyframe animator.
55template <typename ValueT, typename AttrT, typename NodeT, typename ApplyFuncT>
56bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<NodeT>& node,
57 ApplyFuncT&& apply) {
58 if (!jprop.isObject())
59 return false;
60
61 if (!ParseBool(jprop["a"], false)) {
62 // Static property.
63 ValueT val;
64 if (!ValueT::Parse(jprop["k"], &val)) {
65 return LogFail(jprop, "Could not parse static property");
66 }
67
68 apply(node, val.template as<AttrT>());
69 } else {
70 // Keyframe property.
71 using AnimatorT = Animator<ValueT, AttrT, NodeT>;
72 auto animator = AnimatorT::Make(jprop["k"], node, std::move(apply));
73
74 if (!animator) {
75 return LogFail(jprop, "Could not instantiate keyframe animator");
76 }
77
78 ctx->fAnimators.push_back(std::move(animator));
79 }
80
81 return true;
82}
83
84sk_sp<sksg::RenderNode> AttachTransform(const Json::Value& t, AttachContext* ctx,
85 sk_sp<sksg::RenderNode> wrapped_node) {
Florin Malita2e1d7e22018-01-02 10:40:00 -050086 if (!t.isObject() || !wrapped_node)
Florin Malita094ccde2017-12-30 12:27:00 -050087 return wrapped_node;
88
89 auto xform = sk_make_sp<CompositeTransform>(wrapped_node);
90 auto anchor_attached = AttachProperty<VectorValue, SkPoint>(t["a"], ctx, xform,
91 [](const sk_sp<CompositeTransform>& node, const SkPoint& a) {
92 node->setAnchorPoint(a);
93 });
94 auto position_attached = AttachProperty<VectorValue, SkPoint>(t["p"], ctx, xform,
95 [](const sk_sp<CompositeTransform>& node, const SkPoint& p) {
96 node->setPosition(p);
97 });
98 auto scale_attached = AttachProperty<VectorValue, SkVector>(t["s"], ctx, xform,
99 [](const sk_sp<CompositeTransform>& node, const SkVector& s) {
100 node->setScale(s);
101 });
102 auto rotation_attached = AttachProperty<ScalarValue, SkScalar>(t["r"], ctx, xform,
103 [](const sk_sp<CompositeTransform>& node, SkScalar r) {
104 node->setRotation(r);
105 });
106 auto skew_attached = AttachProperty<ScalarValue, SkScalar>(t["sk"], ctx, xform,
107 [](const sk_sp<CompositeTransform>& node, SkScalar sk) {
108 node->setSkew(sk);
109 });
110 auto skewaxis_attached = AttachProperty<ScalarValue, SkScalar>(t["sa"], ctx, xform,
111 [](const sk_sp<CompositeTransform>& node, SkScalar sa) {
112 node->setSkewAxis(sa);
113 });
114
115 if (!anchor_attached &&
116 !position_attached &&
117 !scale_attached &&
118 !rotation_attached &&
119 !skew_attached &&
120 !skewaxis_attached) {
121 LogFail(t, "Could not parse transform");
122 return wrapped_node;
123 }
124
125 return xform->node();
126}
127
128sk_sp<sksg::RenderNode> AttachShape(const Json::Value&, AttachContext* ctx);
129sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
130
131sk_sp<sksg::RenderNode> AttachShapeGroup(const Json::Value& jgroup, AttachContext* ctx) {
132 SkASSERT(jgroup.isObject());
133
134 return AttachShape(jgroup["it"], ctx);
135}
136
137sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
138 SkASSERT(jpath.isObject());
139
140 auto path_node = sksg::Path::Make();
141 auto path_attached = AttachProperty<ShapeValue, SkPath>(jpath["ks"], ctx, path_node,
142 [](const sk_sp<sksg::Path>& node, const SkPath& p) { node->setPath(p); });
143
144 if (path_attached)
145 LOG("** Attached path geometry - verbs: %d\n", path_node->getPath().countVerbs());
146
147 return path_attached ? path_node : nullptr;
148}
149
Florin Malita2e1d7e22018-01-02 10:40:00 -0500150sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
151 SkASSERT(jrect.isObject());
152
153 auto rect_node = sksg::RRect::Make();
154 auto composite = sk_make_sp<CompositeRRect>(rect_node);
155
156 auto p_attached = AttachProperty<VectorValue, SkPoint>(jrect["p"], ctx, composite,
157 [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
158 auto s_attached = AttachProperty<VectorValue, SkSize>(jrect["s"], ctx, composite,
159 [](const sk_sp<CompositeRRect>& node, const SkSize& sz) { node->setSize(sz); });
160 auto r_attached = AttachProperty<ScalarValue, SkScalar>(jrect["r"], ctx, composite,
Florin Malitafbc13f12018-01-04 10:26:35 -0500161 [](const sk_sp<CompositeRRect>& node, SkScalar radius) {
162 node->setRadius(SkSize::Make(radius, radius));
163 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500164
165 if (!p_attached && !s_attached && !r_attached) {
166 return nullptr;
167 }
168
Florin Malitafbc13f12018-01-04 10:26:35 -0500169 LOG("** Attached (r)rect geometry\n");
170
171 return rect_node;
172}
173
174sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
175 SkASSERT(jellipse.isObject());
176
177 auto rect_node = sksg::RRect::Make();
178 auto composite = sk_make_sp<CompositeRRect>(rect_node);
179
180 auto p_attached = AttachProperty<VectorValue, SkPoint>(jellipse["p"], ctx, composite,
181 [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
182 auto s_attached = AttachProperty<VectorValue, SkSize>(jellipse["s"], ctx, composite,
183 [](const sk_sp<CompositeRRect>& node, const SkSize& sz) {
184 node->setSize(sz);
185 node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
186 });
187
188 if (!p_attached && !s_attached) {
189 return nullptr;
190 }
191
192 LOG("** Attached ellipse geometry\n");
193
Florin Malita2e1d7e22018-01-02 10:40:00 -0500194 return rect_node;
195}
196
Florin Malita094ccde2017-12-30 12:27:00 -0500197sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
198 SkASSERT(obj.isObject());
199
200 auto color_node = sksg::Color::Make(SK_ColorBLACK);
201 color_node->setAntiAlias(true);
202
203 auto color_attached = AttachProperty<VectorValue, SkColor>(obj["c"], ctx, color_node,
204 [](const sk_sp<sksg::Color>& node, SkColor c) { node->setColor(c); });
205
206 return color_attached ? color_node : nullptr;
207}
208
209sk_sp<sksg::PaintNode> AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) {
210 SkASSERT(jfill.isObject());
211
212 auto color = AttachColorPaint(jfill, ctx);
213 if (color) {
214 LOG("** Attached color fill: 0x%x\n", color->getColor());
215 }
216 return color;
217}
218
219sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
220 SkASSERT(jstroke.isObject());
221
222 auto stroke_node = AttachColorPaint(jstroke, ctx);
223 if (!stroke_node)
224 return nullptr;
225
226 LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor());
227
228 stroke_node->setStyle(SkPaint::kStroke_Style);
229
230 auto width_attached = AttachProperty<ScalarValue, SkScalar>(jstroke["w"], ctx, stroke_node,
231 [](const sk_sp<sksg::Color>& node, SkScalar width) { node->setStrokeWidth(width); });
232 if (!width_attached)
233 return nullptr;
234
235 stroke_node->setStrokeMiter(ParseScalar(jstroke["ml"], 4));
236
237 static constexpr SkPaint::Join gJoins[] = {
238 SkPaint::kMiter_Join,
239 SkPaint::kRound_Join,
240 SkPaint::kBevel_Join,
241 };
242 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseInt(jstroke["lj"], 1) - 1,
243 0, SK_ARRAY_COUNT(gJoins) - 1)]);
244
245 static constexpr SkPaint::Cap gCaps[] = {
246 SkPaint::kButt_Cap,
247 SkPaint::kRound_Cap,
248 SkPaint::kSquare_Cap,
249 };
250 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseInt(jstroke["lc"], 1) - 1,
251 0, SK_ARRAY_COUNT(gCaps) - 1)]);
252
253 return stroke_node;
254}
255
Florin Malitae6345d92018-01-03 23:37:54 -0500256std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
257 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
258 std::vector<sk_sp<sksg::GeometryNode>> merged;
259
260 static constexpr sksg::Merge::Mode gModes[] = {
261 sksg::Merge::Mode::kMerge, // "mm": 1
262 sksg::Merge::Mode::kUnion, // "mm": 2
263 sksg::Merge::Mode::kDifference, // "mm": 3
264 sksg::Merge::Mode::kIntersect, // "mm": 4
265 sksg::Merge::Mode::kXOR , // "mm": 5
266 };
267
268 const auto mode = gModes[SkTPin<int>(ParseInt(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes))];
269 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
270
271 LOG("** Attached merge path effect, mode: %d\n", mode);
272
273 return merged;
274}
275
Florin Malita094ccde2017-12-30 12:27:00 -0500276using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
277static constexpr GeometryAttacherT gGeometryAttachers[] = {
278 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500279 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500280 AttachEllipseGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500281};
282
283using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
284static constexpr PaintAttacherT gPaintAttachers[] = {
285 AttachFillPaint,
286 AttachStrokePaint,
287};
288
289using GroupAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
290static constexpr GroupAttacherT gGroupAttachers[] = {
291 AttachShapeGroup,
292};
293
Florin Malitadacc02b2017-12-31 09:12:31 -0500294using TransformAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*,
295 sk_sp<sksg::RenderNode>);
296static constexpr TransformAttacherT gTransformAttachers[] = {
297 AttachTransform,
298};
299
Florin Malitae6345d92018-01-03 23:37:54 -0500300using GeometryEffectAttacherT =
301 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
302 AttachContext*,
303 std::vector<sk_sp<sksg::GeometryNode>>&&);
304static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
305 AttachMergeGeometryEffect,
306};
307
Florin Malita094ccde2017-12-30 12:27:00 -0500308enum class ShapeType {
309 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500310 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500311 kPaint,
312 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500313 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500314};
315
316struct ShapeInfo {
317 const char* fTypeString;
318 ShapeType fShapeType;
319 uint32_t fAttacherIndex; // index into respective attacher tables
320};
321
322const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
323 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500324 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500325 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint
326 { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup
327 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
328 { "rc", ShapeType::kGeometry , 1 }, // shape -> AttachRRectGeometry
329 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
330 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint
331 { "tr", ShapeType::kTransform , 0 }, // transform -> AttachTransform
Florin Malita094ccde2017-12-30 12:27:00 -0500332 };
333
334 if (!shape.isObject())
335 return nullptr;
336
337 const auto& type = shape["ty"];
338 if (!type.isString())
339 return nullptr;
340
341 const auto* info = bsearch(type.asCString(),
342 gShapeInfo,
343 SK_ARRAY_COUNT(gShapeInfo),
344 sizeof(ShapeInfo),
345 [](const void* key, const void* info) {
346 return strcmp(static_cast<const char*>(key),
347 static_cast<const ShapeInfo*>(info)->fTypeString);
348 });
349
350 return static_cast<const ShapeInfo*>(info);
351}
352
353sk_sp<sksg::RenderNode> AttachShape(const Json::Value& shapeArray, AttachContext* ctx) {
354 if (!shapeArray.isArray())
355 return nullptr;
356
Florin Malita2a8275b2018-01-02 12:52:43 -0500357 // (https://helpx.adobe.com/after-effects/using/overview-shape-layers-paths-vector.html#groups_and_render_order_for_shapes_and_shape_attributes)
358 //
359 // Render order for shapes within a shape layer
360 //
361 // The rules for rendering a shape layer are similar to the rules for rendering a composition
362 // that contains nested compositions:
363 //
364 // * Within a group, the shape at the bottom of the Timeline panel stacking order is rendered
365 // first.
366 //
367 // * All path operations within a group are performed before paint operations. This means,
368 // for example, that the stroke follows the distortions in the path made by the Wiggle Paths
369 // path operation. Path operations within a group are performed from top to bottom.
370 //
371 // * Paint operations within a group are performed from the bottom to the top in the Timeline
372 // panel stacking order. This means, for example, that a stroke is rendered on top of
373 // (in front of) a stroke that appears after it in the Timeline panel.
374 //
Florin Malitadacc02b2017-12-31 09:12:31 -0500375 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
376 sk_sp<sksg::RenderNode> xformed_group = shape_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500377
Florin Malitae6345d92018-01-03 23:37:54 -0500378 std::vector<sk_sp<sksg::GeometryNode>> geos;
379 std::vector<sk_sp<sksg::RenderNode>> draws;
Florin Malita094ccde2017-12-30 12:27:00 -0500380
381 for (const auto& s : shapeArray) {
382 const auto* info = FindShapeInfo(s);
383 if (!info) {
384 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
385 continue;
386 }
387
388 switch (info->fShapeType) {
389 case ShapeType::kGeometry: {
390 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
391 if (auto geo = gGeometryAttachers[info->fAttacherIndex](s, ctx)) {
392 geos.push_back(std::move(geo));
393 }
394 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500395 case ShapeType::kGeometryEffect: {
396 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
397 geos = gGeometryEffectAttachers[info->fAttacherIndex](s, ctx, std::move(geos));
398 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500399 case ShapeType::kPaint: {
400 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
401 if (auto paint = gPaintAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500402 for (const auto& geo : geos) {
403 draws.push_back(sksg::Draw::Make(geo, paint));
404 }
Florin Malita094ccde2017-12-30 12:27:00 -0500405 }
406 } break;
407 case ShapeType::kGroup: {
408 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGroupAttachers));
409 if (auto group = gGroupAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500410 draws.push_back(std::move(group));
Florin Malita094ccde2017-12-30 12:27:00 -0500411 }
412 } break;
Florin Malitadacc02b2017-12-31 09:12:31 -0500413 case ShapeType::kTransform: {
Florin Malita2a8275b2018-01-02 12:52:43 -0500414 // TODO: BM appears to transform the geometry, not the draw op itself.
Florin Malitadacc02b2017-12-31 09:12:31 -0500415 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gTransformAttachers));
416 xformed_group = gTransformAttachers[info->fAttacherIndex](s, ctx, xformed_group);
417 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500418 }
419 }
420
Florin Malita2a8275b2018-01-02 12:52:43 -0500421 if (draws.empty()) {
422 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500423 }
424
Florin Malitae6345d92018-01-03 23:37:54 -0500425 for (auto draw = draws.rbegin(); draw != draws.rend(); ++draw) {
426 shape_group->addChild(std::move(*draw));
Florin Malita2a8275b2018-01-02 12:52:43 -0500427 }
428
Florin Malitae6345d92018-01-03 23:37:54 -0500429 LOG("** Attached shape: %zd draws.\n", draws.size());
Florin Malitadacc02b2017-12-31 09:12:31 -0500430 return xformed_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500431}
432
433sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
434 SkASSERT(layer.isObject());
435
436 auto refId = ParseString(layer["refId"], "");
437 if (refId.isEmpty()) {
438 LOG("!! Comp layer missing refId\n");
439 return nullptr;
440 }
441
442 const auto* comp = ctx->fAssets.find(refId);
443 if (!comp) {
444 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
445 return nullptr;
446 }
447
448 // TODO: cycle detection
449 return AttachComposition(**comp, ctx);
450}
451
452sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& layer, AttachContext*) {
453 SkASSERT(layer.isObject());
454
455 LOG("?? Solid layer stub\n");
456 return nullptr;
457}
458
459sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext*) {
460 SkASSERT(layer.isObject());
461
462 LOG("?? Image layer stub\n");
463 return nullptr;
464}
465
466sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
467 SkASSERT(layer.isObject());
468
469 LOG("?? Null layer stub\n");
470 return nullptr;
471}
472
473sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx) {
474 SkASSERT(layer.isObject());
475
476 LOG("** Attaching shape layer ind: %d\n", ParseInt(layer["ind"], 0));
477
478 return AttachShape(layer["shapes"], ctx);
479}
480
481sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*) {
482 SkASSERT(layer.isObject());
483
484 LOG("?? Text layer stub\n");
485 return nullptr;
486}
487
488sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& layer, AttachContext* ctx) {
489 if (!layer.isObject())
490 return nullptr;
491
492 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
493 static constexpr LayerAttacher gLayerAttachers[] = {
494 AttachCompLayer, // 'ty': 0
495 AttachSolidLayer, // 'ty': 1
496 AttachImageLayer, // 'ty': 2
497 AttachNullLayer, // 'ty': 3
498 AttachShapeLayer, // 'ty': 4
499 AttachTextLayer, // 'ty': 5
500 };
501
502 int type = ParseInt(layer["ty"], -1);
503 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
504 return nullptr;
505 }
506
507 return AttachTransform(layer["ks"], ctx, gLayerAttachers[type](layer, ctx));
508}
509
510sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
511 if (!comp.isObject())
512 return nullptr;
513
Florin Malita2a8275b2018-01-02 12:52:43 -0500514 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
Florin Malita094ccde2017-12-30 12:27:00 -0500515
516 for (const auto& l : comp["layers"]) {
517 if (auto layer_fragment = AttachLayer(l, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500518 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -0500519 }
520 }
521
Florin Malita2a8275b2018-01-02 12:52:43 -0500522 if (layers.empty()) {
523 return nullptr;
524 }
525
526 // Layers are painted in bottom->top order.
527 auto comp_group = sksg::Group::Make();
528 for (int i = layers.count() - 1; i >= 0; --i) {
529 comp_group->addChild(std::move(layers[i]));
530 }
531
532 LOG("** Attached composition '%s': %d layers.\n",
533 ParseString(comp["id"], "").c_str(), layers.count());
534
Florin Malita094ccde2017-12-30 12:27:00 -0500535 return comp_group;
536}
537
538} // namespace
539
540std::unique_ptr<Animation> Animation::Make(SkStream* stream) {
541 if (!stream->hasLength()) {
542 // TODO: handle explicit buffering?
543 LOG("!! cannot parse streaming content\n");
544 return nullptr;
545 }
546
547 Json::Value json;
548 {
549 auto data = SkData::MakeFromStream(stream, stream->getLength());
550 if (!data) {
551 LOG("!! could not read stream\n");
552 return nullptr;
553 }
554
555 Json::Reader reader;
556
557 auto dataStart = static_cast<const char*>(data->data());
558 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
559 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
560 return nullptr;
561 }
562 }
563
564 const auto version = ParseString(json["v"], "");
565 const auto size = SkSize::Make(ParseScalar(json["w"], -1), ParseScalar(json["h"], -1));
566 const auto fps = ParseScalar(json["fr"], -1);
567
568 if (size.isEmpty() || version.isEmpty() || fps < 0) {
569 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
570 version.c_str(), size.width(), size.height(), fps);
571 return nullptr;
572 }
573
574 return std::unique_ptr<Animation>(new Animation(std::move(version), size, fps, json));
575}
576
577Animation::Animation(SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
578 : fVersion(std::move(version))
579 , fSize(size)
580 , fFrameRate(fps)
581 , fInPoint(ParseScalar(json["ip"], 0))
582 , fOutPoint(SkTMax(ParseScalar(json["op"], SK_ScalarMax), fInPoint)) {
583
584 AssetMap assets;
585 for (const auto& asset : json["assets"]) {
586 if (!asset.isObject()) {
587 continue;
588 }
589
590 assets.set(ParseString(asset["id"], ""), &asset);
591 }
592
593 AttachContext ctx = { assets, fAnimators };
594 fDom = AttachComposition(json, &ctx);
595
596 LOG("** Attached %d animators\n", fAnimators.count());
597}
598
599Animation::~Animation() = default;
600
601void Animation::render(SkCanvas* canvas) const {
602 if (!fDom)
603 return;
604
605 sksg::InvalidationController ic;
606 fDom->revalidate(&ic, SkMatrix::I());
607
608 // TODO: proper inval
609 fDom->render(canvas);
610
611 if (!fShowInval)
612 return;
613
614 SkPaint fill, stroke;
615 fill.setAntiAlias(true);
616 fill.setColor(0x40ff0000);
617 stroke.setAntiAlias(true);
618 stroke.setColor(0xffff0000);
619 stroke.setStyle(SkPaint::kStroke_Style);
620
621 for (const auto& r : ic) {
622 canvas->drawRect(r, fill);
623 canvas->drawRect(r, stroke);
624 }
625}
626
627void Animation::animationTick(SkMSec ms) {
628 // 't' in the BM model really means 'frame #'
629 auto t = static_cast<float>(ms) * fFrameRate / 1000;
630
631 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
632
633 // TODO: this can be optimized quite a bit with some sorting/state tracking.
634 for (const auto& a : fAnimators) {
635 a->tick(t);
636 }
637}
638
639} // namespace skotty