blob: e84abcd4a86e98f0056febe91dd3e4d13b6f02cc [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 Malita02a32b02018-01-04 11:27:09 -0500197sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
198 SkASSERT(jstar.isObject());
199
200 static constexpr CompositePolyStar::Type gTypes[] = {
201 CompositePolyStar::Type::kStar, // "sy": 1
202 CompositePolyStar::Type::kPoly, // "sy": 2
203 };
204
205 const auto type = ParseInt(jstar["sy"], 0) - 1;
206 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
207 LogFail(jstar, "Unknown polystar type");
208 return nullptr;
209 }
210
211 auto path_node = sksg::Path::Make();
212 auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
213
214 AttachProperty<VectorValue, SkPoint>(jstar["p"], ctx, composite,
215 [](const sk_sp<CompositePolyStar>& node, const SkPoint& p) { node->setPosition(p); });
216 AttachProperty<ScalarValue, SkScalar>(jstar["pt"], ctx, composite,
217 [](const sk_sp<CompositePolyStar>& node, SkScalar pt) { node->setPointCount(pt); });
218 AttachProperty<ScalarValue, SkScalar>(jstar["ir"], ctx, composite,
219 [](const sk_sp<CompositePolyStar>& node, SkScalar ir) { node->setInnerRadius(ir); });
220 AttachProperty<ScalarValue, SkScalar>(jstar["or"], ctx, composite,
221 [](const sk_sp<CompositePolyStar>& node, SkScalar otr) { node->setOuterRadius(otr); });
222 AttachProperty<ScalarValue, SkScalar>(jstar["is"], ctx, composite,
223 [](const sk_sp<CompositePolyStar>& node, SkScalar is) { node->setInnerRoundness(is); });
224 AttachProperty<ScalarValue, SkScalar>(jstar["os"], ctx, composite,
225 [](const sk_sp<CompositePolyStar>& node, SkScalar os) { node->setOuterRoundness(os); });
226 AttachProperty<ScalarValue, SkScalar>(jstar["r"], ctx, composite,
227 [](const sk_sp<CompositePolyStar>& node, SkScalar r) { node->setRotation(r); });
228
229 return path_node;
230}
231
Florin Malita094ccde2017-12-30 12:27:00 -0500232sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
233 SkASSERT(obj.isObject());
234
235 auto color_node = sksg::Color::Make(SK_ColorBLACK);
236 color_node->setAntiAlias(true);
237
238 auto color_attached = AttachProperty<VectorValue, SkColor>(obj["c"], ctx, color_node,
239 [](const sk_sp<sksg::Color>& node, SkColor c) { node->setColor(c); });
240
241 return color_attached ? color_node : nullptr;
242}
243
244sk_sp<sksg::PaintNode> AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) {
245 SkASSERT(jfill.isObject());
246
247 auto color = AttachColorPaint(jfill, ctx);
248 if (color) {
249 LOG("** Attached color fill: 0x%x\n", color->getColor());
250 }
251 return color;
252}
253
254sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
255 SkASSERT(jstroke.isObject());
256
257 auto stroke_node = AttachColorPaint(jstroke, ctx);
258 if (!stroke_node)
259 return nullptr;
260
261 LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor());
262
263 stroke_node->setStyle(SkPaint::kStroke_Style);
264
265 auto width_attached = AttachProperty<ScalarValue, SkScalar>(jstroke["w"], ctx, stroke_node,
266 [](const sk_sp<sksg::Color>& node, SkScalar width) { node->setStrokeWidth(width); });
267 if (!width_attached)
268 return nullptr;
269
270 stroke_node->setStrokeMiter(ParseScalar(jstroke["ml"], 4));
271
272 static constexpr SkPaint::Join gJoins[] = {
273 SkPaint::kMiter_Join,
274 SkPaint::kRound_Join,
275 SkPaint::kBevel_Join,
276 };
277 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseInt(jstroke["lj"], 1) - 1,
278 0, SK_ARRAY_COUNT(gJoins) - 1)]);
279
280 static constexpr SkPaint::Cap gCaps[] = {
281 SkPaint::kButt_Cap,
282 SkPaint::kRound_Cap,
283 SkPaint::kSquare_Cap,
284 };
285 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseInt(jstroke["lc"], 1) - 1,
286 0, SK_ARRAY_COUNT(gCaps) - 1)]);
287
288 return stroke_node;
289}
290
Florin Malitae6345d92018-01-03 23:37:54 -0500291std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
292 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
293 std::vector<sk_sp<sksg::GeometryNode>> merged;
294
295 static constexpr sksg::Merge::Mode gModes[] = {
296 sksg::Merge::Mode::kMerge, // "mm": 1
297 sksg::Merge::Mode::kUnion, // "mm": 2
298 sksg::Merge::Mode::kDifference, // "mm": 3
299 sksg::Merge::Mode::kIntersect, // "mm": 4
300 sksg::Merge::Mode::kXOR , // "mm": 5
301 };
302
303 const auto mode = gModes[SkTPin<int>(ParseInt(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes))];
304 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
305
306 LOG("** Attached merge path effect, mode: %d\n", mode);
307
308 return merged;
309}
310
Florin Malita094ccde2017-12-30 12:27:00 -0500311using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
312static constexpr GeometryAttacherT gGeometryAttachers[] = {
313 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500314 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500315 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500316 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500317};
318
319using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
320static constexpr PaintAttacherT gPaintAttachers[] = {
321 AttachFillPaint,
322 AttachStrokePaint,
323};
324
325using GroupAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
326static constexpr GroupAttacherT gGroupAttachers[] = {
327 AttachShapeGroup,
328};
329
Florin Malitadacc02b2017-12-31 09:12:31 -0500330using TransformAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*,
331 sk_sp<sksg::RenderNode>);
332static constexpr TransformAttacherT gTransformAttachers[] = {
333 AttachTransform,
334};
335
Florin Malitae6345d92018-01-03 23:37:54 -0500336using GeometryEffectAttacherT =
337 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
338 AttachContext*,
339 std::vector<sk_sp<sksg::GeometryNode>>&&);
340static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
341 AttachMergeGeometryEffect,
342};
343
Florin Malita094ccde2017-12-30 12:27:00 -0500344enum class ShapeType {
345 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500346 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500347 kPaint,
348 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500349 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500350};
351
352struct ShapeInfo {
353 const char* fTypeString;
354 ShapeType fShapeType;
355 uint32_t fAttacherIndex; // index into respective attacher tables
356};
357
358const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
359 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500360 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500361 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint
362 { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup
363 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500364 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500365 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500366 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500367 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint
368 { "tr", ShapeType::kTransform , 0 }, // transform -> AttachTransform
Florin Malita094ccde2017-12-30 12:27:00 -0500369 };
370
371 if (!shape.isObject())
372 return nullptr;
373
374 const auto& type = shape["ty"];
375 if (!type.isString())
376 return nullptr;
377
378 const auto* info = bsearch(type.asCString(),
379 gShapeInfo,
380 SK_ARRAY_COUNT(gShapeInfo),
381 sizeof(ShapeInfo),
382 [](const void* key, const void* info) {
383 return strcmp(static_cast<const char*>(key),
384 static_cast<const ShapeInfo*>(info)->fTypeString);
385 });
386
387 return static_cast<const ShapeInfo*>(info);
388}
389
390sk_sp<sksg::RenderNode> AttachShape(const Json::Value& shapeArray, AttachContext* ctx) {
391 if (!shapeArray.isArray())
392 return nullptr;
393
Florin Malita2a8275b2018-01-02 12:52:43 -0500394 // (https://helpx.adobe.com/after-effects/using/overview-shape-layers-paths-vector.html#groups_and_render_order_for_shapes_and_shape_attributes)
395 //
396 // Render order for shapes within a shape layer
397 //
398 // The rules for rendering a shape layer are similar to the rules for rendering a composition
399 // that contains nested compositions:
400 //
401 // * Within a group, the shape at the bottom of the Timeline panel stacking order is rendered
402 // first.
403 //
404 // * All path operations within a group are performed before paint operations. This means,
405 // for example, that the stroke follows the distortions in the path made by the Wiggle Paths
406 // path operation. Path operations within a group are performed from top to bottom.
407 //
408 // * Paint operations within a group are performed from the bottom to the top in the Timeline
409 // panel stacking order. This means, for example, that a stroke is rendered on top of
410 // (in front of) a stroke that appears after it in the Timeline panel.
411 //
Florin Malitadacc02b2017-12-31 09:12:31 -0500412 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
413 sk_sp<sksg::RenderNode> xformed_group = shape_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500414
Florin Malitae6345d92018-01-03 23:37:54 -0500415 std::vector<sk_sp<sksg::GeometryNode>> geos;
416 std::vector<sk_sp<sksg::RenderNode>> draws;
Florin Malita094ccde2017-12-30 12:27:00 -0500417
418 for (const auto& s : shapeArray) {
419 const auto* info = FindShapeInfo(s);
420 if (!info) {
421 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
422 continue;
423 }
424
425 switch (info->fShapeType) {
426 case ShapeType::kGeometry: {
427 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
428 if (auto geo = gGeometryAttachers[info->fAttacherIndex](s, ctx)) {
429 geos.push_back(std::move(geo));
430 }
431 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500432 case ShapeType::kGeometryEffect: {
433 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
434 geos = gGeometryEffectAttachers[info->fAttacherIndex](s, ctx, std::move(geos));
435 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500436 case ShapeType::kPaint: {
437 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
438 if (auto paint = gPaintAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500439 for (const auto& geo : geos) {
440 draws.push_back(sksg::Draw::Make(geo, paint));
441 }
Florin Malita094ccde2017-12-30 12:27:00 -0500442 }
443 } break;
444 case ShapeType::kGroup: {
445 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGroupAttachers));
446 if (auto group = gGroupAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500447 draws.push_back(std::move(group));
Florin Malita094ccde2017-12-30 12:27:00 -0500448 }
449 } break;
Florin Malitadacc02b2017-12-31 09:12:31 -0500450 case ShapeType::kTransform: {
Florin Malita2a8275b2018-01-02 12:52:43 -0500451 // TODO: BM appears to transform the geometry, not the draw op itself.
Florin Malitadacc02b2017-12-31 09:12:31 -0500452 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gTransformAttachers));
453 xformed_group = gTransformAttachers[info->fAttacherIndex](s, ctx, xformed_group);
454 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500455 }
456 }
457
Florin Malita2a8275b2018-01-02 12:52:43 -0500458 if (draws.empty()) {
459 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500460 }
461
Florin Malitae6345d92018-01-03 23:37:54 -0500462 for (auto draw = draws.rbegin(); draw != draws.rend(); ++draw) {
463 shape_group->addChild(std::move(*draw));
Florin Malita2a8275b2018-01-02 12:52:43 -0500464 }
465
Florin Malitae6345d92018-01-03 23:37:54 -0500466 LOG("** Attached shape: %zd draws.\n", draws.size());
Florin Malitadacc02b2017-12-31 09:12:31 -0500467 return xformed_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500468}
469
470sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
471 SkASSERT(layer.isObject());
472
473 auto refId = ParseString(layer["refId"], "");
474 if (refId.isEmpty()) {
475 LOG("!! Comp layer missing refId\n");
476 return nullptr;
477 }
478
479 const auto* comp = ctx->fAssets.find(refId);
480 if (!comp) {
481 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
482 return nullptr;
483 }
484
485 // TODO: cycle detection
486 return AttachComposition(**comp, ctx);
487}
488
489sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& layer, AttachContext*) {
490 SkASSERT(layer.isObject());
491
492 LOG("?? Solid layer stub\n");
493 return nullptr;
494}
495
496sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext*) {
497 SkASSERT(layer.isObject());
498
499 LOG("?? Image layer stub\n");
500 return nullptr;
501}
502
503sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
504 SkASSERT(layer.isObject());
505
506 LOG("?? Null layer stub\n");
507 return nullptr;
508}
509
510sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx) {
511 SkASSERT(layer.isObject());
512
513 LOG("** Attaching shape layer ind: %d\n", ParseInt(layer["ind"], 0));
514
515 return AttachShape(layer["shapes"], ctx);
516}
517
518sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*) {
519 SkASSERT(layer.isObject());
520
521 LOG("?? Text layer stub\n");
522 return nullptr;
523}
524
525sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& layer, AttachContext* ctx) {
526 if (!layer.isObject())
527 return nullptr;
528
529 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
530 static constexpr LayerAttacher gLayerAttachers[] = {
531 AttachCompLayer, // 'ty': 0
532 AttachSolidLayer, // 'ty': 1
533 AttachImageLayer, // 'ty': 2
534 AttachNullLayer, // 'ty': 3
535 AttachShapeLayer, // 'ty': 4
536 AttachTextLayer, // 'ty': 5
537 };
538
539 int type = ParseInt(layer["ty"], -1);
540 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
541 return nullptr;
542 }
543
544 return AttachTransform(layer["ks"], ctx, gLayerAttachers[type](layer, ctx));
545}
546
547sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
548 if (!comp.isObject())
549 return nullptr;
550
Florin Malita2a8275b2018-01-02 12:52:43 -0500551 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
Florin Malita094ccde2017-12-30 12:27:00 -0500552
553 for (const auto& l : comp["layers"]) {
554 if (auto layer_fragment = AttachLayer(l, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500555 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -0500556 }
557 }
558
Florin Malita2a8275b2018-01-02 12:52:43 -0500559 if (layers.empty()) {
560 return nullptr;
561 }
562
563 // Layers are painted in bottom->top order.
564 auto comp_group = sksg::Group::Make();
565 for (int i = layers.count() - 1; i >= 0; --i) {
566 comp_group->addChild(std::move(layers[i]));
567 }
568
569 LOG("** Attached composition '%s': %d layers.\n",
570 ParseString(comp["id"], "").c_str(), layers.count());
571
Florin Malita094ccde2017-12-30 12:27:00 -0500572 return comp_group;
573}
574
575} // namespace
576
577std::unique_ptr<Animation> Animation::Make(SkStream* stream) {
578 if (!stream->hasLength()) {
579 // TODO: handle explicit buffering?
580 LOG("!! cannot parse streaming content\n");
581 return nullptr;
582 }
583
584 Json::Value json;
585 {
586 auto data = SkData::MakeFromStream(stream, stream->getLength());
587 if (!data) {
588 LOG("!! could not read stream\n");
589 return nullptr;
590 }
591
592 Json::Reader reader;
593
594 auto dataStart = static_cast<const char*>(data->data());
595 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
596 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
597 return nullptr;
598 }
599 }
600
601 const auto version = ParseString(json["v"], "");
602 const auto size = SkSize::Make(ParseScalar(json["w"], -1), ParseScalar(json["h"], -1));
603 const auto fps = ParseScalar(json["fr"], -1);
604
605 if (size.isEmpty() || version.isEmpty() || fps < 0) {
606 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
607 version.c_str(), size.width(), size.height(), fps);
608 return nullptr;
609 }
610
611 return std::unique_ptr<Animation>(new Animation(std::move(version), size, fps, json));
612}
613
614Animation::Animation(SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
615 : fVersion(std::move(version))
616 , fSize(size)
617 , fFrameRate(fps)
618 , fInPoint(ParseScalar(json["ip"], 0))
619 , fOutPoint(SkTMax(ParseScalar(json["op"], SK_ScalarMax), fInPoint)) {
620
621 AssetMap assets;
622 for (const auto& asset : json["assets"]) {
623 if (!asset.isObject()) {
624 continue;
625 }
626
627 assets.set(ParseString(asset["id"], ""), &asset);
628 }
629
630 AttachContext ctx = { assets, fAnimators };
631 fDom = AttachComposition(json, &ctx);
632
633 LOG("** Attached %d animators\n", fAnimators.count());
634}
635
636Animation::~Animation() = default;
637
638void Animation::render(SkCanvas* canvas) const {
639 if (!fDom)
640 return;
641
642 sksg::InvalidationController ic;
643 fDom->revalidate(&ic, SkMatrix::I());
644
645 // TODO: proper inval
646 fDom->render(canvas);
647
648 if (!fShowInval)
649 return;
650
651 SkPaint fill, stroke;
652 fill.setAntiAlias(true);
653 fill.setColor(0x40ff0000);
654 stroke.setAntiAlias(true);
655 stroke.setColor(0xffff0000);
656 stroke.setStyle(SkPaint::kStroke_Style);
657
658 for (const auto& r : ic) {
659 canvas->drawRect(r, fill);
660 canvas->drawRect(r, stroke);
661 }
662}
663
664void Animation::animationTick(SkMSec ms) {
665 // 't' in the BM model really means 'frame #'
666 auto t = static_cast<float>(ms) * fFrameRate / 1000;
667
668 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
669
670 // TODO: this can be optimized quite a bit with some sorting/state tracking.
671 for (const auto& a : fAnimators) {
672 a->tick(t);
673 }
674}
675
676} // namespace skotty