blob: 48e20d28dc9b7df4bc99f79854ae65d189f38c71 [file] [log] [blame]
Florin Malita1b1dead2018-08-21 14:34:02 -04001/*
2 * Copyright 2018 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 "SkottiePriv.h"
9
10#include "SkJSON.h"
11#include "SkottieAdapter.h"
Florin Malita1b1dead2018-08-21 14:34:02 -040012#include "SkottieJson.h"
13#include "SkottieValue.h"
14#include "SkPath.h"
15#include "SkSGColor.h"
16#include "SkSGDraw.h"
17#include "SkSGGeometryTransform.h"
18#include "SkSGGradient.h"
19#include "SkSGGroup.h"
20#include "SkSGMerge.h"
21#include "SkSGPath.h"
22#include "SkSGRect.h"
23#include "SkSGRoundEffect.h"
24#include "SkSGTransform.h"
25#include "SkSGTrimEffect.h"
26
Florin Malitacd9d0742018-09-10 09:57:15 -040027#include <algorithm>
Florin Malita40c7c642018-09-08 20:09:37 -040028#include <iterator>
29
Florin Malita1b1dead2018-08-21 14:34:02 -040030namespace skottie {
31namespace internal {
32
33namespace {
34
35sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath,
Florin Malita471a9462018-08-25 20:22:34 -040036 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040037 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -040038 return abuilder->attachPath(jpath["ks"], ascope);
Florin Malita1b1dead2018-08-21 14:34:02 -040039}
40
41sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
Florin Malita471a9462018-08-25 20:22:34 -040042 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040043 AnimatorScope* ascope) {
44 auto rect_node = sksg::RRect::Make();
Florin Malitac91527f2019-01-22 12:19:51 -050045 rect_node->setDirection(ParseDefault(jrect["d"], -1) == 3 ? SkPath::kCCW_Direction
46 : SkPath::kCW_Direction);
47 rect_node->setInitialPointIndex(2); // starting point: (Right, Top - radius.y)
48
Florin Malita1b1dead2018-08-21 14:34:02 -040049 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
50
Florin Malita471a9462018-08-25 20:22:34 -040051 auto p_attached = abuilder->bindProperty<VectorValue>(jrect["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040052 [adapter](const VectorValue& p) {
53 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
54 });
Florin Malita471a9462018-08-25 20:22:34 -040055 auto s_attached = abuilder->bindProperty<VectorValue>(jrect["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040056 [adapter](const VectorValue& s) {
57 adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
58 });
Florin Malita471a9462018-08-25 20:22:34 -040059 auto r_attached = abuilder->bindProperty<ScalarValue>(jrect["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040060 [adapter](const ScalarValue& r) {
61 adapter->setRadius(SkSize::Make(r, r));
62 });
63
64 if (!p_attached && !s_attached && !r_attached) {
65 return nullptr;
66 }
67
68 return std::move(rect_node);
69}
70
71sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
Florin Malita471a9462018-08-25 20:22:34 -040072 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040073 AnimatorScope* ascope) {
74 auto rect_node = sksg::RRect::Make();
Florin Malitac91527f2019-01-22 12:19:51 -050075 rect_node->setDirection(ParseDefault(jellipse["d"], -1) == 3 ? SkPath::kCCW_Direction
76 : SkPath::kCW_Direction);
77 rect_node->setInitialPointIndex(1); // starting point: (Center, Top)
78
Florin Malita1b1dead2018-08-21 14:34:02 -040079 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
80
Florin Malita471a9462018-08-25 20:22:34 -040081 auto p_attached = abuilder->bindProperty<VectorValue>(jellipse["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040082 [adapter](const VectorValue& p) {
83 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
84 });
Florin Malita471a9462018-08-25 20:22:34 -040085 auto s_attached = abuilder->bindProperty<VectorValue>(jellipse["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040086 [adapter](const VectorValue& s) {
87 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
88 adapter->setSize(sz);
89 adapter->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
90 });
91
92 if (!p_attached && !s_attached) {
93 return nullptr;
94 }
95
96 return std::move(rect_node);
97}
98
99sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
Florin Malita471a9462018-08-25 20:22:34 -0400100 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400101 AnimatorScope* ascope) {
102 static constexpr PolyStarAdapter::Type gTypes[] = {
103 PolyStarAdapter::Type::kStar, // "sy": 1
104 PolyStarAdapter::Type::kPoly, // "sy": 2
105 };
106
107 const auto type = ParseDefault<size_t>(jstar["sy"], 0) - 1;
108 if (type >= SK_ARRAY_COUNT(gTypes)) {
Florin Malita57b9d402018-10-02 12:48:00 -0400109 abuilder->log(Logger::Level::kError, &jstar, "Unknown polystar type.");
Florin Malita1b1dead2018-08-21 14:34:02 -0400110 return nullptr;
111 }
112
113 auto path_node = sksg::Path::Make();
114 auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
115
Florin Malita471a9462018-08-25 20:22:34 -0400116 abuilder->bindProperty<VectorValue>(jstar["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400117 [adapter](const VectorValue& p) {
118 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
119 });
Florin Malita471a9462018-08-25 20:22:34 -0400120 abuilder->bindProperty<ScalarValue>(jstar["pt"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400121 [adapter](const ScalarValue& pt) {
122 adapter->setPointCount(pt);
123 });
Florin Malita471a9462018-08-25 20:22:34 -0400124 abuilder->bindProperty<ScalarValue>(jstar["ir"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400125 [adapter](const ScalarValue& ir) {
126 adapter->setInnerRadius(ir);
127 });
Florin Malita471a9462018-08-25 20:22:34 -0400128 abuilder->bindProperty<ScalarValue>(jstar["or"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400129 [adapter](const ScalarValue& otr) {
130 adapter->setOuterRadius(otr);
131 });
Florin Malita471a9462018-08-25 20:22:34 -0400132 abuilder->bindProperty<ScalarValue>(jstar["is"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400133 [adapter](const ScalarValue& is) {
134 adapter->setInnerRoundness(is);
135 });
Florin Malita471a9462018-08-25 20:22:34 -0400136 abuilder->bindProperty<ScalarValue>(jstar["os"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400137 [adapter](const ScalarValue& os) {
138 adapter->setOuterRoundness(os);
139 });
Florin Malita471a9462018-08-25 20:22:34 -0400140 abuilder->bindProperty<ScalarValue>(jstar["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400141 [adapter](const ScalarValue& r) {
142 adapter->setRotation(r);
143 });
144
145 return std::move(path_node);
146}
147
Florin Malita471a9462018-08-25 20:22:34 -0400148sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad,
149 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400150 const skjson::ObjectValue* stops = jgrad["g"];
151 if (!stops)
152 return nullptr;
153
154 const auto stopCount = ParseDefault<int>((*stops)["p"], -1);
155 if (stopCount < 0)
156 return nullptr;
157
158 sk_sp<sksg::Gradient> gradient_node;
159 sk_sp<GradientAdapter> adapter;
160
161 if (ParseDefault<int>(jgrad["t"], 1) == 1) {
162 auto linear_node = sksg::LinearGradient::Make();
163 adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
164 gradient_node = std::move(linear_node);
165 } else {
166 auto radial_node = sksg::RadialGradient::Make();
167 adapter = sk_make_sp<RadialGradientAdapter>(radial_node, stopCount);
168
169 // TODO: highlight, angle
170 gradient_node = std::move(radial_node);
171 }
172
Florin Malita471a9462018-08-25 20:22:34 -0400173 abuilder->bindProperty<VectorValue>((*stops)["k"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400174 [adapter](const VectorValue& stops) {
175 adapter->setColorStops(stops);
176 });
Florin Malita471a9462018-08-25 20:22:34 -0400177 abuilder->bindProperty<VectorValue>(jgrad["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400178 [adapter](const VectorValue& s) {
179 adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
180 });
Florin Malita471a9462018-08-25 20:22:34 -0400181 abuilder->bindProperty<VectorValue>(jgrad["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400182 [adapter](const VectorValue& e) {
183 adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
184 });
185
186 return gradient_node;
187}
188
Florin Malita471a9462018-08-25 20:22:34 -0400189sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint,
190 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400191 sk_sp<sksg::PaintNode> paint_node) {
192 if (paint_node) {
193 paint_node->setAntiAlias(true);
194
Florin Malita471a9462018-08-25 20:22:34 -0400195 abuilder->bindProperty<ScalarValue>(jpaint["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400196 [paint_node](const ScalarValue& o) {
197 // BM opacity is [0..100]
198 paint_node->setOpacity(o * 0.01f);
199 });
200 }
201
202 return paint_node;
203}
204
Florin Malita471a9462018-08-25 20:22:34 -0400205sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke,
206 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400207 sk_sp<sksg::PaintNode> stroke_node) {
208 if (!stroke_node)
209 return nullptr;
210
211 stroke_node->setStyle(SkPaint::kStroke_Style);
212
Florin Malita471a9462018-08-25 20:22:34 -0400213 abuilder->bindProperty<ScalarValue>(jstroke["w"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400214 [stroke_node](const ScalarValue& w) {
215 stroke_node->setStrokeWidth(w);
216 });
217
218 stroke_node->setStrokeMiter(ParseDefault<SkScalar>(jstroke["ml"], 4.0f));
219
220 static constexpr SkPaint::Join gJoins[] = {
221 SkPaint::kMiter_Join,
222 SkPaint::kRound_Join,
223 SkPaint::kBevel_Join,
224 };
225 stroke_node->setStrokeJoin(gJoins[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lj"], 1) - 1,
226 SK_ARRAY_COUNT(gJoins) - 1)]);
227
228 static constexpr SkPaint::Cap gCaps[] = {
229 SkPaint::kButt_Cap,
230 SkPaint::kRound_Cap,
231 SkPaint::kSquare_Cap,
232 };
233 stroke_node->setStrokeCap(gCaps[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lc"], 1) - 1,
234 SK_ARRAY_COUNT(gCaps) - 1)]);
235
236 return stroke_node;
237}
238
Florin Malita471a9462018-08-25 20:22:34 -0400239sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill,
240 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
241 return AttachPaint(jfill, abuilder, ascope, abuilder->attachColor(jfill, ascope, "c"));
Florin Malita1b1dead2018-08-21 14:34:02 -0400242}
243
Florin Malita471a9462018-08-25 20:22:34 -0400244sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill,
245 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
246 return AttachPaint(jfill, abuilder, ascope, AttachGradient(jfill, abuilder, ascope));
Florin Malita1b1dead2018-08-21 14:34:02 -0400247}
248
249sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke,
Florin Malita471a9462018-08-25 20:22:34 -0400250 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400251 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -0400252 return AttachStroke(jstroke, abuilder, ascope,
253 AttachPaint(jstroke, abuilder, ascope,
254 abuilder->attachColor(jstroke, ascope, "c")));
Florin Malita1b1dead2018-08-21 14:34:02 -0400255}
256
257sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
Florin Malita471a9462018-08-25 20:22:34 -0400258 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400259 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -0400260 return AttachStroke(jstroke, abuilder, ascope,
261 AttachPaint(jstroke, abuilder, ascope,
262 AttachGradient(jstroke, abuilder, ascope)));
Florin Malita1b1dead2018-08-21 14:34:02 -0400263}
264
265sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
266 std::vector<sksg::Merge::Rec> merge_recs;
267 merge_recs.reserve(geos.size());
268
Florin Malita928db922018-10-17 17:07:05 -0400269 for (auto& geo : geos) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400270 merge_recs.push_back(
271 {std::move(geo), merge_recs.empty() ? sksg::Merge::Mode::kMerge : mode});
272 }
273
274 return sksg::Merge::Make(std::move(merge_recs));
275}
276
277std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400278 const skjson::ObjectValue& jmerge, const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400279 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
280 static constexpr sksg::Merge::Mode gModes[] = {
281 sksg::Merge::Mode::kMerge, // "mm": 1
282 sksg::Merge::Mode::kUnion, // "mm": 2
283 sksg::Merge::Mode::kDifference, // "mm": 3
284 sksg::Merge::Mode::kIntersect, // "mm": 4
285 sksg::Merge::Mode::kXOR , // "mm": 5
286 };
287
288 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jmerge["mm"], 1) - 1,
289 SK_ARRAY_COUNT(gModes) - 1)];
290
291 std::vector<sk_sp<sksg::GeometryNode>> merged;
292 merged.push_back(Merge(std::move(geos), mode));
293
294 return merged;
295}
296
297std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400298 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400299 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
300
301 enum class Mode {
302 kMerged, // "m": 1
303 kSeparate, // "m": 2
304 } gModes[] = { Mode::kMerged, Mode::kSeparate };
305
306 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
307 SK_ARRAY_COUNT(gModes) - 1)];
308
309 std::vector<sk_sp<sksg::GeometryNode>> inputs;
310 if (mode == Mode::kMerged) {
311 inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge));
312 } else {
313 inputs = std::move(geos);
314 }
315
316 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
317 trimmed.reserve(inputs.size());
318 for (const auto& i : inputs) {
Florin Malita928db922018-10-17 17:07:05 -0400319 auto trimEffect = sksg::TrimEffect::Make(i);
Florin Malita1b1dead2018-08-21 14:34:02 -0400320 trimmed.push_back(trimEffect);
321
322 const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
Florin Malita471a9462018-08-25 20:22:34 -0400323 abuilder->bindProperty<ScalarValue>(jtrim["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400324 [adapter](const ScalarValue& s) {
325 adapter->setStart(s);
326 });
Florin Malita471a9462018-08-25 20:22:34 -0400327 abuilder->bindProperty<ScalarValue>(jtrim["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400328 [adapter](const ScalarValue& e) {
329 adapter->setEnd(e);
330 });
Florin Malita471a9462018-08-25 20:22:34 -0400331 abuilder->bindProperty<ScalarValue>(jtrim["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400332 [adapter](const ScalarValue& o) {
333 adapter->setOffset(o);
334 });
335 }
336
337 return trimmed;
338}
339
340std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400341 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400342 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
343
344 std::vector<sk_sp<sksg::GeometryNode>> rounded;
345 rounded.reserve(geos.size());
346
Florin Malita928db922018-10-17 17:07:05 -0400347 for (auto& g : geos) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400348 const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
349 rounded.push_back(roundEffect);
350
Florin Malita471a9462018-08-25 20:22:34 -0400351 abuilder->bindProperty<ScalarValue>(jtrim["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400352 [roundEffect](const ScalarValue& r) {
353 roundEffect->setRadius(r);
354 });
355 }
356
357 return rounded;
358}
359
Florin Malita8ec9a602019-02-01 17:59:25 -0500360std::vector<sk_sp<sksg::RenderNode>> AttachRepeaterDrawEffect(
361 const skjson::ObjectValue& jrepeater,
362 const AnimationBuilder* abuilder,
363 AnimatorScope* ascope,
364 std::vector<sk_sp<sksg::RenderNode>>&& draws) {
365
366 std::vector<sk_sp<sksg::RenderNode>> repeater_draws;
367
368 if (const skjson::ObjectValue* jtransform = jrepeater["tr"]) {
369 sk_sp<sksg::RenderNode> repeater_node;
370 if (draws.size() > 1) {
371 repeater_node = sksg::Group::Make(std::move(draws));
372 } else {
373 repeater_node = std::move(draws[0]);
374 }
375
376 const auto repeater_composite = (ParseDefault(jrepeater["m"], 1) == 1)
377 ? RepeaterAdapter::Composite::kAbove
378 : RepeaterAdapter::Composite::kBelow;
379
380 auto adapter = sk_make_sp<RepeaterAdapter>(std::move(repeater_node),
381 repeater_composite);
382
383 abuilder->bindProperty<ScalarValue>(jrepeater["c"], ascope,
384 [adapter](const ScalarValue& c) {
385 adapter->setCount(c);
386 });
387 abuilder->bindProperty<ScalarValue>(jrepeater["o"], ascope,
388 [adapter](const ScalarValue& o) {
389 adapter->setOffset(o);
390 });
391 abuilder->bindProperty<VectorValue>((*jtransform)["a"], ascope,
392 [adapter](const VectorValue& a) {
393 adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
394 });
395 abuilder->bindProperty<VectorValue>((*jtransform)["p"], ascope,
396 [adapter](const VectorValue& p) {
397 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
398 });
399 abuilder->bindProperty<VectorValue>((*jtransform)["s"], ascope,
400 [adapter](const VectorValue& s) {
401 adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
402 });
403 abuilder->bindProperty<ScalarValue>((*jtransform)["r"], ascope,
404 [adapter](const ScalarValue& r) {
405 adapter->setRotation(r);
406 });
407 abuilder->bindProperty<ScalarValue>((*jtransform)["so"], ascope,
408 [adapter](const ScalarValue& so) {
409 adapter->setStartOpacity(so);
410 });
411 abuilder->bindProperty<ScalarValue>((*jtransform)["eo"], ascope,
412 [adapter](const ScalarValue& eo) {
413 adapter->setEndOpacity(eo);
414 });
415
416 repeater_draws.reserve(1);
417 repeater_draws.push_back(adapter->root());
418 } else {
419 repeater_draws = std::move(draws);
420 }
421
422 return repeater_draws;
423}
424
Florin Malita471a9462018-08-25 20:22:34 -0400425using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&,
426 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400427static constexpr GeometryAttacherT gGeometryAttachers[] = {
428 AttachPathGeometry,
429 AttachRRectGeometry,
430 AttachEllipseGeometry,
431 AttachPolystarGeometry,
432};
433
Florin Malita471a9462018-08-25 20:22:34 -0400434using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&,
435 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400436static constexpr PaintAttacherT gPaintAttachers[] = {
437 AttachColorFill,
438 AttachColorStroke,
439 AttachGradientFill,
440 AttachGradientStroke,
441};
442
443using GeometryEffectAttacherT =
444 std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
Florin Malita471a9462018-08-25 20:22:34 -0400445 const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400446 std::vector<sk_sp<sksg::GeometryNode>>&&);
447static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
448 AttachMergeGeometryEffect,
449 AttachTrimGeometryEffect,
450 AttachRoundGeometryEffect,
451};
452
Florin Malita8ec9a602019-02-01 17:59:25 -0500453using DrawEffectAttacherT =
454 std::vector<sk_sp<sksg::RenderNode>> (*)(const skjson::ObjectValue&,
455 const AnimationBuilder*, AnimatorScope*,
456 std::vector<sk_sp<sksg::RenderNode>>&&);
457
458static constexpr DrawEffectAttacherT gDrawEffectAttachers[] = {
459 AttachRepeaterDrawEffect,
460};
461
Florin Malita1b1dead2018-08-21 14:34:02 -0400462enum class ShapeType {
463 kGeometry,
464 kGeometryEffect,
465 kPaint,
466 kGroup,
467 kTransform,
Florin Malita8ec9a602019-02-01 17:59:25 -0500468 kDrawEffect,
Florin Malita1b1dead2018-08-21 14:34:02 -0400469};
470
471struct ShapeInfo {
472 const char* fTypeString;
473 ShapeType fShapeType;
474 uint32_t fAttacherIndex; // index into respective attacher tables
475};
476
477const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) {
478 static constexpr ShapeInfo gShapeInfo[] = {
479 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
480 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
481 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
482 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
483 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
484 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
485 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
486 { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect
Florin Malita8ec9a602019-02-01 17:59:25 -0500487 { "rp", ShapeType::kDrawEffect , 0 }, // repeater -> AttachRepeaterDrawEffect
Florin Malita1b1dead2018-08-21 14:34:02 -0400488 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
489 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
490 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
491 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
492 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
493 };
494
495 const skjson::StringValue* type = jshape["ty"];
496 if (!type) {
497 return nullptr;
498 }
499
500 const auto* info = bsearch(type->begin(),
501 gShapeInfo,
502 SK_ARRAY_COUNT(gShapeInfo),
503 sizeof(ShapeInfo),
504 [](const void* key, const void* info) {
505 return strcmp(static_cast<const char*>(key),
506 static_cast<const ShapeInfo*>(info)->fTypeString);
507 });
508
509 return static_cast<const ShapeInfo*>(info);
510}
511
512struct GeometryEffectRec {
513 const skjson::ObjectValue& fJson;
514 GeometryEffectAttacherT fAttach;
515};
516
Florin Malitaa85f3a12018-09-24 17:24:59 -0400517} // namespace
518
519struct AnimationBuilder::AttachShapeContext {
520 AttachShapeContext(AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400521 std::vector<sk_sp<sksg::GeometryNode>>* geos,
522 std::vector<GeometryEffectRec>* effects,
523 size_t committedAnimators)
Florin Malitaa85f3a12018-09-24 17:24:59 -0400524 : fScope(ascope)
Florin Malita1b1dead2018-08-21 14:34:02 -0400525 , fGeometryStack(geos)
526 , fGeometryEffectStack(effects)
527 , fCommittedAnimators(committedAnimators) {}
528
529 AnimatorScope* fScope;
530 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
531 std::vector<GeometryEffectRec>* fGeometryEffectStack;
532 size_t fCommittedAnimators;
533};
534
Florin Malitaa85f3a12018-09-24 17:24:59 -0400535sk_sp<sksg::RenderNode> AnimationBuilder::attachShape(const skjson::ArrayValue* jshape,
536 AttachShapeContext* ctx) const {
Florin Malita1b1dead2018-08-21 14:34:02 -0400537 if (!jshape)
538 return nullptr;
539
Florin Malita471a9462018-08-25 20:22:34 -0400540 SkDEBUGCODE(const auto initialGeometryEffects = ctx->fGeometryEffectStack->size();)
Florin Malita1b1dead2018-08-21 14:34:02 -0400541
Florin Malita40c7c642018-09-08 20:09:37 -0400542 const skjson::ObjectValue* jtransform = nullptr;
Florin Malita1b1dead2018-08-21 14:34:02 -0400543
544 struct ShapeRec {
545 const skjson::ObjectValue& fJson;
546 const ShapeInfo& fInfo;
547 };
548
549 // First pass (bottom->top):
550 //
551 // * pick up the group transform and opacity
552 // * push local geometry effects onto the stack
553 // * store recs for next pass
554 //
555 std::vector<ShapeRec> recs;
556 for (size_t i = 0; i < jshape->size(); ++i) {
557 const skjson::ObjectValue* shape = (*jshape)[jshape->size() - 1 - i];
558 if (!shape) continue;
559
560 const auto* info = FindShapeInfo(*shape);
561 if (!info) {
Florin Malita57b9d402018-10-02 12:48:00 -0400562 this->log(Logger::Level::kError, &(*shape)["ty"], "Unknown shape.");
Florin Malita1b1dead2018-08-21 14:34:02 -0400563 continue;
564 }
565
566 recs.push_back({ *shape, *info });
567
568 switch (info->fShapeType) {
569 case ShapeType::kTransform:
Florin Malita40c7c642018-09-08 20:09:37 -0400570 // Just track the transform property for now -- we'll deal with it later.
571 jtransform = shape;
Florin Malita1b1dead2018-08-21 14:34:02 -0400572 break;
573 case ShapeType::kGeometryEffect:
574 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400575 ctx->fGeometryEffectStack->push_back(
Florin Malita1b1dead2018-08-21 14:34:02 -0400576 { *shape, gGeometryEffectAttachers[info->fAttacherIndex] });
577 break;
578 default:
579 break;
580 }
581 }
582
583 // Second pass (top -> bottom, after 2x reverse):
584 //
585 // * track local geometry
586 // * emit local paints
587 //
588 std::vector<sk_sp<sksg::GeometryNode>> geos;
589 std::vector<sk_sp<sksg::RenderNode >> draws;
590 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400591 const AutoPropertyTracker apt(this, rec->fJson);
592
Florin Malita1b1dead2018-08-21 14:34:02 -0400593 switch (rec->fInfo.fShapeType) {
594 case ShapeType::kGeometry: {
595 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
596 if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaa85f3a12018-09-24 17:24:59 -0400597 this,
Florin Malita471a9462018-08-25 20:22:34 -0400598 ctx->fScope)) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400599 geos.push_back(std::move(geo));
600 }
601 } break;
602 case ShapeType::kGeometryEffect: {
603 // Apply the current effect and pop from the stack.
604 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
605 if (!geos.empty()) {
606 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaa85f3a12018-09-24 17:24:59 -0400607 this,
Florin Malita471a9462018-08-25 20:22:34 -0400608 ctx->fScope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400609 std::move(geos));
610 }
611
Florin Malita471a9462018-08-25 20:22:34 -0400612 SkASSERT(&ctx->fGeometryEffectStack->back().fJson == &rec->fJson);
613 SkASSERT(ctx->fGeometryEffectStack->back().fAttach ==
Florin Malita1b1dead2018-08-21 14:34:02 -0400614 gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
Florin Malita471a9462018-08-25 20:22:34 -0400615 ctx->fGeometryEffectStack->pop_back();
Florin Malita1b1dead2018-08-21 14:34:02 -0400616 } break;
617 case ShapeType::kGroup: {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400618 AttachShapeContext groupShapeCtx(ctx->fScope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400619 &geos,
Florin Malita471a9462018-08-25 20:22:34 -0400620 ctx->fGeometryEffectStack,
621 ctx->fCommittedAnimators);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400622 if (auto subgroup = this->attachShape(rec->fJson["it"], &groupShapeCtx)) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400623 draws.push_back(std::move(subgroup));
Florin Malita471a9462018-08-25 20:22:34 -0400624 SkASSERT(groupShapeCtx.fCommittedAnimators >= ctx->fCommittedAnimators);
625 ctx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita1b1dead2018-08-21 14:34:02 -0400626 }
627 } break;
628 case ShapeType::kPaint: {
629 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400630 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaa85f3a12018-09-24 17:24:59 -0400631 this,
Florin Malita471a9462018-08-25 20:22:34 -0400632 ctx->fScope);
Florin Malita1b1dead2018-08-21 14:34:02 -0400633 if (!paint || geos.empty())
634 break;
635
636 auto drawGeos = geos;
637
638 // Apply all pending effects from the stack.
Florin Malita471a9462018-08-25 20:22:34 -0400639 for (auto it = ctx->fGeometryEffectStack->rbegin();
640 it != ctx->fGeometryEffectStack->rend(); ++it) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400641 drawGeos = it->fAttach(it->fJson, this, ctx->fScope, std::move(drawGeos));
Florin Malita1b1dead2018-08-21 14:34:02 -0400642 }
643
644 // If we still have multiple geos, reduce using 'merge'.
645 auto geo = drawGeos.size() > 1
646 ? Merge(std::move(drawGeos), sksg::Merge::Mode::kMerge)
647 : drawGeos[0];
648
649 SkASSERT(geo);
650 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malita471a9462018-08-25 20:22:34 -0400651 ctx->fCommittedAnimators = ctx->fScope->size();
Florin Malita1b1dead2018-08-21 14:34:02 -0400652 } break;
Florin Malita8ec9a602019-02-01 17:59:25 -0500653 case ShapeType::kDrawEffect: {
654 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gDrawEffectAttachers));
655 if (!draws.empty()) {
656 draws = gDrawEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
657 this,
658 ctx->fScope,
659 std::move(draws));
660 ctx->fCommittedAnimators = ctx->fScope->size();
661 }
662 } break;
Florin Malita1b1dead2018-08-21 14:34:02 -0400663 default:
664 break;
665 }
666 }
667
668 // By now we should have popped all local geometry effects.
Florin Malita471a9462018-08-25 20:22:34 -0400669 SkASSERT(ctx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita1b1dead2018-08-21 14:34:02 -0400670
Florin Malita40c7c642018-09-08 20:09:37 -0400671 sk_sp<sksg::RenderNode> shape_wrapper;
672 if (draws.size() == 1) {
673 // For a single draw, we don't need a group.
674 shape_wrapper = std::move(draws.front());
675 } else if (!draws.empty()) {
Florin Malita40c7c642018-09-08 20:09:37 -0400676 // Emit local draws reversed (bottom->top, per spec).
Florin Malitacd9d0742018-09-10 09:57:15 -0400677 std::reverse(draws.begin(), draws.end());
678 draws.shrink_to_fit();
Florin Malita40c7c642018-09-08 20:09:37 -0400679
Florin Malitacd9d0742018-09-10 09:57:15 -0400680 // We need a group to dispatch multiple draws.
681 shape_wrapper = sksg::Group::Make(std::move(draws));
Florin Malita40c7c642018-09-08 20:09:37 -0400682 }
683
Florin Malita919e2092019-01-09 15:37:57 -0500684 sk_sp<sksg::Transform> shape_transform;
Florin Malita40c7c642018-09-08 20:09:37 -0400685 if (jtransform) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400686 const AutoPropertyTracker apt(this, *jtransform);
687
Florin Malita40c7c642018-09-08 20:09:37 -0400688 // This is tricky due to the interaction with ctx->fCommittedAnimators: we want any
689 // animators related to tranform/opacity to be committed => they must be inserted in front
690 // of the dangling/uncommitted ones.
691 AnimatorScope local_scope;
692
Florin Malita919e2092019-01-09 15:37:57 -0500693 if ((shape_transform = this->attachMatrix2D(*jtransform, &local_scope, nullptr))) {
694 shape_wrapper = sksg::TransformEffect::Make(std::move(shape_wrapper), shape_transform);
Florin Malita40c7c642018-09-08 20:09:37 -0400695 }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400696 shape_wrapper = this->attachOpacity(*jtransform, &local_scope, std::move(shape_wrapper));
Florin Malita40c7c642018-09-08 20:09:37 -0400697
698 ctx->fScope->insert(ctx->fScope->begin() + ctx->fCommittedAnimators,
699 std::make_move_iterator(local_scope.begin()),
700 std::make_move_iterator(local_scope.end()));
701 ctx->fCommittedAnimators += local_scope.size();
702 }
703
Florin Malita1b1dead2018-08-21 14:34:02 -0400704 // Push transformed local geometries to parent list, for subsequent paints.
Florin Malita928db922018-10-17 17:07:05 -0400705 for (auto& geo : geos) {
Florin Malita919e2092019-01-09 15:37:57 -0500706 ctx->fGeometryStack->push_back(shape_transform
707 ? sksg::GeometryTransform::Make(std::move(geo), shape_transform)
Florin Malita1b1dead2018-08-21 14:34:02 -0400708 : std::move(geo));
709 }
710
Florin Malita40c7c642018-09-08 20:09:37 -0400711 return shape_wrapper;
Florin Malita1b1dead2018-08-21 14:34:02 -0400712}
713
Florin Malita1b1dead2018-08-21 14:34:02 -0400714sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
Florin Malita62c6bd92018-10-03 14:39:56 -0400715 const LayerInfo&,
Florin Malita471a9462018-08-25 20:22:34 -0400716 AnimatorScope* ascope) const {
Florin Malita1b1dead2018-08-21 14:34:02 -0400717 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
718 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400719 AttachShapeContext shapeCtx(ascope, &geometryStack, &geometryEffectStack, ascope->size());
720 auto shapeNode = this->attachShape(layer["shapes"], &shapeCtx);
Florin Malita1b1dead2018-08-21 14:34:02 -0400721
722 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
723 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
724 // due to attached animators. To avoid this, we track committed animators and discard the
725 // orphans here.
726 SkASSERT(shapeCtx.fCommittedAnimators <= ascope->size());
727 ascope->resize(shapeCtx.fCommittedAnimators);
728
729 return shapeNode;
730}
731
732} // namespace internal
733} // namespace skottie