blob: d118516d16a193c7edb40c8c642f4a4ec2778b46 [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"
Florin Malita49328072018-01-08 12:51:12 -050015#include "SkImage.h"
Florin Malita094ccde2017-12-30 12:27:00 -050016#include "SkMakeUnique.h"
Florin Malita49328072018-01-08 12:51:12 -050017#include "SkOSPath.h"
Florin Malita094ccde2017-12-30 12:27:00 -050018#include "SkPaint.h"
19#include "SkPath.h"
20#include "SkPoint.h"
21#include "SkSGColor.h"
22#include "SkSGDraw.h"
Florin Malita094ccde2017-12-30 12:27:00 -050023#include "SkSGGroup.h"
Florin Malita49328072018-01-08 12:51:12 -050024#include "SkSGImage.h"
25#include "SkSGInvalidationController.h"
Florin Malitae6345d92018-01-03 23:37:54 -050026#include "SkSGMerge.h"
Florin Malitac0034172018-01-08 16:42:59 -050027#include "SkSGOpacityEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050028#include "SkSGPath.h"
Florin Malita2e1d7e22018-01-02 10:40:00 -050029#include "SkSGRect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050030#include "SkSGTransform.h"
Florin Malita51b8c892018-01-07 08:54:24 -050031#include "SkSGTrimEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050032#include "SkStream.h"
33#include "SkTArray.h"
34#include "SkTHash.h"
35
36#include <cmath>
Florin Malita18eafd92018-01-04 21:11:55 -050037#include <unordered_map>
Florin Malitae6345d92018-01-03 23:37:54 -050038#include <vector>
39
Florin Malita094ccde2017-12-30 12:27:00 -050040#include "stdlib.h"
41
42namespace skotty {
43
44namespace {
45
46using AssetMap = SkTHashMap<SkString, const Json::Value*>;
47
48struct AttachContext {
Florin Malita49328072018-01-08 12:51:12 -050049 const ResourceProvider& fResources;
Florin Malita094ccde2017-12-30 12:27:00 -050050 const AssetMap& fAssets;
51 SkTArray<std::unique_ptr<AnimatorBase>>& fAnimators;
52};
53
54bool LogFail(const Json::Value& json, const char* msg) {
55 const auto dump = json.toStyledString();
56 LOG("!! %s: %s", msg, dump.c_str());
57 return false;
58}
59
60// This is the workhorse for binding properties: depending on whether the property is animated,
61// it will either apply immediately or instantiate and attach a keyframe animator.
Florin Malitaf9590922018-01-09 11:56:09 -050062template <typename ValT, typename NodeT>
63bool BindProperty(const Json::Value& jprop, AttachContext* ctx, const sk_sp<NodeT>& node,
64 typename Animator<ValT, NodeT>::ApplyFuncT&& apply) {
Florin Malita094ccde2017-12-30 12:27:00 -050065 if (!jprop.isObject())
66 return false;
67
Florin Malita95448a92018-01-08 10:15:12 -050068 const auto& jpropA = jprop["a"];
69 const auto& jpropK = jprop["k"];
70
71 // Older Json versions don't have an "a" animation marker.
72 // For those, we attempt to parse both ways.
73 if (jpropA.isNull() || !ParseBool(jpropA, "false")) {
Florin Malitaf9590922018-01-09 11:56:09 -050074 ValT val;
75 if (ValueTraits<ValT>::Parse(jpropK, &val)) {
Florin Malita95448a92018-01-08 10:15:12 -050076 // Static property.
Florin Malitaf9590922018-01-09 11:56:09 -050077 apply(node.get(), val);
Florin Malita95448a92018-01-08 10:15:12 -050078 return true;
Florin Malita094ccde2017-12-30 12:27:00 -050079 }
80
Florin Malita95448a92018-01-08 10:15:12 -050081 if (!jpropA.isNull()) {
82 return LogFail(jprop, "Could not parse (explicit) static property");
Florin Malita094ccde2017-12-30 12:27:00 -050083 }
Florin Malita094ccde2017-12-30 12:27:00 -050084 }
85
Florin Malita95448a92018-01-08 10:15:12 -050086 // Keyframe property.
Florin Malitaf9590922018-01-09 11:56:09 -050087 using AnimatorT = Animator<ValT, NodeT>;
88 auto animator = AnimatorT::Make(ParseFrames<ValT>(jpropK), node, std::move(apply));
Florin Malita95448a92018-01-08 10:15:12 -050089
90 if (!animator) {
91 return LogFail(jprop, "Could not parse keyframed property");
92 }
93
94 ctx->fAnimators.push_back(std::move(animator));
95
Florin Malita094ccde2017-12-30 12:27:00 -050096 return true;
97}
98
Florin Malita18eafd92018-01-04 21:11:55 -050099sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
100 sk_sp<sksg::Matrix> parentMatrix) {
101 if (!t.isObject())
102 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500103
Florin Malita18eafd92018-01-04 21:11:55 -0500104 auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
105 auto composite = sk_make_sp<CompositeTransform>(matrix);
Florin Malitaf9590922018-01-09 11:56:09 -0500106 auto anchor_attached = BindProperty<VectorValue>(t["a"], ctx, composite,
107 [](CompositeTransform* node, const VectorValue& a) {
108 node->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
Florin Malita094ccde2017-12-30 12:27:00 -0500109 });
Florin Malitaf9590922018-01-09 11:56:09 -0500110 auto position_attached = BindProperty<VectorValue>(t["p"], ctx, composite,
111 [](CompositeTransform* node, const VectorValue& p) {
112 node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malita094ccde2017-12-30 12:27:00 -0500113 });
Florin Malitaf9590922018-01-09 11:56:09 -0500114 auto scale_attached = BindProperty<VectorValue>(t["s"], ctx, composite,
115 [](CompositeTransform* node, const VectorValue& s) {
116 node->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
Florin Malita094ccde2017-12-30 12:27:00 -0500117 });
Florin Malitaf9590922018-01-09 11:56:09 -0500118 auto rotation_attached = BindProperty<ScalarValue>(t["r"], ctx, composite,
119 [](CompositeTransform* node, const ScalarValue& r) {
Florin Malita094ccde2017-12-30 12:27:00 -0500120 node->setRotation(r);
121 });
Florin Malitaf9590922018-01-09 11:56:09 -0500122 auto skew_attached = BindProperty<ScalarValue>(t["sk"], ctx, composite,
123 [](CompositeTransform* node, const ScalarValue& sk) {
Florin Malita094ccde2017-12-30 12:27:00 -0500124 node->setSkew(sk);
125 });
Florin Malitaf9590922018-01-09 11:56:09 -0500126 auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], ctx, composite,
127 [](CompositeTransform* node, const ScalarValue& sa) {
Florin Malita094ccde2017-12-30 12:27:00 -0500128 node->setSkewAxis(sa);
129 });
130
131 if (!anchor_attached &&
132 !position_attached &&
133 !scale_attached &&
134 !rotation_attached &&
135 !skew_attached &&
136 !skewaxis_attached) {
137 LogFail(t, "Could not parse transform");
Florin Malita18eafd92018-01-04 21:11:55 -0500138 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500139 }
140
Florin Malita18eafd92018-01-04 21:11:55 -0500141 return matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500142}
143
Florin Malitac0034172018-01-08 16:42:59 -0500144sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachContext* ctx,
145 sk_sp<sksg::RenderNode> childNode) {
146 if (!jtransform.isObject() || !childNode)
147 return childNode;
148
149 // This is more peeky than other attachers, because we want to avoid redundant opacity
150 // nodes for the extremely common case of static opaciy == 100.
151 const auto& opacity = jtransform["o"];
152 if (opacity.isObject() &&
153 !ParseBool(opacity["a"], true) &&
154 ParseScalar(opacity["k"], -1) == 100) {
155 // Ignoring static full opacity.
156 return childNode;
157 }
158
159 auto opacityNode = sksg::OpacityEffect::Make(childNode);
Florin Malitaf9590922018-01-09 11:56:09 -0500160 BindProperty<ScalarValue>(opacity, ctx, opacityNode,
161 [](sksg::OpacityEffect* node, const ScalarValue& o) {
Florin Malitac0034172018-01-08 16:42:59 -0500162 // BM opacity is [0..100]
163 node->setOpacity(o * 0.01f);
164 });
165
166 return opacityNode;
167}
168
Florin Malita094ccde2017-12-30 12:27:00 -0500169sk_sp<sksg::RenderNode> AttachShape(const Json::Value&, AttachContext* ctx);
170sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
171
172sk_sp<sksg::RenderNode> AttachShapeGroup(const Json::Value& jgroup, AttachContext* ctx) {
173 SkASSERT(jgroup.isObject());
174
175 return AttachShape(jgroup["it"], ctx);
176}
177
178sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
179 SkASSERT(jpath.isObject());
180
181 auto path_node = sksg::Path::Make();
Florin Malitaf9590922018-01-09 11:56:09 -0500182 auto path_attached = BindProperty<ShapeValue>(jpath["ks"], ctx, path_node,
183 [](sksg::Path* node, const ShapeValue& p) { node->setPath(p); });
Florin Malita094ccde2017-12-30 12:27:00 -0500184
185 if (path_attached)
186 LOG("** Attached path geometry - verbs: %d\n", path_node->getPath().countVerbs());
187
188 return path_attached ? path_node : nullptr;
189}
190
Florin Malita2e1d7e22018-01-02 10:40:00 -0500191sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
192 SkASSERT(jrect.isObject());
193
194 auto rect_node = sksg::RRect::Make();
195 auto composite = sk_make_sp<CompositeRRect>(rect_node);
196
Florin Malitaf9590922018-01-09 11:56:09 -0500197 auto p_attached = BindProperty<VectorValue>(jrect["p"], ctx, composite,
198 [](CompositeRRect* node, const VectorValue& p) {
199 node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
200 });
201 auto s_attached = BindProperty<VectorValue>(jrect["s"], ctx, composite,
202 [](CompositeRRect* node, const VectorValue& s) {
203 node->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
204 });
205 auto r_attached = BindProperty<ScalarValue>(jrect["r"], ctx, composite,
206 [](CompositeRRect* node, const ScalarValue& r) {
207 node->setRadius(SkSize::Make(r, r));
208 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500209
210 if (!p_attached && !s_attached && !r_attached) {
211 return nullptr;
212 }
213
Florin Malitafbc13f12018-01-04 10:26:35 -0500214 LOG("** Attached (r)rect geometry\n");
215
216 return rect_node;
217}
218
219sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
220 SkASSERT(jellipse.isObject());
221
222 auto rect_node = sksg::RRect::Make();
223 auto composite = sk_make_sp<CompositeRRect>(rect_node);
224
Florin Malitaf9590922018-01-09 11:56:09 -0500225 auto p_attached = BindProperty<VectorValue>(jellipse["p"], ctx, composite,
226 [](CompositeRRect* node, const VectorValue& p) {
227 node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
228 });
229 auto s_attached = BindProperty<VectorValue>(jellipse["s"], ctx, composite,
230 [](CompositeRRect* node, const VectorValue& s) {
231 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
232 node->setSize(sz);
233 node->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
234 });
Florin Malitafbc13f12018-01-04 10:26:35 -0500235
236 if (!p_attached && !s_attached) {
237 return nullptr;
238 }
239
240 LOG("** Attached ellipse geometry\n");
241
Florin Malita2e1d7e22018-01-02 10:40:00 -0500242 return rect_node;
243}
244
Florin Malita02a32b02018-01-04 11:27:09 -0500245sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
246 SkASSERT(jstar.isObject());
247
248 static constexpr CompositePolyStar::Type gTypes[] = {
249 CompositePolyStar::Type::kStar, // "sy": 1
250 CompositePolyStar::Type::kPoly, // "sy": 2
251 };
252
253 const auto type = ParseInt(jstar["sy"], 0) - 1;
254 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
255 LogFail(jstar, "Unknown polystar type");
256 return nullptr;
257 }
258
259 auto path_node = sksg::Path::Make();
260 auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
261
Florin Malitaf9590922018-01-09 11:56:09 -0500262 BindProperty<VectorValue>(jstar["p"], ctx, composite,
263 [](CompositePolyStar* node, const VectorValue& p) {
264 node->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
265 });
266 BindProperty<ScalarValue>(jstar["pt"], ctx, composite,
267 [](CompositePolyStar* node, const ScalarValue& pt) {
268 node->setPointCount(pt);
269 });
270 BindProperty<ScalarValue>(jstar["ir"], ctx, composite,
271 [](CompositePolyStar* node, const ScalarValue& ir) {
272 node->setInnerRadius(ir);
273 });
274 BindProperty<ScalarValue>(jstar["or"], ctx, composite,
275 [](CompositePolyStar* node, const ScalarValue& otr) {
Florin Malita9661b982018-01-06 14:25:49 -0500276 node->setOuterRadius(otr);
277 });
Florin Malitaf9590922018-01-09 11:56:09 -0500278 BindProperty<ScalarValue>(jstar["is"], ctx, composite,
279 [](CompositePolyStar* node, const ScalarValue& is) {
Florin Malita9661b982018-01-06 14:25:49 -0500280 node->setInnerRoundness(is);
281 });
Florin Malitaf9590922018-01-09 11:56:09 -0500282 BindProperty<ScalarValue>(jstar["os"], ctx, composite,
283 [](CompositePolyStar* node, const ScalarValue& os) {
Florin Malita9661b982018-01-06 14:25:49 -0500284 node->setOuterRoundness(os);
285 });
Florin Malitaf9590922018-01-09 11:56:09 -0500286 BindProperty<ScalarValue>(jstar["r"], ctx, composite,
287 [](CompositePolyStar* node, const ScalarValue& r) {
288 node->setRotation(r);
289 });
Florin Malita02a32b02018-01-04 11:27:09 -0500290
291 return path_node;
292}
293
Florin Malita094ccde2017-12-30 12:27:00 -0500294sk_sp<sksg::Color> AttachColorPaint(const Json::Value& obj, AttachContext* ctx) {
295 SkASSERT(obj.isObject());
296
297 auto color_node = sksg::Color::Make(SK_ColorBLACK);
298 color_node->setAntiAlias(true);
299
Florin Malitaf9590922018-01-09 11:56:09 -0500300 auto color_attached = BindProperty<VectorValue>(obj["c"], ctx, color_node,
301 [](sksg::Color* node, const VectorValue& c) {
302 node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
303 });
Florin Malita094ccde2017-12-30 12:27:00 -0500304
305 return color_attached ? color_node : nullptr;
306}
307
308sk_sp<sksg::PaintNode> AttachFillPaint(const Json::Value& jfill, AttachContext* ctx) {
309 SkASSERT(jfill.isObject());
310
311 auto color = AttachColorPaint(jfill, ctx);
312 if (color) {
313 LOG("** Attached color fill: 0x%x\n", color->getColor());
314 }
315 return color;
316}
317
318sk_sp<sksg::PaintNode> AttachStrokePaint(const Json::Value& jstroke, AttachContext* ctx) {
319 SkASSERT(jstroke.isObject());
320
321 auto stroke_node = AttachColorPaint(jstroke, ctx);
322 if (!stroke_node)
323 return nullptr;
324
325 LOG("** Attached color stroke: 0x%x\n", stroke_node->getColor());
326
327 stroke_node->setStyle(SkPaint::kStroke_Style);
328
Florin Malitaf9590922018-01-09 11:56:09 -0500329 auto width_attached = BindProperty<ScalarValue>(jstroke["w"], ctx, stroke_node,
330 [](sksg::Color* node, const ScalarValue& w) {
331 node->setStrokeWidth(w);
332 });
Florin Malita094ccde2017-12-30 12:27:00 -0500333 if (!width_attached)
334 return nullptr;
335
336 stroke_node->setStrokeMiter(ParseScalar(jstroke["ml"], 4));
337
338 static constexpr SkPaint::Join gJoins[] = {
339 SkPaint::kMiter_Join,
340 SkPaint::kRound_Join,
341 SkPaint::kBevel_Join,
342 };
343 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseInt(jstroke["lj"], 1) - 1,
344 0, SK_ARRAY_COUNT(gJoins) - 1)]);
345
346 static constexpr SkPaint::Cap gCaps[] = {
347 SkPaint::kButt_Cap,
348 SkPaint::kRound_Cap,
349 SkPaint::kSquare_Cap,
350 };
351 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseInt(jstroke["lc"], 1) - 1,
352 0, SK_ARRAY_COUNT(gCaps) - 1)]);
353
354 return stroke_node;
355}
356
Florin Malitae6345d92018-01-03 23:37:54 -0500357std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
358 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
359 std::vector<sk_sp<sksg::GeometryNode>> merged;
360
361 static constexpr sksg::Merge::Mode gModes[] = {
362 sksg::Merge::Mode::kMerge, // "mm": 1
363 sksg::Merge::Mode::kUnion, // "mm": 2
364 sksg::Merge::Mode::kDifference, // "mm": 3
365 sksg::Merge::Mode::kIntersect, // "mm": 4
366 sksg::Merge::Mode::kXOR , // "mm": 5
367 };
368
Florin Malita51b8c892018-01-07 08:54:24 -0500369 const auto mode = gModes[SkTPin<int>(ParseInt(jmerge["mm"], 1) - 1,
370 0, SK_ARRAY_COUNT(gModes) - 1)];
Florin Malitae6345d92018-01-03 23:37:54 -0500371 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
372
373 LOG("** Attached merge path effect, mode: %d\n", mode);
374
375 return merged;
376}
377
Florin Malita51b8c892018-01-07 08:54:24 -0500378std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
379 const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
380
381 enum class Mode {
382 kMerged, // "m": 1
383 kSeparate, // "m": 2
384 } gModes[] = { Mode::kMerged, Mode::kSeparate };
385
386 const auto mode = gModes[SkTPin<int>(ParseInt(jtrim["m"], 1) - 1,
387 0, SK_ARRAY_COUNT(gModes) - 1)];
388
389 std::vector<sk_sp<sksg::GeometryNode>> inputs;
390 if (mode == Mode::kMerged) {
391 inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge));
392 } else {
393 inputs = std::move(geos);
394 }
395
396 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
397 trimmed.reserve(inputs.size());
398 for (const auto& i : inputs) {
399 const auto trim = sksg::TrimEffect::Make(i);
400 trimmed.push_back(trim);
Florin Malitaf9590922018-01-09 11:56:09 -0500401 BindProperty<ScalarValue>(jtrim["s"], ctx, trim,
402 [](sksg::TrimEffect* node, const ScalarValue& s) {
Florin Malita51b8c892018-01-07 08:54:24 -0500403 node->setStart(s * 0.01f);
404 });
Florin Malitaf9590922018-01-09 11:56:09 -0500405 BindProperty<ScalarValue>(jtrim["e"], ctx, trim,
406 [](sksg::TrimEffect* node, const ScalarValue& e) {
Florin Malita51b8c892018-01-07 08:54:24 -0500407 node->setEnd(e * 0.01f);
408 });
409 // TODO: "offset" doesn't currently work the same as BM - figure out what's going on.
Florin Malitaf9590922018-01-09 11:56:09 -0500410 BindProperty<ScalarValue>(jtrim["o"], ctx, trim,
411 [](sksg::TrimEffect* node, const ScalarValue& o) {
Florin Malita51b8c892018-01-07 08:54:24 -0500412 node->setOffset(o * 0.01f);
413 });
414 }
415
416 return trimmed;
417}
418
Florin Malita094ccde2017-12-30 12:27:00 -0500419using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
420static constexpr GeometryAttacherT gGeometryAttachers[] = {
421 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500422 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500423 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500424 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500425};
426
427using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
428static constexpr PaintAttacherT gPaintAttachers[] = {
429 AttachFillPaint,
430 AttachStrokePaint,
431};
432
433using GroupAttacherT = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
434static constexpr GroupAttacherT gGroupAttachers[] = {
435 AttachShapeGroup,
436};
437
Florin Malitae6345d92018-01-03 23:37:54 -0500438using GeometryEffectAttacherT =
439 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
440 AttachContext*,
441 std::vector<sk_sp<sksg::GeometryNode>>&&);
442static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
443 AttachMergeGeometryEffect,
Florin Malita51b8c892018-01-07 08:54:24 -0500444 AttachTrimGeometryEffect,
Florin Malitae6345d92018-01-03 23:37:54 -0500445};
446
Florin Malita094ccde2017-12-30 12:27:00 -0500447enum class ShapeType {
448 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500449 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500450 kPaint,
451 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500452 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500453};
454
455struct ShapeInfo {
456 const char* fTypeString;
457 ShapeType fShapeType;
458 uint32_t fAttacherIndex; // index into respective attacher tables
459};
460
461const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
462 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500463 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500464 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachFillPaint
465 { "gr", ShapeType::kGroup , 0 }, // group -> AttachShapeGroup
466 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500467 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500468 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500469 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500470 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachStrokePaint
Florin Malita51b8c892018-01-07 08:54:24 -0500471 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
Florin Malita18eafd92018-01-04 21:11:55 -0500472 { "tr", ShapeType::kTransform , 0 }, // transform -> In-place handler
Florin Malita094ccde2017-12-30 12:27:00 -0500473 };
474
475 if (!shape.isObject())
476 return nullptr;
477
478 const auto& type = shape["ty"];
479 if (!type.isString())
480 return nullptr;
481
482 const auto* info = bsearch(type.asCString(),
483 gShapeInfo,
484 SK_ARRAY_COUNT(gShapeInfo),
485 sizeof(ShapeInfo),
486 [](const void* key, const void* info) {
487 return strcmp(static_cast<const char*>(key),
488 static_cast<const ShapeInfo*>(info)->fTypeString);
489 });
490
491 return static_cast<const ShapeInfo*>(info);
492}
493
494sk_sp<sksg::RenderNode> AttachShape(const Json::Value& shapeArray, AttachContext* ctx) {
495 if (!shapeArray.isArray())
496 return nullptr;
497
Florin Malita2a8275b2018-01-02 12:52:43 -0500498 // (https://helpx.adobe.com/after-effects/using/overview-shape-layers-paths-vector.html#groups_and_render_order_for_shapes_and_shape_attributes)
499 //
500 // Render order for shapes within a shape layer
501 //
502 // The rules for rendering a shape layer are similar to the rules for rendering a composition
503 // that contains nested compositions:
504 //
505 // * Within a group, the shape at the bottom of the Timeline panel stacking order is rendered
506 // first.
507 //
508 // * All path operations within a group are performed before paint operations. This means,
509 // for example, that the stroke follows the distortions in the path made by the Wiggle Paths
510 // path operation. Path operations within a group are performed from top to bottom.
511 //
512 // * Paint operations within a group are performed from the bottom to the top in the Timeline
513 // panel stacking order. This means, for example, that a stroke is rendered on top of
514 // (in front of) a stroke that appears after it in the Timeline panel.
515 //
Florin Malitadacc02b2017-12-31 09:12:31 -0500516 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
517 sk_sp<sksg::RenderNode> xformed_group = shape_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500518
Florin Malitae6345d92018-01-03 23:37:54 -0500519 std::vector<sk_sp<sksg::GeometryNode>> geos;
520 std::vector<sk_sp<sksg::RenderNode>> draws;
Florin Malita094ccde2017-12-30 12:27:00 -0500521
522 for (const auto& s : shapeArray) {
523 const auto* info = FindShapeInfo(s);
524 if (!info) {
525 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
526 continue;
527 }
528
529 switch (info->fShapeType) {
530 case ShapeType::kGeometry: {
531 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
532 if (auto geo = gGeometryAttachers[info->fAttacherIndex](s, ctx)) {
533 geos.push_back(std::move(geo));
534 }
535 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500536 case ShapeType::kGeometryEffect: {
537 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
538 geos = gGeometryEffectAttachers[info->fAttacherIndex](s, ctx, std::move(geos));
539 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500540 case ShapeType::kPaint: {
541 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
542 if (auto paint = gPaintAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500543 for (const auto& geo : geos) {
544 draws.push_back(sksg::Draw::Make(geo, paint));
545 }
Florin Malita094ccde2017-12-30 12:27:00 -0500546 }
547 } break;
548 case ShapeType::kGroup: {
549 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGroupAttachers));
550 if (auto group = gGroupAttachers[info->fAttacherIndex](s, ctx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500551 draws.push_back(std::move(group));
Florin Malita094ccde2017-12-30 12:27:00 -0500552 }
553 } break;
Florin Malitadacc02b2017-12-31 09:12:31 -0500554 case ShapeType::kTransform: {
Florin Malita2a8275b2018-01-02 12:52:43 -0500555 // TODO: BM appears to transform the geometry, not the draw op itself.
Florin Malita18eafd92018-01-04 21:11:55 -0500556 if (auto matrix = AttachMatrix(s, ctx, nullptr)) {
557 xformed_group = sksg::Transform::Make(std::move(xformed_group),
558 std::move(matrix));
559 }
Florin Malitac0034172018-01-08 16:42:59 -0500560 xformed_group = AttachOpacity(s, ctx, std::move(xformed_group));
Florin Malitadacc02b2017-12-31 09:12:31 -0500561 } break;
Florin Malita094ccde2017-12-30 12:27:00 -0500562 }
563 }
564
Florin Malita2a8275b2018-01-02 12:52:43 -0500565 if (draws.empty()) {
566 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500567 }
568
Florin Malitae6345d92018-01-03 23:37:54 -0500569 for (auto draw = draws.rbegin(); draw != draws.rend(); ++draw) {
570 shape_group->addChild(std::move(*draw));
Florin Malita2a8275b2018-01-02 12:52:43 -0500571 }
572
Florin Malitae6345d92018-01-03 23:37:54 -0500573 LOG("** Attached shape: %zd draws.\n", draws.size());
Florin Malitadacc02b2017-12-31 09:12:31 -0500574 return xformed_group;
Florin Malita094ccde2017-12-30 12:27:00 -0500575}
576
577sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
578 SkASSERT(layer.isObject());
579
580 auto refId = ParseString(layer["refId"], "");
581 if (refId.isEmpty()) {
582 LOG("!! Comp layer missing refId\n");
583 return nullptr;
584 }
585
586 const auto* comp = ctx->fAssets.find(refId);
587 if (!comp) {
588 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
589 return nullptr;
590 }
591
592 // TODO: cycle detection
593 return AttachComposition(**comp, ctx);
594}
595
596sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& layer, AttachContext*) {
597 SkASSERT(layer.isObject());
598
599 LOG("?? Solid layer stub\n");
600 return nullptr;
601}
602
Florin Malita49328072018-01-08 12:51:12 -0500603sk_sp<sksg::RenderNode> AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) {
604 SkASSERT(jimage.isObject());
605
606 const auto name = ParseString(jimage["p"], ""),
607 path = ParseString(jimage["u"], "");
608 if (name.isEmpty())
609 return nullptr;
610
611 // TODO: plumb resource paths explicitly to ResourceProvider?
612 const auto resName = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
613 const auto resStream = ctx->fResources.openStream(resName.c_str());
614 if (!resStream || !resStream->hasLength()) {
615 LOG("!! Could not load image resource: %s\n", resName.c_str());
616 return nullptr;
617 }
618
619 // TODO: non-intrisic image sizing
620 return sksg::Image::Make(
621 SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
622}
623
624sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx) {
Florin Malita094ccde2017-12-30 12:27:00 -0500625 SkASSERT(layer.isObject());
626
Florin Malita49328072018-01-08 12:51:12 -0500627 auto refId = ParseString(layer["refId"], "");
628 if (refId.isEmpty()) {
629 LOG("!! Image layer missing refId\n");
630 return nullptr;
631 }
632
633 const auto* jimage = ctx->fAssets.find(refId);
634 if (!jimage) {
635 LOG("!! Image asset not found: '%s'\n", refId.c_str());
636 return nullptr;
637 }
638
639 return AttachImageAsset(**jimage, ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500640}
641
642sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
643 SkASSERT(layer.isObject());
644
Florin Malita18eafd92018-01-04 21:11:55 -0500645 // Null layers are used solely to drive dependent transforms,
646 // but we use free-floating sksg::Matrices for that purpose.
Florin Malita094ccde2017-12-30 12:27:00 -0500647 return nullptr;
648}
649
650sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx) {
651 SkASSERT(layer.isObject());
652
653 LOG("** Attaching shape layer ind: %d\n", ParseInt(layer["ind"], 0));
654
655 return AttachShape(layer["shapes"], ctx);
656}
657
658sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*) {
659 SkASSERT(layer.isObject());
660
661 LOG("?? Text layer stub\n");
662 return nullptr;
663}
664
Florin Malita18eafd92018-01-04 21:11:55 -0500665struct AttachLayerContext {
666 AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
667 : fLayerList(jlayers), fCtx(ctx) {}
668
669 const Json::Value& fLayerList;
670 AttachContext* fCtx;
671 std::unordered_map<const Json::Value*, sk_sp<sksg::Matrix>> fLayerMatrixCache;
672 std::unordered_map<int, const Json::Value*> fLayerIndexCache;
673
674 const Json::Value* findLayer(int index) {
675 SkASSERT(fLayerList.isArray());
676
677 if (index < 0) {
678 return nullptr;
679 }
680
681 const auto cached = fLayerIndexCache.find(index);
682 if (cached != fLayerIndexCache.end()) {
683 return cached->second;
684 }
685
686 for (const auto& l : fLayerList) {
687 if (!l.isObject()) {
688 continue;
689 }
690
691 if (ParseInt(l["ind"], -1) == index) {
692 fLayerIndexCache.insert(std::make_pair(index, &l));
693 return &l;
694 }
695 }
696
697 return nullptr;
698 }
699
700 sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
701 SkASSERT(jlayer.isObject());
702
703 const auto cached = fLayerMatrixCache.find(&jlayer);
704 if (cached != fLayerMatrixCache.end()) {
705 return cached->second;
706 }
707
708 const auto* parentLayer = this->findLayer(ParseInt(jlayer["parent"], -1));
709
710 // TODO: cycle detection?
711 auto parentMatrix = (parentLayer && parentLayer != &jlayer)
712 ? this->AttachLayerMatrix(*parentLayer) : nullptr;
713
714 auto layerMatrix = AttachMatrix(jlayer["ks"], fCtx, std::move(parentMatrix));
715 fLayerMatrixCache.insert(std::make_pair(&jlayer, layerMatrix));
716
717 return layerMatrix;
718 }
719};
720
721sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
722 AttachLayerContext* layerCtx) {
723 if (!jlayer.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500724 return nullptr;
725
726 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
727 static constexpr LayerAttacher gLayerAttachers[] = {
728 AttachCompLayer, // 'ty': 0
729 AttachSolidLayer, // 'ty': 1
730 AttachImageLayer, // 'ty': 2
731 AttachNullLayer, // 'ty': 3
732 AttachShapeLayer, // 'ty': 4
733 AttachTextLayer, // 'ty': 5
734 };
735
Florin Malita18eafd92018-01-04 21:11:55 -0500736 int type = ParseInt(jlayer["ty"], -1);
Florin Malita094ccde2017-12-30 12:27:00 -0500737 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
738 return nullptr;
739 }
740
Florin Malita71cba8f2018-01-09 08:07:14 -0500741 // Layer content.
742 auto layer = gLayerAttachers[type](jlayer, layerCtx->fCtx);
743 if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
744 // Optional layer transform.
745 layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
746 }
747 // Optional layer opacity.
748 layer = AttachOpacity(jlayer["ks"], layerCtx->fCtx, std::move(layer));
Florin Malita18eafd92018-01-04 21:11:55 -0500749
Florin Malita71cba8f2018-01-09 08:07:14 -0500750 // TODO: we should also disable related/inactive animators.
751 class Activator final : public AnimatorBase {
752 public:
753 Activator(sk_sp<sksg::OpacityEffect> controlNode, float in, float out)
754 : fControlNode(std::move(controlNode))
755 , fIn(in)
756 , fOut(out) {}
757
Florin Malitaa6dd7522018-01-09 08:46:52 -0500758 void tick(float t) override {
Florin Malita71cba8f2018-01-09 08:07:14 -0500759 // Keep the layer fully transparent except for its [in..out] lifespan.
760 // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
761 fControlNode->setOpacity(t >= fIn && t <= fOut ? 1 : 0);
762 }
763
764 private:
765 const sk_sp<sksg::OpacityEffect> fControlNode;
766 const float fIn,
767 fOut;
768 };
769
770 auto layerControl = sksg::OpacityEffect::Make(std::move(layer));
771 const auto in = ParseScalar(jlayer["ip"], 0),
772 out = ParseScalar(jlayer["op"], in);
773
774 if (in >= out || ! layerControl)
775 return nullptr;
776
777 layerCtx->fCtx->fAnimators.push_back(skstd::make_unique<Activator>(layerControl, in, out));
778
779 return layerControl;
Florin Malita094ccde2017-12-30 12:27:00 -0500780}
781
782sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
783 if (!comp.isObject())
784 return nullptr;
785
Florin Malita18eafd92018-01-04 21:11:55 -0500786 const auto& jlayers = comp["layers"];
787 if (!jlayers.isArray())
788 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500789
Florin Malita18eafd92018-01-04 21:11:55 -0500790 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
791 AttachLayerContext layerCtx(jlayers, ctx);
792
793 for (const auto& l : jlayers) {
794 if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500795 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -0500796 }
797 }
798
Florin Malita2a8275b2018-01-02 12:52:43 -0500799 if (layers.empty()) {
800 return nullptr;
801 }
802
803 // Layers are painted in bottom->top order.
804 auto comp_group = sksg::Group::Make();
805 for (int i = layers.count() - 1; i >= 0; --i) {
806 comp_group->addChild(std::move(layers[i]));
807 }
808
809 LOG("** Attached composition '%s': %d layers.\n",
810 ParseString(comp["id"], "").c_str(), layers.count());
811
Florin Malita094ccde2017-12-30 12:27:00 -0500812 return comp_group;
813}
814
815} // namespace
816
Florin Malita49328072018-01-08 12:51:12 -0500817std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
Florin Malita094ccde2017-12-30 12:27:00 -0500818 if (!stream->hasLength()) {
819 // TODO: handle explicit buffering?
820 LOG("!! cannot parse streaming content\n");
821 return nullptr;
822 }
823
824 Json::Value json;
825 {
826 auto data = SkData::MakeFromStream(stream, stream->getLength());
827 if (!data) {
828 LOG("!! could not read stream\n");
829 return nullptr;
830 }
831
832 Json::Reader reader;
833
834 auto dataStart = static_cast<const char*>(data->data());
835 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
836 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
837 return nullptr;
838 }
839 }
840
841 const auto version = ParseString(json["v"], "");
842 const auto size = SkSize::Make(ParseScalar(json["w"], -1), ParseScalar(json["h"], -1));
843 const auto fps = ParseScalar(json["fr"], -1);
844
845 if (size.isEmpty() || version.isEmpty() || fps < 0) {
846 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
847 version.c_str(), size.width(), size.height(), fps);
848 return nullptr;
849 }
850
Florin Malita49328072018-01-08 12:51:12 -0500851 return std::unique_ptr<Animation>(new Animation(res, std::move(version), size, fps, json));
Florin Malita094ccde2017-12-30 12:27:00 -0500852}
853
Florin Malita49328072018-01-08 12:51:12 -0500854std::unique_ptr<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
855 class DirectoryResourceProvider final : public ResourceProvider {
856 public:
857 explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
858
859 std::unique_ptr<SkStream> openStream(const char resource[]) const override {
860 const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
861 return SkStream::MakeFromFile(resPath.c_str());
862 }
863
864 private:
865 const SkString fDir;
866 };
867
868 const auto jsonStream = SkStream::MakeFromFile(path);
869 if (!jsonStream)
870 return nullptr;
871
872 std::unique_ptr<ResourceProvider> defaultProvider;
873 if (!res) {
874 defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
875 }
876
877 return Make(jsonStream.get(), res ? *res : *defaultProvider);
878}
879
880Animation::Animation(const ResourceProvider& resources,
881 SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
Florin Malita094ccde2017-12-30 12:27:00 -0500882 : fVersion(std::move(version))
883 , fSize(size)
884 , fFrameRate(fps)
885 , fInPoint(ParseScalar(json["ip"], 0))
886 , fOutPoint(SkTMax(ParseScalar(json["op"], SK_ScalarMax), fInPoint)) {
887
888 AssetMap assets;
889 for (const auto& asset : json["assets"]) {
890 if (!asset.isObject()) {
891 continue;
892 }
893
894 assets.set(ParseString(asset["id"], ""), &asset);
895 }
896
Florin Malita49328072018-01-08 12:51:12 -0500897 AttachContext ctx = { resources, assets, fAnimators };
Florin Malita094ccde2017-12-30 12:27:00 -0500898 fDom = AttachComposition(json, &ctx);
899
Florin Malitadb385732018-01-09 12:19:32 -0500900 // In case the client calls render before the first tick.
901 this->animationTick(0);
902
Florin Malita094ccde2017-12-30 12:27:00 -0500903 LOG("** Attached %d animators\n", fAnimators.count());
904}
905
906Animation::~Animation() = default;
907
Mike Reed29859872018-01-08 08:25:27 -0500908void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
Florin Malita094ccde2017-12-30 12:27:00 -0500909 if (!fDom)
910 return;
911
912 sksg::InvalidationController ic;
913 fDom->revalidate(&ic, SkMatrix::I());
914
915 // TODO: proper inval
Mike Reed29859872018-01-08 08:25:27 -0500916 SkAutoCanvasRestore restore(canvas, true);
917 const SkRect srcR = SkRect::MakeSize(this->size());
918 if (dstR) {
919 canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
920 }
921 canvas->clipRect(srcR);
Florin Malita094ccde2017-12-30 12:27:00 -0500922 fDom->render(canvas);
923
924 if (!fShowInval)
925 return;
926
927 SkPaint fill, stroke;
928 fill.setAntiAlias(true);
929 fill.setColor(0x40ff0000);
930 stroke.setAntiAlias(true);
931 stroke.setColor(0xffff0000);
932 stroke.setStyle(SkPaint::kStroke_Style);
933
934 for (const auto& r : ic) {
935 canvas->drawRect(r, fill);
936 canvas->drawRect(r, stroke);
937 }
938}
939
940void Animation::animationTick(SkMSec ms) {
941 // 't' in the BM model really means 'frame #'
942 auto t = static_cast<float>(ms) * fFrameRate / 1000;
943
944 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
945
946 // TODO: this can be optimized quite a bit with some sorting/state tracking.
947 for (const auto& a : fAnimators) {
948 a->tick(t);
949 }
950}
951
952} // namespace skotty