blob: 8545de839127ca5baa8c7c6d6802c9a3f4216d8e [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
Florin Malita54f65c42018-01-16 17:04:30 -05008#include "Skottie.h"
Florin Malita094ccde2017-12-30 12:27:00 -05009
10#include "SkCanvas.h"
Florin Malitafc807c82018-01-25 22:35:09 -050011#include "SkJSONCPP.h"
Florin Malita54f65c42018-01-16 17:04:30 -050012#include "SkottieAnimator.h"
Florin Malitacf8ed522018-01-25 15:27:33 -050013#include "SkottieParser.h"
Florin Malita54f65c42018-01-16 17:04:30 -050014#include "SkottieProperties.h"
Florin Malita094ccde2017-12-30 12:27:00 -050015#include "SkData.h"
Florin Malita49328072018-01-08 12:51:12 -050016#include "SkImage.h"
Florin Malita094ccde2017-12-30 12:27:00 -050017#include "SkMakeUnique.h"
Florin Malita49328072018-01-08 12:51:12 -050018#include "SkOSPath.h"
Florin Malita094ccde2017-12-30 12:27:00 -050019#include "SkPaint.h"
Florin Malita0e66fba2018-01-09 17:10:18 -050020#include "SkParse.h"
Florin Malita094ccde2017-12-30 12:27:00 -050021#include "SkPath.h"
22#include "SkPoint.h"
23#include "SkSGColor.h"
24#include "SkSGDraw.h"
Florin Malita16d0ad02018-01-19 15:07:29 -050025#include "SkSGGeometryTransform.h"
Florin Malita6aaee592018-01-12 12:25:09 -050026#include "SkSGGradient.h"
Florin Malita094ccde2017-12-30 12:27:00 -050027#include "SkSGGroup.h"
Florin Malita49328072018-01-08 12:51:12 -050028#include "SkSGImage.h"
29#include "SkSGInvalidationController.h"
Florin Malita5f9102f2018-01-10 13:36:22 -050030#include "SkSGMaskEffect.h"
Florin Malitae6345d92018-01-03 23:37:54 -050031#include "SkSGMerge.h"
Florin Malitac0034172018-01-08 16:42:59 -050032#include "SkSGOpacityEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050033#include "SkSGPath.h"
Florin Malita2e1d7e22018-01-02 10:40:00 -050034#include "SkSGRect.h"
Florin Malita35efaa82018-01-22 12:57:06 -050035#include "SkSGScene.h"
Florin Malita094ccde2017-12-30 12:27:00 -050036#include "SkSGTransform.h"
Florin Malita51b8c892018-01-07 08:54:24 -050037#include "SkSGTrimEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050038#include "SkStream.h"
39#include "SkTArray.h"
40#include "SkTHash.h"
41
42#include <cmath>
Florin Malitae6345d92018-01-03 23:37:54 -050043#include <vector>
44
Florin Malita094ccde2017-12-30 12:27:00 -050045#include "stdlib.h"
46
Florin Malita54f65c42018-01-16 17:04:30 -050047namespace skottie {
Florin Malita094ccde2017-12-30 12:27:00 -050048
Florin Malitacf8ed522018-01-25 15:27:33 -050049#define LOG SkDebugf
50
Florin Malita094ccde2017-12-30 12:27:00 -050051namespace {
52
53using AssetMap = SkTHashMap<SkString, const Json::Value*>;
54
55struct AttachContext {
Florin Malita35efaa82018-01-22 12:57:06 -050056 const ResourceProvider& fResources;
57 const AssetMap& fAssets;
58 sksg::Scene::AnimatorList& fAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -050059};
60
61bool LogFail(const Json::Value& json, const char* msg) {
62 const auto dump = json.toStyledString();
63 LOG("!! %s: %s", msg, dump.c_str());
64 return false;
65}
66
Florin Malita18eafd92018-01-04 21:11:55 -050067sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
68 sk_sp<sksg::Matrix> parentMatrix) {
69 if (!t.isObject())
70 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -050071
Florin Malita18eafd92018-01-04 21:11:55 -050072 auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
73 auto composite = sk_make_sp<CompositeTransform>(matrix);
Florin Malitafc807c82018-01-25 22:35:09 -050074 auto anchor_attached = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050075 [composite](const VectorValue& a) {
76 composite->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
Florin Malita094ccde2017-12-30 12:27:00 -050077 });
Florin Malitafc807c82018-01-25 22:35:09 -050078 auto position_attached = BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050079 [composite](const VectorValue& p) {
80 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malita094ccde2017-12-30 12:27:00 -050081 });
Florin Malitafc807c82018-01-25 22:35:09 -050082 auto scale_attached = BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050083 [composite](const VectorValue& s) {
84 composite->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
Florin Malita094ccde2017-12-30 12:27:00 -050085 });
Florin Malita1eb98db2018-01-26 15:03:38 -050086
87 auto* jrotation = &t["r"];
88 if (jrotation->isNull()) {
89 // 3d rotations have separate rx,ry,rz components. While we don't fully support them,
90 // we can still make use of rz.
91 jrotation = &t["rz"];
92 }
93 auto rotation_attached = BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050094 [composite](const ScalarValue& r) {
95 composite->setRotation(r);
Florin Malita094ccde2017-12-30 12:27:00 -050096 });
Florin Malitafc807c82018-01-25 22:35:09 -050097 auto skew_attached = BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050098 [composite](const ScalarValue& sk) {
99 composite->setSkew(sk);
Florin Malita094ccde2017-12-30 12:27:00 -0500100 });
Florin Malitafc807c82018-01-25 22:35:09 -0500101 auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500102 [composite](const ScalarValue& sa) {
103 composite->setSkewAxis(sa);
Florin Malita094ccde2017-12-30 12:27:00 -0500104 });
105
106 if (!anchor_attached &&
107 !position_attached &&
108 !scale_attached &&
109 !rotation_attached &&
110 !skew_attached &&
111 !skewaxis_attached) {
112 LogFail(t, "Could not parse transform");
Florin Malita18eafd92018-01-04 21:11:55 -0500113 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500114 }
115
Florin Malita18eafd92018-01-04 21:11:55 -0500116 return matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500117}
118
Florin Malitac0034172018-01-08 16:42:59 -0500119sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachContext* ctx,
120 sk_sp<sksg::RenderNode> childNode) {
121 if (!jtransform.isObject() || !childNode)
122 return childNode;
123
124 // This is more peeky than other attachers, because we want to avoid redundant opacity
125 // nodes for the extremely common case of static opaciy == 100.
126 const auto& opacity = jtransform["o"];
127 if (opacity.isObject() &&
Florin Malitacf8ed522018-01-25 15:27:33 -0500128 !ParseDefault(opacity["a"], true) &&
129 ParseDefault(opacity["k"], -1) == 100) {
Florin Malitac0034172018-01-08 16:42:59 -0500130 // Ignoring static full opacity.
131 return childNode;
132 }
133
134 auto opacityNode = sksg::OpacityEffect::Make(childNode);
Florin Malitafc807c82018-01-25 22:35:09 -0500135 BindProperty<ScalarValue>(opacity, &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500136 [opacityNode](const ScalarValue& o) {
Florin Malitac0034172018-01-08 16:42:59 -0500137 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500138 opacityNode->setOpacity(o * 0.01f);
Florin Malitac0034172018-01-08 16:42:59 -0500139 });
140
141 return opacityNode;
142}
143
Florin Malita094ccde2017-12-30 12:27:00 -0500144sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
145
Florin Malita25366fa2018-01-23 13:37:59 -0500146sk_sp<sksg::Path> AttachPath(const Json::Value& jpath, AttachContext* ctx) {
147 auto path_node = sksg::Path::Make();
Florin Malitafc807c82018-01-25 22:35:09 -0500148 return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
149 [path_node](const ShapeValue& p) { path_node->setPath(p); })
Florin Malita25366fa2018-01-23 13:37:59 -0500150 ? path_node
151 : nullptr;
152}
153
Florin Malita094ccde2017-12-30 12:27:00 -0500154sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
155 SkASSERT(jpath.isObject());
156
Florin Malita25366fa2018-01-23 13:37:59 -0500157 return AttachPath(jpath["ks"], ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500158}
159
Florin Malita2e1d7e22018-01-02 10:40:00 -0500160sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
161 SkASSERT(jrect.isObject());
162
163 auto rect_node = sksg::RRect::Make();
164 auto composite = sk_make_sp<CompositeRRect>(rect_node);
165
Florin Malitafc807c82018-01-25 22:35:09 -0500166 auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500167 [composite](const VectorValue& p) {
168 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500169 });
Florin Malitafc807c82018-01-25 22:35:09 -0500170 auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500171 [composite](const VectorValue& s) {
172 composite->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
Florin Malitaf9590922018-01-09 11:56:09 -0500173 });
Florin Malitafc807c82018-01-25 22:35:09 -0500174 auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500175 [composite](const ScalarValue& r) {
176 composite->setRadius(SkSize::Make(r, r));
Florin Malitaf9590922018-01-09 11:56:09 -0500177 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500178
179 if (!p_attached && !s_attached && !r_attached) {
180 return nullptr;
181 }
182
Florin Malitafbc13f12018-01-04 10:26:35 -0500183 LOG("** Attached (r)rect geometry\n");
184
185 return rect_node;
186}
187
188sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
189 SkASSERT(jellipse.isObject());
190
191 auto rect_node = sksg::RRect::Make();
192 auto composite = sk_make_sp<CompositeRRect>(rect_node);
193
Florin Malitafc807c82018-01-25 22:35:09 -0500194 auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500195 [composite](const VectorValue& p) {
196 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500197 });
Florin Malitafc807c82018-01-25 22:35:09 -0500198 auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500199 [composite](const VectorValue& s) {
Florin Malitaf9590922018-01-09 11:56:09 -0500200 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
Florin Malita2518a0a2018-01-24 18:29:00 -0500201 composite->setSize(sz);
202 composite->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
Florin Malitaf9590922018-01-09 11:56:09 -0500203 });
Florin Malitafbc13f12018-01-04 10:26:35 -0500204
205 if (!p_attached && !s_attached) {
206 return nullptr;
207 }
208
209 LOG("** Attached ellipse geometry\n");
210
Florin Malita2e1d7e22018-01-02 10:40:00 -0500211 return rect_node;
212}
213
Florin Malita02a32b02018-01-04 11:27:09 -0500214sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
215 SkASSERT(jstar.isObject());
216
217 static constexpr CompositePolyStar::Type gTypes[] = {
218 CompositePolyStar::Type::kStar, // "sy": 1
219 CompositePolyStar::Type::kPoly, // "sy": 2
220 };
221
Florin Malitacf8ed522018-01-25 15:27:33 -0500222 const auto type = ParseDefault(jstar["sy"], 0) - 1;
Florin Malita02a32b02018-01-04 11:27:09 -0500223 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
224 LogFail(jstar, "Unknown polystar type");
225 return nullptr;
226 }
227
228 auto path_node = sksg::Path::Make();
229 auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
230
Florin Malitafc807c82018-01-25 22:35:09 -0500231 BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500232 [composite](const VectorValue& p) {
233 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500234 });
Florin Malitafc807c82018-01-25 22:35:09 -0500235 BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500236 [composite](const ScalarValue& pt) {
237 composite->setPointCount(pt);
Florin Malitaf9590922018-01-09 11:56:09 -0500238 });
Florin Malitafc807c82018-01-25 22:35:09 -0500239 BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500240 [composite](const ScalarValue& ir) {
241 composite->setInnerRadius(ir);
Florin Malitaf9590922018-01-09 11:56:09 -0500242 });
Florin Malitafc807c82018-01-25 22:35:09 -0500243 BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500244 [composite](const ScalarValue& otr) {
245 composite->setOuterRadius(otr);
Florin Malita9661b982018-01-06 14:25:49 -0500246 });
Florin Malitafc807c82018-01-25 22:35:09 -0500247 BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500248 [composite](const ScalarValue& is) {
249 composite->setInnerRoundness(is);
Florin Malita9661b982018-01-06 14:25:49 -0500250 });
Florin Malitafc807c82018-01-25 22:35:09 -0500251 BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500252 [composite](const ScalarValue& os) {
253 composite->setOuterRoundness(os);
Florin Malita9661b982018-01-06 14:25:49 -0500254 });
Florin Malitafc807c82018-01-25 22:35:09 -0500255 BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500256 [composite](const ScalarValue& r) {
257 composite->setRotation(r);
Florin Malitaf9590922018-01-09 11:56:09 -0500258 });
Florin Malita02a32b02018-01-04 11:27:09 -0500259
260 return path_node;
261}
262
Florin Malita6aaee592018-01-12 12:25:09 -0500263sk_sp<sksg::Color> AttachColor(const Json::Value& obj, AttachContext* ctx) {
Florin Malita094ccde2017-12-30 12:27:00 -0500264 SkASSERT(obj.isObject());
265
266 auto color_node = sksg::Color::Make(SK_ColorBLACK);
Florin Malitafc807c82018-01-25 22:35:09 -0500267 auto color_attached = BindProperty<VectorValue>(obj["c"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500268 [color_node](const VectorValue& c) {
269 color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
Florin Malitaf9590922018-01-09 11:56:09 -0500270 });
Florin Malita094ccde2017-12-30 12:27:00 -0500271
Florin Malita1586d852018-01-12 14:27:39 -0500272 return color_attached ? color_node : nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500273}
274
Florin Malita6aaee592018-01-12 12:25:09 -0500275sk_sp<sksg::Gradient> AttachGradient(const Json::Value& obj, AttachContext* ctx) {
276 SkASSERT(obj.isObject());
Florin Malita094ccde2017-12-30 12:27:00 -0500277
Florin Malita6aaee592018-01-12 12:25:09 -0500278 const auto& stops = obj["g"];
279 if (!stops.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500280 return nullptr;
281
Florin Malitacf8ed522018-01-25 15:27:33 -0500282 const auto stopCount = ParseDefault(stops["p"], -1);
Florin Malita6aaee592018-01-12 12:25:09 -0500283 if (stopCount < 0)
284 return nullptr;
285
286 sk_sp<sksg::Gradient> gradient_node;
287 sk_sp<CompositeGradient> composite;
288
Florin Malitacf8ed522018-01-25 15:27:33 -0500289 if (ParseDefault(obj["t"], 1) == 1) {
Florin Malita6aaee592018-01-12 12:25:09 -0500290 auto linear_node = sksg::LinearGradient::Make();
291 composite = sk_make_sp<CompositeLinearGradient>(linear_node, stopCount);
292 gradient_node = std::move(linear_node);
293 } else {
294 auto radial_node = sksg::RadialGradient::Make();
295 composite = sk_make_sp<CompositeRadialGradient>(radial_node, stopCount);
296
297 // TODO: highlight, angle
298 gradient_node = std::move(radial_node);
299 }
300
Florin Malitafc807c82018-01-25 22:35:09 -0500301 BindProperty<VectorValue>(stops["k"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500302 [composite](const VectorValue& stops) {
303 composite->setColorStops(stops);
Florin Malita6aaee592018-01-12 12:25:09 -0500304 });
Florin Malitafc807c82018-01-25 22:35:09 -0500305 BindProperty<VectorValue>(obj["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500306 [composite](const VectorValue& s) {
307 composite->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
Florin Malita6aaee592018-01-12 12:25:09 -0500308 });
Florin Malitafc807c82018-01-25 22:35:09 -0500309 BindProperty<VectorValue>(obj["e"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500310 [composite](const VectorValue& e) {
311 composite->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
Florin Malita6aaee592018-01-12 12:25:09 -0500312 });
313
314 return gradient_node;
315}
316
Florin Malita1586d852018-01-12 14:27:39 -0500317sk_sp<sksg::PaintNode> AttachPaint(const Json::Value& jpaint, AttachContext* ctx,
Florin Malita6aaee592018-01-12 12:25:09 -0500318 sk_sp<sksg::PaintNode> paint_node) {
319 if (paint_node) {
320 paint_node->setAntiAlias(true);
321
Florin Malitafc807c82018-01-25 22:35:09 -0500322 BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500323 [paint_node](const ScalarValue& o) {
Florin Malita1586d852018-01-12 14:27:39 -0500324 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500325 paint_node->setOpacity(o * 0.01f);
Florin Malita1586d852018-01-12 14:27:39 -0500326 });
Florin Malita6aaee592018-01-12 12:25:09 -0500327 }
328
329 return paint_node;
330}
331
332sk_sp<sksg::PaintNode> AttachStroke(const Json::Value& jstroke, AttachContext* ctx,
333 sk_sp<sksg::PaintNode> stroke_node) {
334 SkASSERT(jstroke.isObject());
335
336 if (!stroke_node)
337 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500338
339 stroke_node->setStyle(SkPaint::kStroke_Style);
340
Florin Malitafc807c82018-01-25 22:35:09 -0500341 auto width_attached = BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500342 [stroke_node](const ScalarValue& w) {
343 stroke_node->setStrokeWidth(w);
Florin Malitaf9590922018-01-09 11:56:09 -0500344 });
Florin Malita094ccde2017-12-30 12:27:00 -0500345 if (!width_attached)
346 return nullptr;
347
Florin Malitacf8ed522018-01-25 15:27:33 -0500348 stroke_node->setStrokeMiter(ParseDefault(jstroke["ml"], 4.0f));
Florin Malita094ccde2017-12-30 12:27:00 -0500349
350 static constexpr SkPaint::Join gJoins[] = {
351 SkPaint::kMiter_Join,
352 SkPaint::kRound_Join,
353 SkPaint::kBevel_Join,
354 };
Florin Malitacf8ed522018-01-25 15:27:33 -0500355 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault(jstroke["lj"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500356 0, SK_ARRAY_COUNT(gJoins) - 1)]);
357
358 static constexpr SkPaint::Cap gCaps[] = {
359 SkPaint::kButt_Cap,
360 SkPaint::kRound_Cap,
361 SkPaint::kSquare_Cap,
362 };
Florin Malitacf8ed522018-01-25 15:27:33 -0500363 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500364 0, SK_ARRAY_COUNT(gCaps) - 1)]);
365
366 return stroke_node;
367}
368
Florin Malita6aaee592018-01-12 12:25:09 -0500369sk_sp<sksg::PaintNode> AttachColorFill(const Json::Value& jfill, AttachContext* ctx) {
370 SkASSERT(jfill.isObject());
371
372 return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
373}
374
375sk_sp<sksg::PaintNode> AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) {
376 SkASSERT(jfill.isObject());
377
378 return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
379}
380
381sk_sp<sksg::PaintNode> AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) {
382 SkASSERT(jstroke.isObject());
383
384 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
385}
386
387sk_sp<sksg::PaintNode> AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) {
388 SkASSERT(jstroke.isObject());
389
390 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
391}
392
Florin Malitae6345d92018-01-03 23:37:54 -0500393std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
394 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
395 std::vector<sk_sp<sksg::GeometryNode>> merged;
396
397 static constexpr sksg::Merge::Mode gModes[] = {
398 sksg::Merge::Mode::kMerge, // "mm": 1
399 sksg::Merge::Mode::kUnion, // "mm": 2
400 sksg::Merge::Mode::kDifference, // "mm": 3
401 sksg::Merge::Mode::kIntersect, // "mm": 4
402 sksg::Merge::Mode::kXOR , // "mm": 5
403 };
404
Florin Malitacf8ed522018-01-25 15:27:33 -0500405 const auto mode = gModes[SkTPin<int>(ParseDefault(jmerge["mm"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500406 0, SK_ARRAY_COUNT(gModes) - 1)];
Florin Malitae6345d92018-01-03 23:37:54 -0500407 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
408
409 LOG("** Attached merge path effect, mode: %d\n", mode);
410
411 return merged;
412}
413
Florin Malita51b8c892018-01-07 08:54:24 -0500414std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
415 const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
416
417 enum class Mode {
418 kMerged, // "m": 1
419 kSeparate, // "m": 2
420 } gModes[] = { Mode::kMerged, Mode::kSeparate };
421
Florin Malitacf8ed522018-01-25 15:27:33 -0500422 const auto mode = gModes[SkTPin<int>(ParseDefault(jtrim["m"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500423 0, SK_ARRAY_COUNT(gModes) - 1)];
424
425 std::vector<sk_sp<sksg::GeometryNode>> inputs;
426 if (mode == Mode::kMerged) {
427 inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge));
428 } else {
429 inputs = std::move(geos);
430 }
431
432 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
433 trimmed.reserve(inputs.size());
434 for (const auto& i : inputs) {
435 const auto trim = sksg::TrimEffect::Make(i);
436 trimmed.push_back(trim);
Florin Malitafc807c82018-01-25 22:35:09 -0500437 BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500438 [trim](const ScalarValue& s) {
439 trim->setStart(s * 0.01f);
Florin Malita51b8c892018-01-07 08:54:24 -0500440 });
Florin Malitafc807c82018-01-25 22:35:09 -0500441 BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500442 [trim](const ScalarValue& e) {
443 trim->setEnd(e * 0.01f);
Florin Malita51b8c892018-01-07 08:54:24 -0500444 });
Florin Malitafc807c82018-01-25 22:35:09 -0500445 BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500446 [trim](const ScalarValue& o) {
447 trim->setOffset(o / 360);
Florin Malita51b8c892018-01-07 08:54:24 -0500448 });
449 }
450
451 return trimmed;
452}
453
Florin Malita094ccde2017-12-30 12:27:00 -0500454using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
455static constexpr GeometryAttacherT gGeometryAttachers[] = {
456 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500457 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500458 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500459 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500460};
461
462using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
463static constexpr PaintAttacherT gPaintAttachers[] = {
Florin Malita6aaee592018-01-12 12:25:09 -0500464 AttachColorFill,
465 AttachColorStroke,
466 AttachGradientFill,
467 AttachGradientStroke,
Florin Malita094ccde2017-12-30 12:27:00 -0500468};
469
Florin Malitae6345d92018-01-03 23:37:54 -0500470using GeometryEffectAttacherT =
471 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
472 AttachContext*,
473 std::vector<sk_sp<sksg::GeometryNode>>&&);
474static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
475 AttachMergeGeometryEffect,
Florin Malita51b8c892018-01-07 08:54:24 -0500476 AttachTrimGeometryEffect,
Florin Malitae6345d92018-01-03 23:37:54 -0500477};
478
Florin Malita094ccde2017-12-30 12:27:00 -0500479enum class ShapeType {
480 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500481 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500482 kPaint,
483 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500484 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500485};
486
487struct ShapeInfo {
488 const char* fTypeString;
489 ShapeType fShapeType;
490 uint32_t fAttacherIndex; // index into respective attacher tables
491};
492
493const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
494 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500495 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500496 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
497 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
Florin Malita16d0ad02018-01-19 15:07:29 -0500498 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
Florin Malita6aaee592018-01-12 12:25:09 -0500499 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
Florin Malitae6345d92018-01-03 23:37:54 -0500500 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500501 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500502 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500503 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500504 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
Florin Malita51b8c892018-01-07 08:54:24 -0500505 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
Florin Malita16d0ad02018-01-19 15:07:29 -0500506 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
Florin Malita094ccde2017-12-30 12:27:00 -0500507 };
508
509 if (!shape.isObject())
510 return nullptr;
511
512 const auto& type = shape["ty"];
513 if (!type.isString())
514 return nullptr;
515
516 const auto* info = bsearch(type.asCString(),
517 gShapeInfo,
518 SK_ARRAY_COUNT(gShapeInfo),
519 sizeof(ShapeInfo),
520 [](const void* key, const void* info) {
521 return strcmp(static_cast<const char*>(key),
522 static_cast<const ShapeInfo*>(info)->fTypeString);
523 });
524
525 return static_cast<const ShapeInfo*>(info);
526}
527
Florin Malita16d0ad02018-01-19 15:07:29 -0500528struct GeometryEffectRec {
529 const Json::Value& fJson;
530 GeometryEffectAttacherT fAttach;
531};
532
Florin Malitaca4439f2018-01-23 10:31:59 -0500533struct AttachShapeContext {
534 AttachShapeContext(AttachContext* ctx,
535 std::vector<sk_sp<sksg::GeometryNode>>* geos,
536 std::vector<GeometryEffectRec>* effects,
537 size_t committedAnimators)
538 : fCtx(ctx)
539 , fGeometryStack(geos)
540 , fGeometryEffectStack(effects)
541 , fCommittedAnimators(committedAnimators) {}
542
543 AttachContext* fCtx;
544 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
545 std::vector<GeometryEffectRec>* fGeometryEffectStack;
546 size_t fCommittedAnimators;
547};
548
549sk_sp<sksg::RenderNode> AttachShape(const Json::Value& jshape, AttachShapeContext* shapeCtx) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500550 if (!jshape.isArray())
Florin Malita094ccde2017-12-30 12:27:00 -0500551 return nullptr;
552
Florin Malitaca4439f2018-01-23 10:31:59 -0500553 SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
Florin Malita094ccde2017-12-30 12:27:00 -0500554
Florin Malita16d0ad02018-01-19 15:07:29 -0500555 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
556 sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
557 sk_sp<sksg::Matrix> shape_matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500558
Florin Malita16d0ad02018-01-19 15:07:29 -0500559 struct ShapeRec {
560 const Json::Value& fJson;
561 const ShapeInfo& fInfo;
562 };
563
564 // First pass (bottom->top):
565 //
566 // * pick up the group transform and opacity
567 // * push local geometry effects onto the stack
568 // * store recs for next pass
569 //
570 std::vector<ShapeRec> recs;
571 for (Json::ArrayIndex i = 0; i < jshape.size(); ++i) {
572 const auto& s = jshape[jshape.size() - 1 - i];
Florin Malita094ccde2017-12-30 12:27:00 -0500573 const auto* info = FindShapeInfo(s);
574 if (!info) {
575 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
576 continue;
577 }
578
Florin Malita16d0ad02018-01-19 15:07:29 -0500579 recs.push_back({ s, *info });
580
Florin Malita094ccde2017-12-30 12:27:00 -0500581 switch (info->fShapeType) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500582 case ShapeType::kTransform:
Florin Malitaca4439f2018-01-23 10:31:59 -0500583 if ((shape_matrix = AttachMatrix(s, shapeCtx->fCtx, nullptr))) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500584 shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
585 }
Florin Malitaca4439f2018-01-23 10:31:59 -0500586 shape_wrapper = AttachOpacity(s, shapeCtx->fCtx, std::move(shape_wrapper));
Florin Malita16d0ad02018-01-19 15:07:29 -0500587 break;
588 case ShapeType::kGeometryEffect:
589 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500590 shapeCtx->fGeometryEffectStack->push_back(
Florin Malita16d0ad02018-01-19 15:07:29 -0500591 { s, gGeometryEffectAttachers[info->fAttacherIndex] });
592 break;
593 default:
594 break;
595 }
596 }
597
598 // Second pass (top -> bottom, after 2x reverse):
599 //
600 // * track local geometry
601 // * emit local paints
602 //
603 std::vector<sk_sp<sksg::GeometryNode>> geos;
604 std::vector<sk_sp<sksg::RenderNode >> draws;
605 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
606 switch (rec->fInfo.fShapeType) {
Florin Malita094ccde2017-12-30 12:27:00 -0500607 case ShapeType::kGeometry: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500608 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500609 if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
610 shapeCtx->fCtx)) {
Florin Malita094ccde2017-12-30 12:27:00 -0500611 geos.push_back(std::move(geo));
612 }
613 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500614 case ShapeType::kGeometryEffect: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500615 // Apply the current effect and pop from the stack.
616 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaee6de4b2018-01-21 11:13:17 -0500617 if (!geos.empty()) {
618 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaca4439f2018-01-23 10:31:59 -0500619 shapeCtx->fCtx,
Florin Malitaee6de4b2018-01-21 11:13:17 -0500620 std::move(geos));
621 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500622
Florin Malitaca4439f2018-01-23 10:31:59 -0500623 SkASSERT(shapeCtx->fGeometryEffectStack->back().fJson == rec->fJson);
624 SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
Florin Malita16d0ad02018-01-19 15:07:29 -0500625 gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
Florin Malitaca4439f2018-01-23 10:31:59 -0500626 shapeCtx->fGeometryEffectStack->pop_back();
Florin Malita094ccde2017-12-30 12:27:00 -0500627 } break;
628 case ShapeType::kGroup: {
Florin Malitaca4439f2018-01-23 10:31:59 -0500629 AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
630 &geos,
631 shapeCtx->fGeometryEffectStack,
632 shapeCtx->fCommittedAnimators);
633 if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500634 draws.push_back(std::move(subgroup));
Florin Malitaca4439f2018-01-23 10:31:59 -0500635 SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
636 shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -0500637 }
638 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500639 case ShapeType::kPaint: {
640 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500641 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
Florin Malita16d0ad02018-01-19 15:07:29 -0500642 if (!paint || geos.empty())
643 break;
644
645 auto drawGeos = geos;
646
647 // Apply all pending effects from the stack.
Florin Malitaca4439f2018-01-23 10:31:59 -0500648 for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
649 it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
650 drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
Florin Malita18eafd92018-01-04 21:11:55 -0500651 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500652
653 // If we still have multiple geos, reduce using 'merge'.
654 auto geo = drawGeos.size() > 1
655 ? sksg::Merge::Make(std::move(drawGeos), sksg::Merge::Mode::kMerge)
656 : drawGeos[0];
657
658 SkASSERT(geo);
659 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malitaca4439f2018-01-23 10:31:59 -0500660 shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
Florin Malitadacc02b2017-12-31 09:12:31 -0500661 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500662 default:
663 break;
Florin Malita094ccde2017-12-30 12:27:00 -0500664 }
665 }
666
Florin Malita16d0ad02018-01-19 15:07:29 -0500667 // By now we should have popped all local geometry effects.
Florin Malitaca4439f2018-01-23 10:31:59 -0500668 SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita16d0ad02018-01-19 15:07:29 -0500669
670 // Push transformed local geometries to parent list, for subsequent paints.
671 for (const auto& geo : geos) {
Florin Malitaca4439f2018-01-23 10:31:59 -0500672 shapeCtx->fGeometryStack->push_back(shape_matrix
Florin Malita16d0ad02018-01-19 15:07:29 -0500673 ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
674 : std::move(geo));
Florin Malita094ccde2017-12-30 12:27:00 -0500675 }
676
Florin Malita16d0ad02018-01-19 15:07:29 -0500677 // Emit local draws reversed (bottom->top, per spec).
678 for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
679 shape_group->addChild(std::move(*it));
Florin Malita2a8275b2018-01-02 12:52:43 -0500680 }
681
Florin Malita16d0ad02018-01-19 15:07:29 -0500682 return draws.empty() ? nullptr : shape_wrapper;
Florin Malita094ccde2017-12-30 12:27:00 -0500683}
684
685sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
686 SkASSERT(layer.isObject());
687
Florin Malitacf8ed522018-01-25 15:27:33 -0500688 SkString refId;
689 if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
Florin Malita094ccde2017-12-30 12:27:00 -0500690 LOG("!! Comp layer missing refId\n");
691 return nullptr;
692 }
693
694 const auto* comp = ctx->fAssets.find(refId);
695 if (!comp) {
696 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
697 return nullptr;
698 }
699
700 // TODO: cycle detection
701 return AttachComposition(**comp, ctx);
702}
703
Florin Malita0e66fba2018-01-09 17:10:18 -0500704sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& jlayer, AttachContext*) {
705 SkASSERT(jlayer.isObject());
Florin Malita094ccde2017-12-30 12:27:00 -0500706
Florin Malitacf8ed522018-01-25 15:27:33 -0500707 const auto size = SkSize::Make(ParseDefault(jlayer["sw"], 0.0f),
708 ParseDefault(jlayer["sh"], 0.0f));
709 const auto hex = ParseDefault(jlayer["sc"], SkString());
Florin Malita0e66fba2018-01-09 17:10:18 -0500710 uint32_t c;
711 if (size.isEmpty() ||
712 !hex.startsWith("#") ||
713 !SkParse::FindHex(hex.c_str() + 1, &c)) {
714 LogFail(jlayer, "Could not parse solid layer");
715 return nullptr;
716 }
717
718 const SkColor color = 0xff000000 | c;
719
720 return sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeSize(size)),
721 sksg::Color::Make(color));
Florin Malita094ccde2017-12-30 12:27:00 -0500722}
723
Florin Malita49328072018-01-08 12:51:12 -0500724sk_sp<sksg::RenderNode> AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) {
725 SkASSERT(jimage.isObject());
726
Florin Malitacf8ed522018-01-25 15:27:33 -0500727 const auto name = ParseDefault(jimage["p"], SkString()),
728 path = ParseDefault(jimage["u"], SkString());
Florin Malita49328072018-01-08 12:51:12 -0500729 if (name.isEmpty())
730 return nullptr;
731
732 // TODO: plumb resource paths explicitly to ResourceProvider?
733 const auto resName = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
734 const auto resStream = ctx->fResources.openStream(resName.c_str());
735 if (!resStream || !resStream->hasLength()) {
736 LOG("!! Could not load image resource: %s\n", resName.c_str());
737 return nullptr;
738 }
739
740 // TODO: non-intrisic image sizing
741 return sksg::Image::Make(
742 SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
743}
744
745sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx) {
Florin Malita094ccde2017-12-30 12:27:00 -0500746 SkASSERT(layer.isObject());
747
Florin Malitacf8ed522018-01-25 15:27:33 -0500748 SkString refId;
749 if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
Florin Malita49328072018-01-08 12:51:12 -0500750 LOG("!! Image layer missing refId\n");
751 return nullptr;
752 }
753
754 const auto* jimage = ctx->fAssets.find(refId);
755 if (!jimage) {
756 LOG("!! Image asset not found: '%s'\n", refId.c_str());
757 return nullptr;
758 }
759
760 return AttachImageAsset(**jimage, ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500761}
762
763sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
764 SkASSERT(layer.isObject());
765
Florin Malita18eafd92018-01-04 21:11:55 -0500766 // Null layers are used solely to drive dependent transforms,
767 // but we use free-floating sksg::Matrices for that purpose.
Florin Malita094ccde2017-12-30 12:27:00 -0500768 return nullptr;
769}
770
771sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx) {
772 SkASSERT(layer.isObject());
773
Florin Malita16d0ad02018-01-19 15:07:29 -0500774 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
775 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malitaca4439f2018-01-23 10:31:59 -0500776 AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
777 auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
778
779 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
780 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
781 // due to attached animators. To avoid this, we track committed animators and discard the
782 // orphans here.
783 SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
784 ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
785
786 return shapeNode;
Florin Malita094ccde2017-12-30 12:27:00 -0500787}
788
789sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*) {
790 SkASSERT(layer.isObject());
791
792 LOG("?? Text layer stub\n");
793 return nullptr;
794}
795
Florin Malita18eafd92018-01-04 21:11:55 -0500796struct AttachLayerContext {
797 AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
798 : fLayerList(jlayers), fCtx(ctx) {}
799
Florin Malita4a490682018-01-28 14:27:51 -0500800 const Json::Value& fLayerList;
801 AttachContext* fCtx;
802 SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
803 sk_sp<sksg::RenderNode> fCurrentMatte;
Florin Malita18eafd92018-01-04 21:11:55 -0500804
Florin Malita4a490682018-01-28 14:27:51 -0500805 sk_sp<sksg::Matrix> AttachParentLayerMatrix(const Json::Value& jlayer) {
806 SkASSERT(jlayer.isObject());
Florin Malita18eafd92018-01-04 21:11:55 -0500807 SkASSERT(fLayerList.isArray());
808
Florin Malita4a490682018-01-28 14:27:51 -0500809 const auto parent_index = ParseDefault(jlayer["parent"], -1);
810 if (parent_index < 0)
Florin Malita18eafd92018-01-04 21:11:55 -0500811 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500812
Florin Malita4a490682018-01-28 14:27:51 -0500813 if (auto* m = fLayerMatrixMap.find(parent_index))
814 return *m;
Florin Malita18eafd92018-01-04 21:11:55 -0500815
816 for (const auto& l : fLayerList) {
817 if (!l.isObject()) {
818 continue;
819 }
820
Florin Malita4a490682018-01-28 14:27:51 -0500821 if (ParseDefault(l["ind"], -1) == parent_index) {
822 return this->AttachLayerMatrix(l);
Florin Malita18eafd92018-01-04 21:11:55 -0500823 }
824 }
825
826 return nullptr;
827 }
828
829 sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
830 SkASSERT(jlayer.isObject());
831
Florin Malita4a490682018-01-28 14:27:51 -0500832 const auto layer_index = ParseDefault(jlayer["ind"], -1);
833 if (layer_index < 0)
834 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500835
Florin Malita4a490682018-01-28 14:27:51 -0500836 if (auto* m = fLayerMatrixMap.find(layer_index))
837 return *m;
Florin Malita18eafd92018-01-04 21:11:55 -0500838
Florin Malita4a490682018-01-28 14:27:51 -0500839 // Add a stub entry to break recursion cycles.
840 fLayerMatrixMap.set(layer_index, nullptr);
Florin Malita18eafd92018-01-04 21:11:55 -0500841
Florin Malita4a490682018-01-28 14:27:51 -0500842 auto parent_matrix = this->AttachParentLayerMatrix(jlayer);
Florin Malita18eafd92018-01-04 21:11:55 -0500843
Florin Malita4a490682018-01-28 14:27:51 -0500844 return *fLayerMatrixMap.set(layer_index,
845 AttachMatrix(jlayer["ks"],
846 fCtx,
847 this->AttachParentLayerMatrix(jlayer)));
Florin Malita18eafd92018-01-04 21:11:55 -0500848 }
849};
850
Florin Malita25366fa2018-01-23 13:37:59 -0500851SkBlendMode MaskBlendMode(char mode) {
852 switch (mode) {
853 case 'a': return SkBlendMode::kSrcOver; // Additive
854 case 's': return SkBlendMode::kExclusion; // Subtract
855 case 'i': return SkBlendMode::kDstIn; // Intersect
856 case 'l': return SkBlendMode::kLighten; // Lighten
857 case 'd': return SkBlendMode::kDarken; // Darken
858 case 'f': return SkBlendMode::kDifference; // Difference
859 default: break;
860 }
861
862 return SkBlendMode::kSrcOver;
863}
864
865sk_sp<sksg::RenderNode> AttachMask(const Json::Value& jmask,
866 AttachContext* ctx,
867 sk_sp<sksg::RenderNode> childNode) {
868 if (!jmask.isArray())
869 return childNode;
870
871 auto mask_group = sksg::Group::Make();
872
873 for (const auto& m : jmask) {
874 if (!m.isObject())
875 continue;
876
Florin Malitacf8ed522018-01-25 15:27:33 -0500877 const auto inverted = ParseDefault(m["inv"], false);
Florin Malita25366fa2018-01-23 13:37:59 -0500878 // TODO
879 if (inverted) {
880 LogFail(m, "Unsupported inverse mask");
881 continue;
882 }
883
884 auto mask_path = AttachPath(m["pt"], ctx);
885 if (!mask_path) {
886 LogFail(m, "Could not parse mask path");
887 continue;
888 }
889
Florin Malitacf8ed522018-01-25 15:27:33 -0500890 SkString mode;
891 if (!Parse(m["mode"], &mode) ||
892 mode.size() != 1 ||
893 !strcmp(mode.c_str(), "n")) { // "None" masks have no effect.
Florin Malita25366fa2018-01-23 13:37:59 -0500894 continue;
Florin Malitacf8ed522018-01-25 15:27:33 -0500895 }
Florin Malita25366fa2018-01-23 13:37:59 -0500896
897 auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
898 mask_paint->setBlendMode(MaskBlendMode(mode.c_str()[0]));
Florin Malitafc807c82018-01-25 22:35:09 -0500899 BindProperty<ScalarValue>(m["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500900 [mask_paint](const ScalarValue& o) { mask_paint->setOpacity(o * 0.01f); });
Florin Malita25366fa2018-01-23 13:37:59 -0500901
902 mask_group->addChild(sksg::Draw::Make(std::move(mask_path), std::move(mask_paint)));
903 }
904
905 return mask_group->empty()
906 ? childNode
907 : sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
908}
909
Florin Malita18eafd92018-01-04 21:11:55 -0500910sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
911 AttachLayerContext* layerCtx) {
912 if (!jlayer.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500913 return nullptr;
914
915 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*);
916 static constexpr LayerAttacher gLayerAttachers[] = {
917 AttachCompLayer, // 'ty': 0
918 AttachSolidLayer, // 'ty': 1
919 AttachImageLayer, // 'ty': 2
920 AttachNullLayer, // 'ty': 3
921 AttachShapeLayer, // 'ty': 4
922 AttachTextLayer, // 'ty': 5
923 };
924
Florin Malitacf8ed522018-01-25 15:27:33 -0500925 int type = ParseDefault(jlayer["ty"], -1);
Florin Malita094ccde2017-12-30 12:27:00 -0500926 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
927 return nullptr;
928 }
929
Florin Malita71cba8f2018-01-09 08:07:14 -0500930 // Layer content.
931 auto layer = gLayerAttachers[type](jlayer, layerCtx->fCtx);
Florin Malita25366fa2018-01-23 13:37:59 -0500932 // Optional layer mask.
933 layer = AttachMask(jlayer["masksProperties"], layerCtx->fCtx, std::move(layer));
934 // Optional layer transform.
Florin Malita71cba8f2018-01-09 08:07:14 -0500935 if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
Florin Malita71cba8f2018-01-09 08:07:14 -0500936 layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
937 }
938 // Optional layer opacity.
939 layer = AttachOpacity(jlayer["ks"], layerCtx->fCtx, std::move(layer));
Florin Malita18eafd92018-01-04 21:11:55 -0500940
Florin Malita71cba8f2018-01-09 08:07:14 -0500941 // TODO: we should also disable related/inactive animators.
Florin Malita35efaa82018-01-22 12:57:06 -0500942 class Activator final : public sksg::Animator {
Florin Malita71cba8f2018-01-09 08:07:14 -0500943 public:
944 Activator(sk_sp<sksg::OpacityEffect> controlNode, float in, float out)
945 : fControlNode(std::move(controlNode))
946 , fIn(in)
947 , fOut(out) {}
948
Florin Malita35efaa82018-01-22 12:57:06 -0500949 void onTick(float t) override {
Florin Malita71cba8f2018-01-09 08:07:14 -0500950 // Keep the layer fully transparent except for its [in..out] lifespan.
951 // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
952 fControlNode->setOpacity(t >= fIn && t <= fOut ? 1 : 0);
953 }
954
955 private:
956 const sk_sp<sksg::OpacityEffect> fControlNode;
957 const float fIn,
958 fOut;
959 };
960
961 auto layerControl = sksg::OpacityEffect::Make(std::move(layer));
Florin Malitacf8ed522018-01-25 15:27:33 -0500962 const auto in = ParseDefault(jlayer["ip"], 0.0f),
963 out = ParseDefault(jlayer["op"], in);
Florin Malita71cba8f2018-01-09 08:07:14 -0500964
965 if (in >= out || ! layerControl)
966 return nullptr;
967
968 layerCtx->fCtx->fAnimators.push_back(skstd::make_unique<Activator>(layerControl, in, out));
969
Florin Malitacf8ed522018-01-25 15:27:33 -0500970 if (ParseDefault(jlayer["td"], false)) {
Florin Malita5f9102f2018-01-10 13:36:22 -0500971 // This layer is a matte. We apply it as a mask to the next layer.
972 layerCtx->fCurrentMatte = std::move(layerControl);
973 return nullptr;
974 }
975
976 if (layerCtx->fCurrentMatte) {
977 // There is a pending matte. Apply and reset.
978 return sksg::MaskEffect::Make(std::move(layerControl), std::move(layerCtx->fCurrentMatte));
979 }
980
Florin Malita71cba8f2018-01-09 08:07:14 -0500981 return layerControl;
Florin Malita094ccde2017-12-30 12:27:00 -0500982}
983
984sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
985 if (!comp.isObject())
986 return nullptr;
987
Florin Malita18eafd92018-01-04 21:11:55 -0500988 const auto& jlayers = comp["layers"];
989 if (!jlayers.isArray())
990 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500991
Florin Malita18eafd92018-01-04 21:11:55 -0500992 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
993 AttachLayerContext layerCtx(jlayers, ctx);
994
995 for (const auto& l : jlayers) {
996 if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -0500997 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -0500998 }
999 }
1000
Florin Malita2a8275b2018-01-02 12:52:43 -05001001 if (layers.empty()) {
1002 return nullptr;
1003 }
1004
1005 // Layers are painted in bottom->top order.
1006 auto comp_group = sksg::Group::Make();
1007 for (int i = layers.count() - 1; i >= 0; --i) {
1008 comp_group->addChild(std::move(layers[i]));
1009 }
1010
1011 LOG("** Attached composition '%s': %d layers.\n",
Florin Malitacf8ed522018-01-25 15:27:33 -05001012 ParseDefault(comp["id"], SkString()).c_str(), layers.count());
Florin Malita2a8275b2018-01-02 12:52:43 -05001013
Florin Malita094ccde2017-12-30 12:27:00 -05001014 return comp_group;
1015}
1016
1017} // namespace
1018
Florin Malita49328072018-01-08 12:51:12 -05001019std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
Florin Malita094ccde2017-12-30 12:27:00 -05001020 if (!stream->hasLength()) {
1021 // TODO: handle explicit buffering?
1022 LOG("!! cannot parse streaming content\n");
1023 return nullptr;
1024 }
1025
1026 Json::Value json;
1027 {
1028 auto data = SkData::MakeFromStream(stream, stream->getLength());
1029 if (!data) {
1030 LOG("!! could not read stream\n");
1031 return nullptr;
1032 }
1033
1034 Json::Reader reader;
1035
1036 auto dataStart = static_cast<const char*>(data->data());
1037 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
1038 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
1039 return nullptr;
1040 }
1041 }
1042
Florin Malitacf8ed522018-01-25 15:27:33 -05001043 const auto version = ParseDefault(json["v"], SkString());
1044 const auto size = SkSize::Make(ParseDefault(json["w"], 0.0f),
1045 ParseDefault(json["h"], 0.0f));
1046 const auto fps = ParseDefault(json["fr"], -1.0f);
Florin Malita094ccde2017-12-30 12:27:00 -05001047
1048 if (size.isEmpty() || version.isEmpty() || fps < 0) {
1049 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
1050 version.c_str(), size.width(), size.height(), fps);
1051 return nullptr;
1052 }
1053
Florin Malita49328072018-01-08 12:51:12 -05001054 return std::unique_ptr<Animation>(new Animation(res, std::move(version), size, fps, json));
Florin Malita094ccde2017-12-30 12:27:00 -05001055}
1056
Florin Malita49328072018-01-08 12:51:12 -05001057std::unique_ptr<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
1058 class DirectoryResourceProvider final : public ResourceProvider {
1059 public:
1060 explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
1061
1062 std::unique_ptr<SkStream> openStream(const char resource[]) const override {
1063 const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
1064 return SkStream::MakeFromFile(resPath.c_str());
1065 }
1066
1067 private:
1068 const SkString fDir;
1069 };
1070
1071 const auto jsonStream = SkStream::MakeFromFile(path);
1072 if (!jsonStream)
1073 return nullptr;
1074
1075 std::unique_ptr<ResourceProvider> defaultProvider;
1076 if (!res) {
1077 defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
1078 }
1079
1080 return Make(jsonStream.get(), res ? *res : *defaultProvider);
1081}
1082
1083Animation::Animation(const ResourceProvider& resources,
1084 SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
Florin Malita094ccde2017-12-30 12:27:00 -05001085 : fVersion(std::move(version))
1086 , fSize(size)
1087 , fFrameRate(fps)
Florin Malitacf8ed522018-01-25 15:27:33 -05001088 , fInPoint(ParseDefault(json["ip"], 0.0f))
1089 , fOutPoint(SkTMax(ParseDefault(json["op"], SK_ScalarMax), fInPoint)) {
Florin Malita094ccde2017-12-30 12:27:00 -05001090
1091 AssetMap assets;
1092 for (const auto& asset : json["assets"]) {
1093 if (!asset.isObject()) {
1094 continue;
1095 }
1096
Florin Malitacf8ed522018-01-25 15:27:33 -05001097 assets.set(ParseDefault(asset["id"], SkString()), &asset);
Florin Malita094ccde2017-12-30 12:27:00 -05001098 }
1099
Florin Malita35efaa82018-01-22 12:57:06 -05001100 sksg::Scene::AnimatorList animators;
1101 AttachContext ctx = { resources, assets, animators };
1102 auto root = AttachComposition(json, &ctx);
1103
1104 LOG("** Attached %d animators\n", animators.size());
1105
1106 fScene = sksg::Scene::Make(std::move(root), std::move(animators));
Florin Malita094ccde2017-12-30 12:27:00 -05001107
Florin Malitadb385732018-01-09 12:19:32 -05001108 // In case the client calls render before the first tick.
1109 this->animationTick(0);
Florin Malita094ccde2017-12-30 12:27:00 -05001110}
1111
1112Animation::~Animation() = default;
1113
Florin Malita35efaa82018-01-22 12:57:06 -05001114void Animation::setShowInval(bool show) {
1115 if (fScene) {
1116 fScene->setShowInval(show);
1117 }
1118}
1119
Mike Reed29859872018-01-08 08:25:27 -05001120void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
Florin Malita35efaa82018-01-22 12:57:06 -05001121 if (!fScene)
Florin Malita094ccde2017-12-30 12:27:00 -05001122 return;
1123
Mike Reed29859872018-01-08 08:25:27 -05001124 SkAutoCanvasRestore restore(canvas, true);
1125 const SkRect srcR = SkRect::MakeSize(this->size());
1126 if (dstR) {
1127 canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
1128 }
1129 canvas->clipRect(srcR);
Florin Malita35efaa82018-01-22 12:57:06 -05001130 fScene->render(canvas);
Florin Malita094ccde2017-12-30 12:27:00 -05001131}
1132
1133void Animation::animationTick(SkMSec ms) {
Florin Malita35efaa82018-01-22 12:57:06 -05001134 if (!fScene)
1135 return;
1136
Florin Malita094ccde2017-12-30 12:27:00 -05001137 // 't' in the BM model really means 'frame #'
1138 auto t = static_cast<float>(ms) * fFrameRate / 1000;
1139
1140 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
1141
Florin Malita35efaa82018-01-22 12:57:06 -05001142 fScene->animate(t);
Florin Malita094ccde2017-12-30 12:27:00 -05001143}
1144
Florin Malita54f65c42018-01-16 17:04:30 -05001145} // namespace skottie