blob: 2731b75552364b2877f72cfdc67b421b9af8debe [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();
45 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
46
Florin Malita471a9462018-08-25 20:22:34 -040047 auto p_attached = abuilder->bindProperty<VectorValue>(jrect["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040048 [adapter](const VectorValue& p) {
49 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
50 });
Florin Malita471a9462018-08-25 20:22:34 -040051 auto s_attached = abuilder->bindProperty<VectorValue>(jrect["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040052 [adapter](const VectorValue& s) {
53 adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
54 });
Florin Malita471a9462018-08-25 20:22:34 -040055 auto r_attached = abuilder->bindProperty<ScalarValue>(jrect["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040056 [adapter](const ScalarValue& r) {
57 adapter->setRadius(SkSize::Make(r, r));
58 });
59
60 if (!p_attached && !s_attached && !r_attached) {
61 return nullptr;
62 }
63
64 return std::move(rect_node);
65}
66
67sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
Florin Malita471a9462018-08-25 20:22:34 -040068 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040069 AnimatorScope* ascope) {
70 auto rect_node = sksg::RRect::Make();
71 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
72
Florin Malita471a9462018-08-25 20:22:34 -040073 auto p_attached = abuilder->bindProperty<VectorValue>(jellipse["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040074 [adapter](const VectorValue& p) {
75 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
76 });
Florin Malita471a9462018-08-25 20:22:34 -040077 auto s_attached = abuilder->bindProperty<VectorValue>(jellipse["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -040078 [adapter](const VectorValue& s) {
79 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
80 adapter->setSize(sz);
81 adapter->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
82 });
83
84 if (!p_attached && !s_attached) {
85 return nullptr;
86 }
87
88 return std::move(rect_node);
89}
90
91sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
Florin Malita471a9462018-08-25 20:22:34 -040092 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -040093 AnimatorScope* ascope) {
94 static constexpr PolyStarAdapter::Type gTypes[] = {
95 PolyStarAdapter::Type::kStar, // "sy": 1
96 PolyStarAdapter::Type::kPoly, // "sy": 2
97 };
98
99 const auto type = ParseDefault<size_t>(jstar["sy"], 0) - 1;
100 if (type >= SK_ARRAY_COUNT(gTypes)) {
Florin Malita57b9d402018-10-02 12:48:00 -0400101 abuilder->log(Logger::Level::kError, &jstar, "Unknown polystar type.");
Florin Malita1b1dead2018-08-21 14:34:02 -0400102 return nullptr;
103 }
104
105 auto path_node = sksg::Path::Make();
106 auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
107
Florin Malita471a9462018-08-25 20:22:34 -0400108 abuilder->bindProperty<VectorValue>(jstar["p"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400109 [adapter](const VectorValue& p) {
110 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
111 });
Florin Malita471a9462018-08-25 20:22:34 -0400112 abuilder->bindProperty<ScalarValue>(jstar["pt"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400113 [adapter](const ScalarValue& pt) {
114 adapter->setPointCount(pt);
115 });
Florin Malita471a9462018-08-25 20:22:34 -0400116 abuilder->bindProperty<ScalarValue>(jstar["ir"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400117 [adapter](const ScalarValue& ir) {
118 adapter->setInnerRadius(ir);
119 });
Florin Malita471a9462018-08-25 20:22:34 -0400120 abuilder->bindProperty<ScalarValue>(jstar["or"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400121 [adapter](const ScalarValue& otr) {
122 adapter->setOuterRadius(otr);
123 });
Florin Malita471a9462018-08-25 20:22:34 -0400124 abuilder->bindProperty<ScalarValue>(jstar["is"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400125 [adapter](const ScalarValue& is) {
126 adapter->setInnerRoundness(is);
127 });
Florin Malita471a9462018-08-25 20:22:34 -0400128 abuilder->bindProperty<ScalarValue>(jstar["os"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400129 [adapter](const ScalarValue& os) {
130 adapter->setOuterRoundness(os);
131 });
Florin Malita471a9462018-08-25 20:22:34 -0400132 abuilder->bindProperty<ScalarValue>(jstar["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400133 [adapter](const ScalarValue& r) {
134 adapter->setRotation(r);
135 });
136
137 return std::move(path_node);
138}
139
Florin Malita471a9462018-08-25 20:22:34 -0400140sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad,
141 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400142 const skjson::ObjectValue* stops = jgrad["g"];
143 if (!stops)
144 return nullptr;
145
146 const auto stopCount = ParseDefault<int>((*stops)["p"], -1);
147 if (stopCount < 0)
148 return nullptr;
149
150 sk_sp<sksg::Gradient> gradient_node;
151 sk_sp<GradientAdapter> adapter;
152
153 if (ParseDefault<int>(jgrad["t"], 1) == 1) {
154 auto linear_node = sksg::LinearGradient::Make();
155 adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
156 gradient_node = std::move(linear_node);
157 } else {
158 auto radial_node = sksg::RadialGradient::Make();
159 adapter = sk_make_sp<RadialGradientAdapter>(radial_node, stopCount);
160
161 // TODO: highlight, angle
162 gradient_node = std::move(radial_node);
163 }
164
Florin Malita471a9462018-08-25 20:22:34 -0400165 abuilder->bindProperty<VectorValue>((*stops)["k"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400166 [adapter](const VectorValue& stops) {
167 adapter->setColorStops(stops);
168 });
Florin Malita471a9462018-08-25 20:22:34 -0400169 abuilder->bindProperty<VectorValue>(jgrad["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400170 [adapter](const VectorValue& s) {
171 adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
172 });
Florin Malita471a9462018-08-25 20:22:34 -0400173 abuilder->bindProperty<VectorValue>(jgrad["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400174 [adapter](const VectorValue& e) {
175 adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
176 });
177
178 return gradient_node;
179}
180
Florin Malita471a9462018-08-25 20:22:34 -0400181sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint,
182 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400183 sk_sp<sksg::PaintNode> paint_node) {
184 if (paint_node) {
185 paint_node->setAntiAlias(true);
186
Florin Malita471a9462018-08-25 20:22:34 -0400187 abuilder->bindProperty<ScalarValue>(jpaint["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400188 [paint_node](const ScalarValue& o) {
189 // BM opacity is [0..100]
190 paint_node->setOpacity(o * 0.01f);
191 });
192 }
193
194 return paint_node;
195}
196
Florin Malita471a9462018-08-25 20:22:34 -0400197sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke,
198 const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400199 sk_sp<sksg::PaintNode> stroke_node) {
200 if (!stroke_node)
201 return nullptr;
202
203 stroke_node->setStyle(SkPaint::kStroke_Style);
204
Florin Malita471a9462018-08-25 20:22:34 -0400205 abuilder->bindProperty<ScalarValue>(jstroke["w"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400206 [stroke_node](const ScalarValue& w) {
207 stroke_node->setStrokeWidth(w);
208 });
209
210 stroke_node->setStrokeMiter(ParseDefault<SkScalar>(jstroke["ml"], 4.0f));
211
212 static constexpr SkPaint::Join gJoins[] = {
213 SkPaint::kMiter_Join,
214 SkPaint::kRound_Join,
215 SkPaint::kBevel_Join,
216 };
217 stroke_node->setStrokeJoin(gJoins[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lj"], 1) - 1,
218 SK_ARRAY_COUNT(gJoins) - 1)]);
219
220 static constexpr SkPaint::Cap gCaps[] = {
221 SkPaint::kButt_Cap,
222 SkPaint::kRound_Cap,
223 SkPaint::kSquare_Cap,
224 };
225 stroke_node->setStrokeCap(gCaps[SkTMin<size_t>(ParseDefault<size_t>(jstroke["lc"], 1) - 1,
226 SK_ARRAY_COUNT(gCaps) - 1)]);
227
228 return stroke_node;
229}
230
Florin Malita471a9462018-08-25 20:22:34 -0400231sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill,
232 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
233 return AttachPaint(jfill, abuilder, ascope, abuilder->attachColor(jfill, ascope, "c"));
Florin Malita1b1dead2018-08-21 14:34:02 -0400234}
235
Florin Malita471a9462018-08-25 20:22:34 -0400236sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill,
237 const AnimationBuilder* abuilder, AnimatorScope* ascope) {
238 return AttachPaint(jfill, abuilder, ascope, AttachGradient(jfill, abuilder, ascope));
Florin Malita1b1dead2018-08-21 14:34:02 -0400239}
240
241sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke,
Florin Malita471a9462018-08-25 20:22:34 -0400242 const AnimationBuilder* abuilder,
Florin Malita1b1dead2018-08-21 14:34:02 -0400243 AnimatorScope* ascope) {
Florin Malita471a9462018-08-25 20:22:34 -0400244 return AttachStroke(jstroke, abuilder, ascope,
245 AttachPaint(jstroke, abuilder, ascope,
246 abuilder->attachColor(jstroke, ascope, "c")));
Florin Malita1b1dead2018-08-21 14:34:02 -0400247}
248
249sk_sp<sksg::PaintNode> AttachGradientStroke(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 AttachGradient(jstroke, abuilder, ascope)));
Florin Malita1b1dead2018-08-21 14:34:02 -0400255}
256
257sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
258 std::vector<sksg::Merge::Rec> merge_recs;
259 merge_recs.reserve(geos.size());
260
Florin Malita928db922018-10-17 17:07:05 -0400261 for (auto& geo : geos) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400262 merge_recs.push_back(
263 {std::move(geo), merge_recs.empty() ? sksg::Merge::Mode::kMerge : mode});
264 }
265
266 return sksg::Merge::Make(std::move(merge_recs));
267}
268
269std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400270 const skjson::ObjectValue& jmerge, const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400271 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
272 static constexpr sksg::Merge::Mode gModes[] = {
273 sksg::Merge::Mode::kMerge, // "mm": 1
274 sksg::Merge::Mode::kUnion, // "mm": 2
275 sksg::Merge::Mode::kDifference, // "mm": 3
276 sksg::Merge::Mode::kIntersect, // "mm": 4
277 sksg::Merge::Mode::kXOR , // "mm": 5
278 };
279
280 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jmerge["mm"], 1) - 1,
281 SK_ARRAY_COUNT(gModes) - 1)];
282
283 std::vector<sk_sp<sksg::GeometryNode>> merged;
284 merged.push_back(Merge(std::move(geos), mode));
285
286 return merged;
287}
288
289std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400290 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400291 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
292
293 enum class Mode {
294 kMerged, // "m": 1
295 kSeparate, // "m": 2
296 } gModes[] = { Mode::kMerged, Mode::kSeparate };
297
298 const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
299 SK_ARRAY_COUNT(gModes) - 1)];
300
301 std::vector<sk_sp<sksg::GeometryNode>> inputs;
302 if (mode == Mode::kMerged) {
303 inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge));
304 } else {
305 inputs = std::move(geos);
306 }
307
308 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
309 trimmed.reserve(inputs.size());
310 for (const auto& i : inputs) {
Florin Malita928db922018-10-17 17:07:05 -0400311 auto trimEffect = sksg::TrimEffect::Make(i);
Florin Malita1b1dead2018-08-21 14:34:02 -0400312 trimmed.push_back(trimEffect);
313
314 const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
Florin Malita471a9462018-08-25 20:22:34 -0400315 abuilder->bindProperty<ScalarValue>(jtrim["s"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400316 [adapter](const ScalarValue& s) {
317 adapter->setStart(s);
318 });
Florin Malita471a9462018-08-25 20:22:34 -0400319 abuilder->bindProperty<ScalarValue>(jtrim["e"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400320 [adapter](const ScalarValue& e) {
321 adapter->setEnd(e);
322 });
Florin Malita471a9462018-08-25 20:22:34 -0400323 abuilder->bindProperty<ScalarValue>(jtrim["o"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400324 [adapter](const ScalarValue& o) {
325 adapter->setOffset(o);
326 });
327 }
328
329 return trimmed;
330}
331
332std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
Florin Malita471a9462018-08-25 20:22:34 -0400333 const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400334 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
335
336 std::vector<sk_sp<sksg::GeometryNode>> rounded;
337 rounded.reserve(geos.size());
338
Florin Malita928db922018-10-17 17:07:05 -0400339 for (auto& g : geos) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400340 const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
341 rounded.push_back(roundEffect);
342
Florin Malita471a9462018-08-25 20:22:34 -0400343 abuilder->bindProperty<ScalarValue>(jtrim["r"], ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400344 [roundEffect](const ScalarValue& r) {
345 roundEffect->setRadius(r);
346 });
347 }
348
349 return rounded;
350}
351
Florin Malita471a9462018-08-25 20:22:34 -0400352using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&,
353 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400354static constexpr GeometryAttacherT gGeometryAttachers[] = {
355 AttachPathGeometry,
356 AttachRRectGeometry,
357 AttachEllipseGeometry,
358 AttachPolystarGeometry,
359};
360
Florin Malita471a9462018-08-25 20:22:34 -0400361using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&,
362 const AnimationBuilder*, AnimatorScope*);
Florin Malita1b1dead2018-08-21 14:34:02 -0400363static constexpr PaintAttacherT gPaintAttachers[] = {
364 AttachColorFill,
365 AttachColorStroke,
366 AttachGradientFill,
367 AttachGradientStroke,
368};
369
370using GeometryEffectAttacherT =
371 std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
Florin Malita471a9462018-08-25 20:22:34 -0400372 const AnimationBuilder*, AnimatorScope*,
Florin Malita1b1dead2018-08-21 14:34:02 -0400373 std::vector<sk_sp<sksg::GeometryNode>>&&);
374static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
375 AttachMergeGeometryEffect,
376 AttachTrimGeometryEffect,
377 AttachRoundGeometryEffect,
378};
379
380enum class ShapeType {
381 kGeometry,
382 kGeometryEffect,
383 kPaint,
384 kGroup,
385 kTransform,
386};
387
388struct ShapeInfo {
389 const char* fTypeString;
390 ShapeType fShapeType;
391 uint32_t fAttacherIndex; // index into respective attacher tables
392};
393
394const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) {
395 static constexpr ShapeInfo gShapeInfo[] = {
396 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
397 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
398 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
399 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
400 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
401 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
402 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
403 { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect
404 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
405 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
406 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
407 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
408 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
409 };
410
411 const skjson::StringValue* type = jshape["ty"];
412 if (!type) {
413 return nullptr;
414 }
415
416 const auto* info = bsearch(type->begin(),
417 gShapeInfo,
418 SK_ARRAY_COUNT(gShapeInfo),
419 sizeof(ShapeInfo),
420 [](const void* key, const void* info) {
421 return strcmp(static_cast<const char*>(key),
422 static_cast<const ShapeInfo*>(info)->fTypeString);
423 });
424
425 return static_cast<const ShapeInfo*>(info);
426}
427
428struct GeometryEffectRec {
429 const skjson::ObjectValue& fJson;
430 GeometryEffectAttacherT fAttach;
431};
432
Florin Malitaa85f3a12018-09-24 17:24:59 -0400433} // namespace
434
435struct AnimationBuilder::AttachShapeContext {
436 AttachShapeContext(AnimatorScope* ascope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400437 std::vector<sk_sp<sksg::GeometryNode>>* geos,
438 std::vector<GeometryEffectRec>* effects,
439 size_t committedAnimators)
Florin Malitaa85f3a12018-09-24 17:24:59 -0400440 : fScope(ascope)
Florin Malita1b1dead2018-08-21 14:34:02 -0400441 , fGeometryStack(geos)
442 , fGeometryEffectStack(effects)
443 , fCommittedAnimators(committedAnimators) {}
444
445 AnimatorScope* fScope;
446 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
447 std::vector<GeometryEffectRec>* fGeometryEffectStack;
448 size_t fCommittedAnimators;
449};
450
Florin Malitaa85f3a12018-09-24 17:24:59 -0400451sk_sp<sksg::RenderNode> AnimationBuilder::attachShape(const skjson::ArrayValue* jshape,
452 AttachShapeContext* ctx) const {
Florin Malita1b1dead2018-08-21 14:34:02 -0400453 if (!jshape)
454 return nullptr;
455
Florin Malita471a9462018-08-25 20:22:34 -0400456 SkDEBUGCODE(const auto initialGeometryEffects = ctx->fGeometryEffectStack->size();)
Florin Malita1b1dead2018-08-21 14:34:02 -0400457
Florin Malita40c7c642018-09-08 20:09:37 -0400458 const skjson::ObjectValue* jtransform = nullptr;
Florin Malita1b1dead2018-08-21 14:34:02 -0400459
460 struct ShapeRec {
461 const skjson::ObjectValue& fJson;
462 const ShapeInfo& fInfo;
463 };
464
465 // First pass (bottom->top):
466 //
467 // * pick up the group transform and opacity
468 // * push local geometry effects onto the stack
469 // * store recs for next pass
470 //
471 std::vector<ShapeRec> recs;
472 for (size_t i = 0; i < jshape->size(); ++i) {
473 const skjson::ObjectValue* shape = (*jshape)[jshape->size() - 1 - i];
474 if (!shape) continue;
475
476 const auto* info = FindShapeInfo(*shape);
477 if (!info) {
Florin Malita57b9d402018-10-02 12:48:00 -0400478 this->log(Logger::Level::kError, &(*shape)["ty"], "Unknown shape.");
Florin Malita1b1dead2018-08-21 14:34:02 -0400479 continue;
480 }
481
482 recs.push_back({ *shape, *info });
483
484 switch (info->fShapeType) {
485 case ShapeType::kTransform:
Florin Malita40c7c642018-09-08 20:09:37 -0400486 // Just track the transform property for now -- we'll deal with it later.
487 jtransform = shape;
Florin Malita1b1dead2018-08-21 14:34:02 -0400488 break;
489 case ShapeType::kGeometryEffect:
490 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400491 ctx->fGeometryEffectStack->push_back(
Florin Malita1b1dead2018-08-21 14:34:02 -0400492 { *shape, gGeometryEffectAttachers[info->fAttacherIndex] });
493 break;
494 default:
495 break;
496 }
497 }
498
499 // Second pass (top -> bottom, after 2x reverse):
500 //
501 // * track local geometry
502 // * emit local paints
503 //
504 std::vector<sk_sp<sksg::GeometryNode>> geos;
505 std::vector<sk_sp<sksg::RenderNode >> draws;
506 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400507 const AutoPropertyTracker apt(this, rec->fJson);
508
Florin Malita1b1dead2018-08-21 14:34:02 -0400509 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 Malitaa85f3a12018-09-24 17:24:59 -0400513 this,
Florin Malita471a9462018-08-25 20:22:34 -0400514 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 Malitaa85f3a12018-09-24 17:24:59 -0400523 this,
Florin Malita471a9462018-08-25 20:22:34 -0400524 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 Malitaa85f3a12018-09-24 17:24:59 -0400534 AttachShapeContext groupShapeCtx(ctx->fScope,
Florin Malita1b1dead2018-08-21 14:34:02 -0400535 &geos,
Florin Malita471a9462018-08-25 20:22:34 -0400536 ctx->fGeometryEffectStack,
537 ctx->fCommittedAnimators);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400538 if (auto subgroup = this->attachShape(rec->fJson["it"], &groupShapeCtx)) {
Florin Malita1b1dead2018-08-21 14:34:02 -0400539 draws.push_back(std::move(subgroup));
Florin Malita471a9462018-08-25 20:22:34 -0400540 SkASSERT(groupShapeCtx.fCommittedAnimators >= ctx->fCommittedAnimators);
541 ctx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita1b1dead2018-08-21 14:34:02 -0400542 }
543 } break;
544 case ShapeType::kPaint: {
545 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malita471a9462018-08-25 20:22:34 -0400546 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaa85f3a12018-09-24 17:24:59 -0400547 this,
Florin Malita471a9462018-08-25 20:22:34 -0400548 ctx->fScope);
Florin Malita1b1dead2018-08-21 14:34:02 -0400549 if (!paint || geos.empty())
550 break;
551
552 auto drawGeos = geos;
553
554 // Apply all pending effects from the stack.
Florin Malita471a9462018-08-25 20:22:34 -0400555 for (auto it = ctx->fGeometryEffectStack->rbegin();
556 it != ctx->fGeometryEffectStack->rend(); ++it) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400557 drawGeos = it->fAttach(it->fJson, this, ctx->fScope, std::move(drawGeos));
Florin Malita1b1dead2018-08-21 14:34:02 -0400558 }
559
560 // If we still have multiple geos, reduce using 'merge'.
561 auto geo = drawGeos.size() > 1
562 ? Merge(std::move(drawGeos), sksg::Merge::Mode::kMerge)
563 : drawGeos[0];
564
565 SkASSERT(geo);
566 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malita471a9462018-08-25 20:22:34 -0400567 ctx->fCommittedAnimators = ctx->fScope->size();
Florin Malita1b1dead2018-08-21 14:34:02 -0400568 } break;
569 default:
570 break;
571 }
572 }
573
574 // By now we should have popped all local geometry effects.
Florin Malita471a9462018-08-25 20:22:34 -0400575 SkASSERT(ctx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita1b1dead2018-08-21 14:34:02 -0400576
Florin Malita40c7c642018-09-08 20:09:37 -0400577 sk_sp<sksg::RenderNode> shape_wrapper;
578 if (draws.size() == 1) {
579 // For a single draw, we don't need a group.
580 shape_wrapper = std::move(draws.front());
581 } else if (!draws.empty()) {
Florin Malita40c7c642018-09-08 20:09:37 -0400582 // Emit local draws reversed (bottom->top, per spec).
Florin Malitacd9d0742018-09-10 09:57:15 -0400583 std::reverse(draws.begin(), draws.end());
584 draws.shrink_to_fit();
Florin Malita40c7c642018-09-08 20:09:37 -0400585
Florin Malitacd9d0742018-09-10 09:57:15 -0400586 // We need a group to dispatch multiple draws.
587 shape_wrapper = sksg::Group::Make(std::move(draws));
Florin Malita40c7c642018-09-08 20:09:37 -0400588 }
589
Florin Malita919e2092019-01-09 15:37:57 -0500590 sk_sp<sksg::Transform> shape_transform;
Florin Malita40c7c642018-09-08 20:09:37 -0400591 if (jtransform) {
Florin Malitaa85f3a12018-09-24 17:24:59 -0400592 const AutoPropertyTracker apt(this, *jtransform);
593
Florin Malita40c7c642018-09-08 20:09:37 -0400594 // This is tricky due to the interaction with ctx->fCommittedAnimators: we want any
595 // animators related to tranform/opacity to be committed => they must be inserted in front
596 // of the dangling/uncommitted ones.
597 AnimatorScope local_scope;
598
Florin Malita919e2092019-01-09 15:37:57 -0500599 if ((shape_transform = this->attachMatrix2D(*jtransform, &local_scope, nullptr))) {
600 shape_wrapper = sksg::TransformEffect::Make(std::move(shape_wrapper), shape_transform);
Florin Malita40c7c642018-09-08 20:09:37 -0400601 }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400602 shape_wrapper = this->attachOpacity(*jtransform, &local_scope, std::move(shape_wrapper));
Florin Malita40c7c642018-09-08 20:09:37 -0400603
604 ctx->fScope->insert(ctx->fScope->begin() + ctx->fCommittedAnimators,
605 std::make_move_iterator(local_scope.begin()),
606 std::make_move_iterator(local_scope.end()));
607 ctx->fCommittedAnimators += local_scope.size();
608 }
609
Florin Malita1b1dead2018-08-21 14:34:02 -0400610 // Push transformed local geometries to parent list, for subsequent paints.
Florin Malita928db922018-10-17 17:07:05 -0400611 for (auto& geo : geos) {
Florin Malita919e2092019-01-09 15:37:57 -0500612 ctx->fGeometryStack->push_back(shape_transform
613 ? sksg::GeometryTransform::Make(std::move(geo), shape_transform)
Florin Malita1b1dead2018-08-21 14:34:02 -0400614 : std::move(geo));
615 }
616
Florin Malita40c7c642018-09-08 20:09:37 -0400617 return shape_wrapper;
Florin Malita1b1dead2018-08-21 14:34:02 -0400618}
619
Florin Malita1b1dead2018-08-21 14:34:02 -0400620sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
Florin Malita62c6bd92018-10-03 14:39:56 -0400621 const LayerInfo&,
Florin Malita471a9462018-08-25 20:22:34 -0400622 AnimatorScope* ascope) const {
Florin Malita1b1dead2018-08-21 14:34:02 -0400623 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
624 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400625 AttachShapeContext shapeCtx(ascope, &geometryStack, &geometryEffectStack, ascope->size());
626 auto shapeNode = this->attachShape(layer["shapes"], &shapeCtx);
Florin Malita1b1dead2018-08-21 14:34:02 -0400627
628 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
629 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
630 // due to attached animators. To avoid this, we track committed animators and discard the
631 // orphans here.
632 SkASSERT(shapeCtx.fCommittedAnimators <= ascope->size());
633 ascope->resize(shapeCtx.fCommittedAnimators);
634
635 return shapeNode;
636}
637
638} // namespace internal
639} // namespace skottie