blob: 0da8b15c39112826609b67af9c750ea4fa45998e [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
27namespace skottie {
28namespace internal {
29
30namespace {
31
32sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath,
Florin Malita471a9462018-08-25 20:22:34 -040033 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040034 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -040035 return abuilder->attachPath(jpath["ks"], ascope);
Florin Malita1b1dead2018-08-21 14:34:02 -040036}
37
38sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
Florin Malita471a9462018-08-25 20:22:34 -040039 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040040 AnimatorScope* ascope) {
41 auto rect_node = sksg::RRect::Make();
42 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
43
Florin Malita471a9462018-08-25 20:22:34 -040044 auto p_attached = abuilder->bindProperty<VectorValue>(jrect["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040045 [adapter](const VectorValue& p) {
46 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
47 });
Florin Malita471a9462018-08-25 20:22:34 -040048 auto s_attached = abuilder->bindProperty<VectorValue>(jrect["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040049 [adapter](const VectorValue& s) {
50 adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
51 });
Florin Malita471a9462018-08-25 20:22:34 -040052 auto r_attached = abuilder->bindProperty<ScalarValue>(jrect["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040053 [adapter](const ScalarValue& r) {
54 adapter->setRadius(SkSize::Make(r, r));
55 });
56
57 if (!p_attached && !s_attached && !r_attached) {
58 return nullptr;
59 }
60
61 return std::move(rect_node);
62}
63
64sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
Florin Malita471a9462018-08-25 20:22:34 -040065 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040066 AnimatorScope* ascope) {
67 auto rect_node = sksg::RRect::Make();
68 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
69
Florin Malita471a9462018-08-25 20:22:34 -040070 auto p_attached = abuilder->bindProperty<VectorValue>(jellipse["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040071 [adapter](const VectorValue& p) {
72 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
73 });
Florin Malita471a9462018-08-25 20:22:34 -040074 auto s_attached = abuilder->bindProperty<VectorValue>(jellipse["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040075 [adapter](const VectorValue& s) {
76 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
77 adapter->setSize(sz);
78 adapter->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
79 });
80
81 if (!p_attached && !s_attached) {
82 return nullptr;
83 }
84
85 return std::move(rect_node);
86}
87
88sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
Florin Malita471a9462018-08-25 20:22:34 -040089 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040090 AnimatorScope* ascope) {
91 static constexpr PolyStarAdapter::Type gTypes[] = {
92 PolyStarAdapter::Type::kStar, // "sy": 1
93 PolyStarAdapter::Type::kPoly, // "sy": 2
94 };
95
96 const auto type = ParseDefault<size_t>(jstar["sy"], 0) - 1;
97 if (type >= SK_ARRAY_COUNT(gTypes)) {
98 LogJSON(jstar, "!! Unknown polystar type");
99 return nullptr;
100 }
101
102 auto path_node = sksg::Path::Make();
103 auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
104
Florin Malita471a9462018-08-25 20:22:34 -0400105 abuilder->bindProperty<VectorValue>(jstar["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400106 [adapter](const VectorValue& p) {
107 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
108 });
Florin Malita471a9462018-08-25 20:22:34 -0400109 abuilder->bindProperty<ScalarValue>(jstar["pt"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400110 [adapter](const ScalarValue& pt) {
111 adapter->setPointCount(pt);
112 });
Florin Malita471a9462018-08-25 20:22:34 -0400113 abuilder->bindProperty<ScalarValue>(jstar["ir"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400114 [adapter](const ScalarValue& ir) {
115 adapter->setInnerRadius(ir);
116 });
Florin Malita471a9462018-08-25 20:22:34 -0400117 abuilder->bindProperty<ScalarValue>(jstar["or"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400118 [adapter](const ScalarValue& otr) {
119 adapter->setOuterRadius(otr);
120 });
Florin Malita471a9462018-08-25 20:22:34 -0400121 abuilder->bindProperty<ScalarValue>(jstar["is"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400122 [adapter](const ScalarValue& is) {
123 adapter->setInnerRoundness(is);
124 });
Florin Malita471a9462018-08-25 20:22:34 -0400125 abuilder->bindProperty<ScalarValue>(jstar["os"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400126 [adapter](const ScalarValue& os) {
127 adapter->setOuterRoundness(os);
128 });
Florin Malita471a9462018-08-25 20:22:34 -0400129 abuilder->bindProperty<ScalarValue>(jstar["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400130 [adapter](const ScalarValue& r) {
131 adapter->setRotation(r);
132 });
133
134 return std::move(path_node);
135}
136
Florin Malita471a9462018-08-25 20:22:34 -0400137sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad,
138 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400139 const skjson::ObjectValue* stops = jgrad["g"];
140 if (!stops)
141 return nullptr;
142
143 const auto stopCount = ParseDefault<int>((*stops)["p"], -1);
144 if (stopCount < 0)
145 return nullptr;
146
147 sk_sp<sksg::Gradient> gradient_node;
148 sk_sp<GradientAdapter> adapter;
149
150 if (ParseDefault<int>(jgrad["t"], 1) == 1) {
151 auto linear_node = sksg::LinearGradient::Make();
152 adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
153 gradient_node = std::move(linear_node);
154 } else {
155 auto radial_node = sksg::RadialGradient::Make();
156 adapter = sk_make_sp<RadialGradientAdapter>(radial_node, stopCount);
157
158 // TODO: highlight, angle
159 gradient_node = std::move(radial_node);
160 }
161
Florin Malita471a9462018-08-25 20:22:34 -0400162 abuilder->bindProperty<VectorValue>((*stops)["k"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400163 [adapter](const VectorValue& stops) {
164 adapter->setColorStops(stops);
165 });
Florin Malita471a9462018-08-25 20:22:34 -0400166 abuilder->bindProperty<VectorValue>(jgrad["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400167 [adapter](const VectorValue& s) {
168 adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
169 });
Florin Malita471a9462018-08-25 20:22:34 -0400170 abuilder->bindProperty<VectorValue>(jgrad["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400171 [adapter](const VectorValue& e) {
172 adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
173 });
174
175 return gradient_node;
176}
177
Florin Malita471a9462018-08-25 20:22:34 -0400178sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint,
179 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400180 sk_sp<sksg::PaintNode> paint_node) {
181 if (paint_node) {
182 paint_node->setAntiAlias(true);
183
Florin Malita471a9462018-08-25 20:22:34 -0400184 abuilder->bindProperty<ScalarValue>(jpaint["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400185 [paint_node](const ScalarValue& o) {
186 // BM opacity is [0..100]
187 paint_node->setOpacity(o * 0.01f);
188 });
189 }
190
191 return paint_node;
192}
193
Florin Malita471a9462018-08-25 20:22:34 -0400194sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke,
195 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400196 sk_sp<sksg::PaintNode> stroke_node) {
197 if (!stroke_node)
198 return nullptr;
199
200 stroke_node->setStyle(SkPaint::kStroke_Style);
201
Florin Malita471a9462018-08-25 20:22:34 -0400202 abuilder->bindProperty<ScalarValue>(jstroke["w"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400203 [stroke_node](const ScalarValue& w) {
204 stroke_node->setStrokeWidth(w);
205 });
206
207 stroke_node->setStrokeMiter(ParseDefault<SkScalar>(jstroke["ml"], 4.0f));
208
209 static constexpr SkPaint::Join gJoins[] = {
210 SkPaint::kMiter_Join,
211 SkPaint::kRound_Join,
212 SkPaint::kBevel_Join,
213 };
214 stroke_node->setStrokeJoin(gJoins[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lj"], 1) - 1,
215 SK_ARRAY_COUNT(gJoins) - 1)]);
216
217 static constexpr SkPaint::Cap gCaps[] = {
218 SkPaint::kButt_Cap,
219 SkPaint::kRound_Cap,
220 SkPaint::kSquare_Cap,
221 };
222 stroke_node->setStrokeCap(gCaps[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lc"], 1) - 1,
223 SK_ARRAY_COUNT(gCaps) - 1)]);
224
225 return stroke_node;
226}
227
Florin Malita471a9462018-08-25 20:22:34 -0400228sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill,
229 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
230 return AttachPaint(jfill, abuilder, ascope, abuilder->attachColor(jfill, ascope, "c"));
Florin Malita1b1dead2018-08-21 14:34:02 -0400231}
232
Florin Malita471a9462018-08-25 20:22:34 -0400233sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill,
234 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
235 return AttachPaint(jfill, abuilder, ascope, AttachGradient(jfill, abuilder, ascope));
Florin Malita1b1dead2018-08-21 14:34:02 -0400236}
237
238sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke,
Florin Malita471a9462018-08-25 20:22:34 -0400239 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400240 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -0400241 return AttachStroke(jstroke, abuilder, ascope,
242 AttachPaint(jstroke, abuilder, ascope,
243 abuilder->attachColor(jstroke, ascope, "c")));
Florin Malita1b1dead2018-08-21 14:34:02 -0400244}
245
246sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
Florin Malita471a9462018-08-25 20:22:34 -0400247 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400248 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -0400249 return AttachStroke(jstroke, abuilder, ascope,
250 AttachPaint(jstroke, abuilder, ascope,
251 AttachGradient(jstroke, abuilder, ascope)));
Florin Malita1b1dead2018-08-21 14:34:02 -0400252}
253
254sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
255 std::vector<sksg::Merge::Rec> merge_recs;
256 merge_recs.reserve(geos.size());
257
258 for (const auto& geo : geos) {
259 merge_recs.push_back(
260 {std::move(geo), merge_recs.empty() ? sksg::Merge::Mode::kMerge : mode});
261 }
262
263 return sksg::Merge::Make(std::move(merge_recs));
264}
265
266std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400267 const skjson::ObjectValue& jmerge, const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400268 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
269 static constexpr sksg::Merge::Mode gModes[] = {
270 sksg::Merge::Mode::kMerge, // "mm": 1
271 sksg::Merge::Mode::kUnion, // "mm": 2
272 sksg::Merge::Mode::kDifference, // "mm": 3
273 sksg::Merge::Mode::kIntersect, // "mm": 4
274 sksg::Merge::Mode::kXOR , // "mm": 5
275 };
276
277 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jmerge["mm"], 1) - 1,
278 SK_ARRAY_COUNT(gModes) - 1)];
279
280 std::vector<sk_sp<sksg::GeometryNode>> merged;
281 merged.push_back(Merge(std::move(geos), mode));
282
283 return merged;
284}
285
286std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400287 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400288 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
289
290 enum class Mode {
291 kMerged, // "m": 1
292 kSeparate, // "m": 2
293 } gModes[] = { Mode::kMerged, Mode::kSeparate };
294
295 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
296 SK_ARRAY_COUNT(gModes) - 1)];
297
298 std::vector<sk_sp<sksg::GeometryNode>> inputs;
299 if (mode == Mode::kMerged) {
300 inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge));
301 } else {
302 inputs = std::move(geos);
303 }
304
305 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
306 trimmed.reserve(inputs.size());
307 for (const auto& i : inputs) {
308 const auto trimEffect = sksg::TrimEffect::Make(i);
309 trimmed.push_back(trimEffect);
310
311 const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
Florin Malita471a9462018-08-25 20:22:34 -0400312 abuilder->bindProperty<ScalarValue>(jtrim["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400313 [adapter](const ScalarValue& s) {
314 adapter->setStart(s);
315 });
Florin Malita471a9462018-08-25 20:22:34 -0400316 abuilder->bindProperty<ScalarValue>(jtrim["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400317 [adapter](const ScalarValue& e) {
318 adapter->setEnd(e);
319 });
Florin Malita471a9462018-08-25 20:22:34 -0400320 abuilder->bindProperty<ScalarValue>(jtrim["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400321 [adapter](const ScalarValue& o) {
322 adapter->setOffset(o);
323 });
324 }
325
326 return trimmed;
327}
328
329std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400330 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400331 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
332
333 std::vector<sk_sp<sksg::GeometryNode>> rounded;
334 rounded.reserve(geos.size());
335
336 for (const auto& g : geos) {
337 const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
338 rounded.push_back(roundEffect);
339
Florin Malita471a9462018-08-25 20:22:34 -0400340 abuilder->bindProperty<ScalarValue>(jtrim["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400341 [roundEffect](const ScalarValue& r) {
342 roundEffect->setRadius(r);
343 });
344 }
345
346 return rounded;
347}
348
Florin Malita471a9462018-08-25 20:22:34 -0400349using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&,
350 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400351static constexpr GeometryAttacherT gGeometryAttachers[] = {
352 AttachPathGeometry,
353 AttachRRectGeometry,
354 AttachEllipseGeometry,
355 AttachPolystarGeometry,
356};
357
Florin Malita471a9462018-08-25 20:22:34 -0400358using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&,
359 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400360static constexpr PaintAttacherT gPaintAttachers[] = {
361 AttachColorFill,
362 AttachColorStroke,
363 AttachGradientFill,
364 AttachGradientStroke,
365};
366
367using GeometryEffectAttacherT =
368 std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
Florin Malita471a9462018-08-25 20:22:34 -0400369 const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400370 std::vector<sk_sp<sksg::GeometryNode>>&&);
371static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
372 AttachMergeGeometryEffect,
373 AttachTrimGeometryEffect,
374 AttachRoundGeometryEffect,
375};
376
377enum class ShapeType {
378 kGeometry,
379 kGeometryEffect,
380 kPaint,
381 kGroup,
382 kTransform,
383};
384
385struct ShapeInfo {
386 const char* fTypeString;
387 ShapeType fShapeType;
388 uint32_t fAttacherIndex; // index into respective attacher tables
389};
390
391const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) {
392 static constexpr ShapeInfo gShapeInfo[] = {
393 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
394 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
395 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
396 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
397 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
398 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
399 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
400 { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect
401 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
402 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
403 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
404 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
405 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
406 };
407
408 const skjson::StringValue* type = jshape["ty"];
409 if (!type) {
410 return nullptr;
411 }
412
413 const auto* info = bsearch(type->begin(),
414 gShapeInfo,
415 SK_ARRAY_COUNT(gShapeInfo),
416 sizeof(ShapeInfo),
417 [](const void* key, const void* info) {
418 return strcmp(static_cast<const char*>(key),
419 static_cast<const ShapeInfo*>(info)->fTypeString);
420 });
421
422 return static_cast<const ShapeInfo*>(info);
423}
424
425struct GeometryEffectRec {
426 const skjson::ObjectValue& fJson;
427 GeometryEffectAttacherT fAttach;
428};
429
430struct AttachShapeContext {
Florin Malita471a9462018-08-25 20:22:34 -0400431 AttachShapeContext(const AnimationBuilder* abuilder,
432 AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400433 std::vector<sk_sp<sksg::GeometryNode>>* geos,
434 std::vector<GeometryEffectRec>* effects,
435 size_t committedAnimators)
Florin Malita471a9462018-08-25 20:22:34 -0400436 : fBuilder(abuilder)
437 , fScope(ascope)
Florin Malita1b1dead2018-08-21 14:34:02 -0400438 , fGeometryStack(geos)
439 , fGeometryEffectStack(effects)
440 , fCommittedAnimators(committedAnimators) {}
441
Florin Malita471a9462018-08-25 20:22:34 -0400442 const AnimationBuilder* fBuilder;
Florin Malita1b1dead2018-08-21 14:34:02 -0400443 AnimatorScope* fScope;
444 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
445 std::vector<GeometryEffectRec>* fGeometryEffectStack;
446 size_t fCommittedAnimators;
447};
448
Florin Malita471a9462018-08-25 20:22:34 -0400449sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape, AttachShapeContext* ctx) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400450 if (!jshape)
451 return nullptr;
452
Florin Malita471a9462018-08-25 20:22:34 -0400453 SkDEBUGCODE(const auto initialGeometryEffects = ctx->fGeometryEffectStack->size();)
Florin Malita1b1dead2018-08-21 14:34:02 -0400454
455 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
456 sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
457 sk_sp<sksg::Matrix> shape_matrix;
458
459 struct ShapeRec {
460 const skjson::ObjectValue& fJson;
461 const ShapeInfo& fInfo;
462 };
463
464 // First pass (bottom->top):
465 //
466 // * pick up the group transform and opacity
467 // * push local geometry effects onto the stack
468 // * store recs for next pass
469 //
470 std::vector<ShapeRec> recs;
471 for (size_t i = 0; i < jshape->size(); ++i) {
472 const skjson::ObjectValue* shape = (*jshape)[jshape->size() - 1 - i];
473 if (!shape) continue;
474
475 const auto* info = FindShapeInfo(*shape);
476 if (!info) {
477 LogJSON((*shape)["ty"], "!! Unknown shape");
478 continue;
479 }
480
481 recs.push_back({ *shape, *info });
482
483 switch (info->fShapeType) {
484 case ShapeType::kTransform:
Florin Malita471a9462018-08-25 20:22:34 -0400485 if ((shape_matrix = ctx->fBuilder->attachMatrix(*shape, ctx->fScope, nullptr))) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400486 shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
487 }
Florin Malita471a9462018-08-25 20:22:34 -0400488 shape_wrapper = ctx->fBuilder->attachOpacity(*shape, ctx->fScope,
489 std::move(shape_wrapper));
Florin Malita1b1dead2018-08-21 14:34:02 -0400490 break;
491 case ShapeType::kGeometryEffect:
492 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400493 ctx->fGeometryEffectStack->push_back(
Florin Malita1b1dead2018-08-21 14:34:02 -0400494 { *shape, gGeometryEffectAttachers[info->fAttacherIndex] });
495 break;
496 default:
497 break;
498 }
499 }
500
501 // Second pass (top -> bottom, after 2x reverse):
502 //
503 // * track local geometry
504 // * emit local paints
505 //
506 std::vector<sk_sp<sksg::GeometryNode>> geos;
507 std::vector<sk_sp<sksg::RenderNode >> draws;
508 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
509 switch (rec->fInfo.fShapeType) {
510 case ShapeType::kGeometry: {
511 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
512 if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malita471a9462018-08-25 20:22:34 -0400513 ctx->fBuilder,
514 ctx->fScope)) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400515 geos.push_back(std::move(geo));
516 }
517 } break;
518 case ShapeType::kGeometryEffect: {
519 // Apply the current effect and pop from the stack.
520 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
521 if (!geos.empty()) {
522 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malita471a9462018-08-25 20:22:34 -0400523 ctx->fBuilder,
524 ctx->fScope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400525 std::move(geos));
526 }
527
Florin Malita471a9462018-08-25 20:22:34 -0400528 SkASSERT(&ctx->fGeometryEffectStack->back().fJson == &rec->fJson);
529 SkASSERT(ctx->fGeometryEffectStack->back().fAttach ==
Florin Malita1b1dead2018-08-21 14:34:02 -0400530 gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
Florin Malita471a9462018-08-25 20:22:34 -0400531 ctx->fGeometryEffectStack->pop_back();
Florin Malita1b1dead2018-08-21 14:34:02 -0400532 } break;
533 case ShapeType::kGroup: {
Florin Malita471a9462018-08-25 20:22:34 -0400534 AttachShapeContext groupShapeCtx(ctx->fBuilder,
535 ctx->fScope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400536 &geos,
Florin Malita471a9462018-08-25 20:22:34 -0400537 ctx->fGeometryEffectStack,
538 ctx->fCommittedAnimators);
Florin Malita1b1dead2018-08-21 14:34:02 -0400539 if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
540 draws.push_back(std::move(subgroup));
Florin Malita471a9462018-08-25 20:22:34 -0400541 SkASSERT(groupShapeCtx.fCommittedAnimators >= ctx->fCommittedAnimators);
542 ctx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita1b1dead2018-08-21 14:34:02 -0400543 }
544 } break;
545 case ShapeType::kPaint: {
546 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400547 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
548 ctx->fBuilder,
549 ctx->fScope);
Florin Malita1b1dead2018-08-21 14:34:02 -0400550 if (!paint || geos.empty())
551 break;
552
553 auto drawGeos = geos;
554
555 // Apply all pending effects from the stack.
Florin Malita471a9462018-08-25 20:22:34 -0400556 for (auto it = ctx->fGeometryEffectStack->rbegin();
557 it != ctx->fGeometryEffectStack->rend(); ++it) {
558 drawGeos = it->fAttach(it->fJson, ctx->fBuilder, ctx->fScope, std::move(drawGeos));
Florin Malita1b1dead2018-08-21 14:34:02 -0400559 }
560
561 // If we still have multiple geos, reduce using 'merge'.
562 auto geo = drawGeos.size() > 1
563 ? Merge(std::move(drawGeos), sksg::Merge::Mode::kMerge)
564 : drawGeos[0];
565
566 SkASSERT(geo);
567 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malita471a9462018-08-25 20:22:34 -0400568 ctx->fCommittedAnimators = ctx->fScope->size();
Florin Malita1b1dead2018-08-21 14:34:02 -0400569 } break;
570 default:
571 break;
572 }
573 }
574
575 // By now we should have popped all local geometry effects.
Florin Malita471a9462018-08-25 20:22:34 -0400576 SkASSERT(ctx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita1b1dead2018-08-21 14:34:02 -0400577
578 // Push transformed local geometries to parent list, for subsequent paints.
579 for (const auto& geo : geos) {
Florin Malita471a9462018-08-25 20:22:34 -0400580 ctx->fGeometryStack->push_back(shape_matrix
Florin Malita1b1dead2018-08-21 14:34:02 -0400581 ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
582 : std::move(geo));
583 }
584
585 // Emit local draws reversed (bottom->top, per spec).
586 for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
587 shape_group->addChild(std::move(*it));
588 }
Florin Malitaca858b62018-09-02 13:44:13 -0400589 shape_group->shrink_to_fit();
Florin Malita1b1dead2018-08-21 14:34:02 -0400590
591 return draws.empty() ? nullptr : shape_wrapper;
592}
593
594} // namespace
595
596sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
Florin Malita471a9462018-08-25 20:22:34 -0400597 AnimatorScope* ascope) const {
Florin Malita1b1dead2018-08-21 14:34:02 -0400598 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
599 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malita471a9462018-08-25 20:22:34 -0400600 AttachShapeContext shapeCtx(this, ascope, &geometryStack, &geometryEffectStack, ascope->size());
Florin Malita1b1dead2018-08-21 14:34:02 -0400601 auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
602
603 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
604 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
605 // due to attached animators. To avoid this, we track committed animators and discard the
606 // orphans here.
607 SkASSERT(shapeCtx.fCommittedAnimators <= ascope->size());
608 ascope->resize(shapeCtx.fCommittedAnimators);
609
610 return shapeNode;
611}
612
613} // namespace internal
614} // namespace skottie