blob: 3635c2c83b3f1f2e06d48d1f47409500337b75fa [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 Malita18eafd92018-01-04 21:11:55 -050032#include <unordered_map>
Florin Malitae6345d92018-01-03 23:37:54 -050033#include <vector>
34
Florin Malita094ccde2017-12-30 12:27:00 -050035#include "stdlib.h"
36
37namespace skotty {
38
39namespace {
40
41using AssetMap = SkTHashMap<SkString, const Json::Value*>;
42
43struct AttachContext {
44 const AssetMap& fAssets;
45 SkTArray<std::unique_ptr<AnimatorBase>>& fAnimators;
46};
47
48bool LogFail(const Json::Value& json, const char* msg) {
49 const auto dump = json.toStyledString();
50 LOG("!! %s: %s", msg, dump.c_str());
51 return false;
52}
53
54// This is the workhorse for binding properties: depending on whether the property is animated,
55// it will either apply immediately or instantiate and attach a keyframe animator.
56template <typename ValueT, typename AttrT, typename NodeT, typename ApplyFuncT>
57bool AttachProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<NodeT>& node,
58 ApplyFuncT&& apply) {
59 if (!jprop.isObject())
60 return false;
61
62 if (!ParseBool(jprop["a"], false)) {
63 // Static property.
64 ValueT val;
65 if (!ValueT::Parse(jprop["k"], &val)) {
66 return LogFail(jprop, "Could not parse static property");
67 }
68
69 apply(node, val.template as<AttrT>());
70 } else {
71 // Keyframe property.
72 using AnimatorT = Animator<ValueT, AttrT, NodeT>;
73 auto animator = AnimatorT::Make(jprop["k"], node, std::move(apply));
74
75 if (!animator) {
76 return LogFail(jprop, "Could not instantiate keyframe animator");
77 }
78
79 ctx->fAnimators.push_back(std::move(animator));
80 }
81
82 return true;
83}
84
Florin Malita18eafd92018-01-04 21:11:55 -050085sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
86 sk_sp<sksg::Matrix> parentMatrix) {
87 if (!t.isObject())
88 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -050089
Florin Malita18eafd92018-01-04 21:11:55 -050090 auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
91 auto composite = sk_make_sp<CompositeTransform>(matrix);
92 auto anchor_attached = AttachProperty<VectorValue, SkPoint>(t["a"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -050093 [](const sk_sp<CompositeTransform>& node, const SkPoint& a) {
94 node->setAnchorPoint(a);
95 });
Florin Malita18eafd92018-01-04 21:11:55 -050096 auto position_attached = AttachProperty<VectorValue, SkPoint>(t["p"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -050097 [](const sk_sp<CompositeTransform>& node, const SkPoint& p) {
98 node->setPosition(p);
99 });
Florin Malita18eafd92018-01-04 21:11:55 -0500100 auto scale_attached = AttachProperty<VectorValue, SkVector>(t["s"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -0500101 [](const sk_sp<CompositeTransform>& node, const SkVector& s) {
102 node->setScale(s);
103 });
Florin Malita18eafd92018-01-04 21:11:55 -0500104 auto rotation_attached = AttachProperty<ScalarValue, SkScalar>(t["r"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -0500105 [](const sk_sp<CompositeTransform>& node, SkScalar r) {
106 node->setRotation(r);
107 });
Florin Malita18eafd92018-01-04 21:11:55 -0500108 auto skew_attached = AttachProperty<ScalarValue, SkScalar>(t["sk"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -0500109 [](const sk_sp<CompositeTransform>& node, SkScalar sk) {
110 node->setSkew(sk);
111 });
Florin Malita18eafd92018-01-04 21:11:55 -0500112 auto skewaxis_attached = AttachProperty<ScalarValue, SkScalar>(t["sa"], ctx, composite,
Florin Malita094ccde2017-12-30 12:27:00 -0500113 [](const sk_sp<CompositeTransform>& node, SkScalar sa) {
114 node->setSkewAxis(sa);
115 });
116
117 if (!anchor_attached &&
118 !position_attached &&
119 !scale_attached &&
120 !rotation_attached &&
121 !skew_attached &&
122 !skewaxis_attached) {
123 LogFail(t, "Could not parse transform");
Florin Malita18eafd92018-01-04 21:11:55 -0500124 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500125 }
126
Florin Malita18eafd92018-01-04 21:11:55 -0500127 return matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500128}
129
130sk_sp<sksg::RenderNode> AttachShape(const Json::Value&, AttachContext* ctx);
131sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
132
133sk_sp<sksg::RenderNode> AttachShapeGroup(const Json::Value& jgroup, AttachContext* ctx) {
134 SkASSERT(jgroup.isObject());
135
136 return AttachShape(jgroup["it"], ctx);
137}
138
139sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
140 SkASSERT(jpath.isObject());
141
142 auto path_node = sksg::Path::Make();
143 auto path_attached = AttachProperty<ShapeValue, SkPath>(jpath["ks"], ctx, path_node,
144 [](const sk_sp<sksg::Path>& node, const SkPath& p) { node->setPath(p); });
145
146 if (path_attached)
147 LOG("** Attached path geometry - verbs: %d\n", path_node->getPath().countVerbs());
148
149 return path_attached ? path_node : nullptr;
150}
151
Florin Malita2e1d7e22018-01-02 10:40:00 -0500152sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
153 SkASSERT(jrect.isObject());
154
155 auto rect_node = sksg::RRect::Make();
156 auto composite = sk_make_sp<CompositeRRect>(rect_node);
157
158 auto p_attached = AttachProperty<VectorValue, SkPoint>(jrect["p"], ctx, composite,
159 [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
160 auto s_attached = AttachProperty<VectorValue, SkSize>(jrect["s"], ctx, composite,
161 [](const sk_sp<CompositeRRect>& node, const SkSize& sz) { node->setSize(sz); });
162 auto r_attached = AttachProperty<ScalarValue, SkScalar>(jrect["r"], ctx, composite,
Florin Malitafbc13f12018-01-04 10:26:35 -0500163 [](const sk_sp<CompositeRRect>& node, SkScalar radius) {
164 node->setRadius(SkSize::Make(radius, radius));
165 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500166
167 if (!p_attached && !s_attached && !r_attached) {
168 return nullptr;
169 }
170
Florin Malitafbc13f12018-01-04 10:26:35 -0500171 LOG("** Attached (r)rect geometry\n");
172
173 return rect_node;
174}
175
176sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
177 SkASSERT(jellipse.isObject());
178
179 auto rect_node = sksg::RRect::Make();
180 auto composite = sk_make_sp<CompositeRRect>(rect_node);
181
182 auto p_attached = AttachProperty<VectorValue, SkPoint>(jellipse["p"], ctx, composite,
183 [](const sk_sp<CompositeRRect>& node, const SkPoint& pos) { node->setPosition(pos); });
184 auto s_attached = AttachProperty<VectorValue, SkSize>(jellipse["s"], ctx, composite,
185 [](const sk_sp<CompositeRRect>& node, const SkSize& sz) {
186 node->setSize(sz);
187 node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
188 });
189
190 if (!p_attached && !s_attached) {
191 return nullptr;
192 }
193
194 LOG("** Attached ellipse geometry\n");
195
Florin Malita2e1d7e22018-01-02 10:40:00 -0500196 return rect_node;
197}
198
Florin Malita02a32b02018-01-04 11:27:09 -0500199sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
200 SkASSERT(jstar.isObject());
201
202 static constexpr CompositePolyStar::Type gTypes[] = {
203 CompositePolyStar::Type::kStar, // "sy": 1
204 CompositePolyStar::Type::kPoly, // "sy": 2
205 };
206
207 const auto type = ParseInt(jstar["sy"], 0) - 1;
208 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
209 LogFail(jstar, "Unknown polystar type");
210 return nullptr;
211 }
212
213 auto path_node = sksg::Path::Make();
214 auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
215
216 AttachProperty<VectorValue, SkPoint>(jstar["p"], ctx, composite,
217 [](const sk_sp<CompositePolyStar>& node, const SkPoint& p) { node->setPosition(p); });
218 AttachProperty<ScalarValue, SkScalar>(jstar["pt"], ctx, composite,
219 [](const sk_sp<CompositePolyStar>& node, SkScalar pt) { node->setPointCount(pt); });
220 AttachProperty<ScalarValue, SkScalar>(jstar["ir"], ctx, composite,
221 [](const sk_sp<CompositePolyStar>& node, SkScalar ir) { node->setInnerRadius(ir); });
222 AttachProperty<ScalarValue, SkScalar>(jstar["or"], ctx, composite,
223 [](const sk_sp<CompositePolyStar>& node, SkScalar otr) { node->setOuterRadius(otr); });
224 AttachProperty<ScalarValue, SkScalar>(jstar["is"], ctx, composite,
225 [](const sk_sp<CompositePolyStar>& node, SkScalar is) { node->setInnerRoundness(is); });
226 AttachProperty<ScalarValue, SkScalar>(jstar["os"], ctx, composite,
227 [](const sk_sp<CompositePolyStar>& node, SkScalar os) { node->setOuterRoundness(os); });
228 AttachProperty<ScalarValue, SkScalar>(jstar["r"], ctx, composite,
229 [](const sk_sp<CompositePolyStar>& node, SkScalar r) { node->setRotation(r); });
230
231 return path_node;
232}
233
Florin Malita094ccde2017-12-30 12:27:00 -0500234sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
235 SkASSERT(obj.isObject());
236
237 auto color_node = sksg::Color::Make(SK_ColorBLACK);
238 color_node->setAntiAlias(true);
239
240 auto color_attached = AttachProperty<VectorValue, SkColor>(obj["c"], ctx, color_node,
241 [](const sk_sp<sksg::Color>& node, SkColor c) { node->setColor(c); });
242
243 return color_attached ? color_node : nullptr;
244}
245
246sk_sp<sksg::PaintNode> AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) {
247 SkASSERT(jfill.isObject());
248
249 auto color = AttachColorPaint(jfill, ctx);
250 if (color) {
251 LOG("** Attached color fill: 0x%x\n", color->getColor());
252 }
253 return color;
254}
255
256sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
257 SkASSERT(jstroke.isObject());
258
259 auto stroke_node = AttachColorPaint(jstroke, ctx);
260 if (!stroke_node)
261 return nullptr;
262
263 LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor());
264
265 stroke_node->setStyle(SkPaint::kStroke_Style);
266
267 auto width_attached = AttachProperty<ScalarValue, SkScalar>(jstroke["w"], ctx, stroke_node,
268 [](const sk_sp<sksg::Color>& node, SkScalar width) { node->setStrokeWidth(width); });
269 if (!width_attached)
270 return nullptr;
271
272 stroke_node->setStrokeMiter(ParseScalar(jstroke["ml"], 4));
273
274 static constexpr SkPaint::Join gJoins[] = {
275 SkPaint::kMiter_Join,
276 SkPaint::kRound_Join,
277 SkPaint::kBevel_Join,
278 };
279 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseInt(jstroke["lj"], 1) - 1,
280 0, SK_ARRAY_COUNT(gJoins) - 1)]);
281
282 static constexpr SkPaint::Cap gCaps[] = {
283 SkPaint::kButt_Cap,
284 SkPaint::kRound_Cap,
285 SkPaint::kSquare_Cap,
286 };
287 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseInt(jstroke["lc"], 1) - 1,
288 0, SK_ARRAY_COUNT(gCaps) - 1)]);
289
290 return stroke_node;
291}
292
Florin Malitae6345d92018-01-03 23:37:54 -0500293std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
294 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
295 std::vector<sk_sp<sksg::GeometryNode>> merged;
296
297 static constexpr sksg::Merge::Mode gModes[] = {
298 sksg::Merge::Mode::kMerge, // "mm": 1
299 sksg::Merge::Mode::kUnion, // "mm": 2
300 sksg::Merge::Mode::kDifference, // "mm": 3
301 sksg::Merge::Mode::kIntersect, // "mm": 4
302 sksg::Merge::Mode::kXOR , // "mm": 5
303 };
304
305 const auto mode = gModes[SkTPin<int>(ParseInt(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes))];
306 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
307
308 LOG("** Attached merge path effect, mode: %d\n", mode);
309
310 return merged;
311}
312
Florin Malita094ccde2017-12-30 12:27:00 -0500313using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
314static constexpr GeometryAttacherT gGeometryAttachers[] = {
315 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500316 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500317 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500318 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500319};
320
321using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
322static constexpr PaintAttacherT gPaintAttachers[] = {
323 AttachFillPaint,
324 AttachStrokePaint,
325};
326
327using GroupAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
328static constexpr GroupAttacherT gGroupAttachers[] = {
329 AttachShapeGroup,
330};
331
Florin Malitae6345d92018-01-03 23:37:54 -0500332using GeometryEffectAttacherT =
333 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
334 AttachContext*,
335 std::vector<sk_sp<sksg::GeometryNode>>&&);
336static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
337 AttachMergeGeometryEffect,
338};
339
Florin Malita094ccde2017-12-30 12:27:00 -0500340enum class ShapeType {
341 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500342 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500343 kPaint,
344 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500345 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500346};
347
348struct ShapeInfo {
349 const char* fTypeString;
350 ShapeType fShapeType;
351 uint32_t fAttacherIndex; // index into respective attacher tables
352};
353
354const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
355 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500356 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500357 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint
358 { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup
359 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500360 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500361 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500362 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500363 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint
Florin Malita18eafd92018-01-04 21:11:55 -0500364 { "tr", ShapeType::kTransform , 0 }, // transform -> In-place handler
Florin Malita094ccde2017-12-30 12:27:00 -0500365 };
366
367 if (!shape.isObject())
368 return nullptr;
369
370 const auto& type = shape["ty"];
371 if (!type.isString())
372 return nullptr;
373
374 const auto* info = bsearch(type.asCString(),
375 gShapeInfo,
376 SK_ARRAY_COUNT(gShapeInfo),
377 sizeof(ShapeInfo),
378 [](const void* key, const void* info) {
379 return strcmp(static_cast<const char*>(key),
380 static_cast<const ShapeInfo*>(info)->fTypeString);
381 });
382
383 return static_cast<const ShapeInfo*>(info);
384}
385
386sk_sp<sksg::RenderNode> AttachShape(const Json::Value& shapeArray, AttachContext* ctx) {
387 if (!shapeArray.isArray())
388 return nullptr;
389
Florin Malita2a8275b2018-01-02 12:52:43 -0500390 // (https://helpx.adobe.com/after-effects/using/overview-shape-layers-paths-vector.html#groups_and_render_order_for_shapes_and_shape_attributes)
391 //
392 // Render order for shapes within a shape layer
393 //
394 // The rules for rendering a shape layer are similar to the rules for rendering a composition
395 // that contains nested compositions:
396 //
397 // * Within a group, the shape at the bottom of the Timeline panel stacking order is rendered
398 // first.
399 //
400 // * All path operations within a group are performed before paint operations. This means,
401 // for example, that the stroke follows the distortions in the path made by the Wiggle Paths
402 // path operation. Path operations within a group are performed from top to bottom.
403 //
404 // * Paint operations within a group are performed from the bottom to the top in the Timeline
405 // panel stacking order. This means, for example, that a stroke is rendered on top of
406 // (in front of) a stroke that appears after it in the Timeline panel.
407 //
Florin Malitadacc02b2017-12-31 09:12:31 -0500408 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
409 sk_sp<sksg::RenderNode> xformed_group = shape_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500410
Florin Malitae6345d92018-01-03 23:37:54 -0500411 std::vector<sk_sp<sksg::GeometryNode>> geos;
412 std::vector<sk_sp<sksg::RenderNode>> draws;
Florin Malita094ccde2017-12-30 12:27:00 -0500413
414 for (const auto& s : shapeArray) {
415 const auto* info = FindShapeInfo(s);
416 if (!info) {
417 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
418 continue;
419 }
420
421 switch (info->fShapeType) {
422 case ShapeType::kGeometry: {
423 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
424 if (auto geo = gGeometryAttachers[info->fAttacherIndex](s, ctx)) {
425 geos.push_back(std::move(geo));
426 }
427 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500428 case ShapeType::kGeometryEffect: {
429 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
430 geos = gGeometryEffectAttachers[info->fAttacherIndex](s, ctx, std::move(geos));
431 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500432 case ShapeType::kPaint: {
433 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
434 if (auto paint = gPaintAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500435 for (const auto& geo : geos) {
436 draws.push_back(sksg::Draw::Make(geo, paint));
437 }
Florin Malita094ccde2017-12-30 12:27:00 -0500438 }
439 } break;
440 case ShapeType::kGroup: {
441 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGroupAttachers));
442 if (auto group = gGroupAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500443 draws.push_back(std::move(group));
Florin Malita094ccde2017-12-30 12:27:00 -0500444 }
445 } break;
Florin Malitadacc02b2017-12-31 09:12:31 -0500446 case ShapeType::kTransform: {
Florin Malita2a8275b2018-01-02 12:52:43 -0500447 // TODO: BM appears to transform the geometry, not the draw op itself.
Florin Malita18eafd92018-01-04 21:11:55 -0500448 if (auto matrix = AttachMatrix(s, ctx, nullptr)) {
449 xformed_group = sksg::Transform::Make(std::move(xformed_group),
450 std::move(matrix));
451 }
Florin Malitadacc02b2017-12-31 09:12:31 -0500452 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500453 }
454 }
455
Florin Malita2a8275b2018-01-02 12:52:43 -0500456 if (draws.empty()) {
457 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500458 }
459
Florin Malitae6345d92018-01-03 23:37:54 -0500460 for (auto draw = draws.rbegin(); draw != draws.rend(); ++draw) {
461 shape_group->addChild(std::move(*draw));
Florin Malita2a8275b2018-01-02 12:52:43 -0500462 }
463
Florin Malitae6345d92018-01-03 23:37:54 -0500464 LOG("** Attached shape: %zd draws.\n", draws.size());
Florin Malitadacc02b2017-12-31 09:12:31 -0500465 return xformed_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500466}
467
468sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
469 SkASSERT(layer.isObject());
470
471 auto refId = ParseString(layer["refId"], "");
472 if (refId.isEmpty()) {
473 LOG("!! Comp layer missing refId\n");
474 return nullptr;
475 }
476
477 const auto* comp = ctx->fAssets.find(refId);
478 if (!comp) {
479 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
480 return nullptr;
481 }
482
483 // TODO: cycle detection
484 return AttachComposition(**comp, ctx);
485}
486
487sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& layer, AttachContext*) {
488 SkASSERT(layer.isObject());
489
490 LOG("?? Solid layer stub\n");
491 return nullptr;
492}
493
494sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext*) {
495 SkASSERT(layer.isObject());
496
497 LOG("?? Image layer stub\n");
498 return nullptr;
499}
500
501sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
502 SkASSERT(layer.isObject());
503
Florin Malita18eafd92018-01-04 21:11:55 -0500504 // Null layers are used solely to drive dependent transforms,
505 // but we use free-floating sksg::Matrices for that purpose.
Florin Malita094ccde2017-12-30 12:27:00 -0500506 return nullptr;
507}
508
509sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx) {
510 SkASSERT(layer.isObject());
511
512 LOG("** Attaching shape layer ind: %d\n", ParseInt(layer["ind"], 0));
513
514 return AttachShape(layer["shapes"], ctx);
515}
516
517sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*) {
518 SkASSERT(layer.isObject());
519
520 LOG("?? Text layer stub\n");
521 return nullptr;
522}
523
Florin Malita18eafd92018-01-04 21:11:55 -0500524struct AttachLayerContext {
525 AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
526 : fLayerList(jlayers), fCtx(ctx) {}
527
528 const Json::Value& fLayerList;
529 AttachContext* fCtx;
530 std::unordered_map<const Json::Value*, sk_sp<sksg::Matrix>> fLayerMatrixCache;
531 std::unordered_map<int, const Json::Value*> fLayerIndexCache;
532
533 const Json::Value* findLayer(int index) {
534 SkASSERT(fLayerList.isArray());
535
536 if (index < 0) {
537 return nullptr;
538 }
539
540 const auto cached = fLayerIndexCache.find(index);
541 if (cached != fLayerIndexCache.end()) {
542 return cached->second;
543 }
544
545 for (const auto& l : fLayerList) {
546 if (!l.isObject()) {
547 continue;
548 }
549
550 if (ParseInt(l["ind"], -1) == index) {
551 fLayerIndexCache.insert(std::make_pair(index, &l));
552 return &l;
553 }
554 }
555
556 return nullptr;
557 }
558
559 sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
560 SkASSERT(jlayer.isObject());
561
562 const auto cached = fLayerMatrixCache.find(&jlayer);
563 if (cached != fLayerMatrixCache.end()) {
564 return cached->second;
565 }
566
567 const auto* parentLayer = this->findLayer(ParseInt(jlayer["parent"], -1));
568
569 // TODO: cycle detection?
570 auto parentMatrix = (parentLayer && parentLayer != &jlayer)
571 ? this->AttachLayerMatrix(*parentLayer) : nullptr;
572
573 auto layerMatrix = AttachMatrix(jlayer["ks"], fCtx, std::move(parentMatrix));
574 fLayerMatrixCache.insert(std::make_pair(&jlayer, layerMatrix));
575
576 return layerMatrix;
577 }
578};
579
580sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
581 AttachLayerContext* layerCtx) {
582 if (!jlayer.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500583 return nullptr;
584
585 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
586 static constexpr LayerAttacher gLayerAttachers[] = {
587 AttachCompLayer, // 'ty': 0
588 AttachSolidLayer, // 'ty': 1
589 AttachImageLayer, // 'ty': 2
590 AttachNullLayer, // 'ty': 3
591 AttachShapeLayer, // 'ty': 4
592 AttachTextLayer, // 'ty': 5
593 };
594
Florin Malita18eafd92018-01-04 21:11:55 -0500595 int type = ParseInt(jlayer["ty"], -1);
Florin Malita094ccde2017-12-30 12:27:00 -0500596 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
597 return nullptr;
598 }
599
Florin Malita18eafd92018-01-04 21:11:55 -0500600 auto layer = gLayerAttachers[type](jlayer, layerCtx->fCtx);
601 auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer);
602
603 return layerMatrix
604 ? sksg::Transform::Make(std::move(layer), std::move(layerMatrix))
605 : layer;
Florin Malita094ccde2017-12-30 12:27:00 -0500606}
607
608sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
609 if (!comp.isObject())
610 return nullptr;
611
Florin Malita18eafd92018-01-04 21:11:55 -0500612 const auto& jlayers = comp["layers"];
613 if (!jlayers.isArray())
614 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500615
Florin Malita18eafd92018-01-04 21:11:55 -0500616 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
617 AttachLayerContext layerCtx(jlayers, ctx);
618
619 for (const auto& l : jlayers) {
620 if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500621 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -0500622 }
623 }
624
Florin Malita2a8275b2018-01-02 12:52:43 -0500625 if (layers.empty()) {
626 return nullptr;
627 }
628
629 // Layers are painted in bottom->top order.
630 auto comp_group = sksg::Group::Make();
631 for (int i = layers.count() - 1; i >= 0; --i) {
632 comp_group->addChild(std::move(layers[i]));
633 }
634
635 LOG("** Attached composition '%s': %d layers.\n",
636 ParseString(comp["id"], "").c_str(), layers.count());
637
Florin Malita094ccde2017-12-30 12:27:00 -0500638 return comp_group;
639}
640
641} // namespace
642
643std::unique_ptr<Animation> Animation::Make(SkStream* stream) {
644 if (!stream->hasLength()) {
645 // TODO: handle explicit buffering?
646 LOG("!! cannot parse streaming content\n");
647 return nullptr;
648 }
649
650 Json::Value json;
651 {
652 auto data = SkData::MakeFromStream(stream, stream->getLength());
653 if (!data) {
654 LOG("!! could not read stream\n");
655 return nullptr;
656 }
657
658 Json::Reader reader;
659
660 auto dataStart = static_cast<const char*>(data->data());
661 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
662 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
663 return nullptr;
664 }
665 }
666
667 const auto version = ParseString(json["v"], "");
668 const auto size = SkSize::Make(ParseScalar(json["w"], -1), ParseScalar(json["h"], -1));
669 const auto fps = ParseScalar(json["fr"], -1);
670
671 if (size.isEmpty() || version.isEmpty() || fps < 0) {
672 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
673 version.c_str(), size.width(), size.height(), fps);
674 return nullptr;
675 }
676
677 return std::unique_ptr<Animation>(new Animation(std::move(version), size, fps, json));
678}
679
680Animation::Animation(SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
681 : fVersion(std::move(version))
682 , fSize(size)
683 , fFrameRate(fps)
684 , fInPoint(ParseScalar(json["ip"], 0))
685 , fOutPoint(SkTMax(ParseScalar(json["op"], SK_ScalarMax), fInPoint)) {
686
687 AssetMap assets;
688 for (const auto& asset : json["assets"]) {
689 if (!asset.isObject()) {
690 continue;
691 }
692
693 assets.set(ParseString(asset["id"], ""), &asset);
694 }
695
696 AttachContext ctx = { assets, fAnimators };
697 fDom = AttachComposition(json, &ctx);
698
699 LOG("** Attached %d animators\n", fAnimators.count());
700}
701
702Animation::~Animation() = default;
703
704void Animation::render(SkCanvas* canvas) const {
705 if (!fDom)
706 return;
707
708 sksg::InvalidationController ic;
709 fDom->revalidate(&ic, SkMatrix::I());
710
711 // TODO: proper inval
712 fDom->render(canvas);
713
714 if (!fShowInval)
715 return;
716
717 SkPaint fill, stroke;
718 fill.setAntiAlias(true);
719 fill.setColor(0x40ff0000);
720 stroke.setAntiAlias(true);
721 stroke.setColor(0xffff0000);
722 stroke.setStyle(SkPaint::kStroke_Style);
723
724 for (const auto& r : ic) {
725 canvas->drawRect(r, fill);
726 canvas->drawRect(r, stroke);
727 }
728}
729
730void Animation::animationTick(SkMSec ms) {
731 // 't' in the BM model really means 'frame #'
732 auto t = static_cast<float>(ms) * fFrameRate / 1000;
733
734 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
735
736 // TODO: this can be optimized quite a bit with some sorting/state tracking.
737 for (const auto& a : fAnimators) {
738 a->tick(t);
739 }
740}
741
742} // namespace skotty