blob: 532cc8d36d3444e3eb68a57b263beb0318293a4e [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 "SkPoint.h"
22#include "SkSGColor.h"
23#include "SkSGDraw.h"
Florin Malita16d0ad02018-01-19 15:07:29 -050024#include "SkSGGeometryTransform.h"
Florin Malita6aaee592018-01-12 12:25:09 -050025#include "SkSGGradient.h"
Florin Malita094ccde2017-12-30 12:27:00 -050026#include "SkSGGroup.h"
Florin Malita49328072018-01-08 12:51:12 -050027#include "SkSGImage.h"
28#include "SkSGInvalidationController.h"
Florin Malita5f9102f2018-01-10 13:36:22 -050029#include "SkSGMaskEffect.h"
Florin Malitae6345d92018-01-03 23:37:54 -050030#include "SkSGMerge.h"
Florin Malitac0034172018-01-08 16:42:59 -050031#include "SkSGOpacityEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050032#include "SkSGPath.h"
Florin Malita2e1d7e22018-01-02 10:40:00 -050033#include "SkSGRect.h"
Florin Malita35efaa82018-01-22 12:57:06 -050034#include "SkSGScene.h"
Florin Malita094ccde2017-12-30 12:27:00 -050035#include "SkSGTransform.h"
Florin Malita51b8c892018-01-07 08:54:24 -050036#include "SkSGTrimEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050037#include "SkStream.h"
38#include "SkTArray.h"
39#include "SkTHash.h"
40
41#include <cmath>
Florin Malitae6345d92018-01-03 23:37:54 -050042#include <vector>
43
Florin Malita094ccde2017-12-30 12:27:00 -050044#include "stdlib.h"
45
Florin Malita54f65c42018-01-16 17:04:30 -050046namespace skottie {
Florin Malita094ccde2017-12-30 12:27:00 -050047
Florin Malitacf8ed522018-01-25 15:27:33 -050048#define LOG SkDebugf
49
Florin Malita094ccde2017-12-30 12:27:00 -050050namespace {
51
Florin Malitadd22cf962018-01-29 15:42:01 +000052using AssetMap = SkTHashMap<SkString, const Json::Value*>;
Florin Malita094ccde2017-12-30 12:27:00 -050053
54struct AttachContext {
Florin Malitacca86f32018-01-29 10:49:49 -050055 const ResourceProvider& fResources;
56 const AssetMap& fAssets;
57 sksg::AnimatorList& fAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -050058};
59
60bool LogFail(const Json::Value& json, const char* msg) {
61 const auto dump = json.toStyledString();
62 LOG("!! %s: %s", msg, dump.c_str());
63 return false;
64}
65
Florin Malita18eafd92018-01-04 21:11:55 -050066sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
67 sk_sp<sksg::Matrix> parentMatrix) {
68 if (!t.isObject())
69 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -050070
Florin Malita18eafd92018-01-04 21:11:55 -050071 auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
72 auto composite = sk_make_sp<CompositeTransform>(matrix);
Florin Malitafc807c82018-01-25 22:35:09 -050073 auto anchor_attached = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050074 [composite](const VectorValue& a) {
75 composite->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
Florin Malita094ccde2017-12-30 12:27:00 -050076 });
Florin Malitafc807c82018-01-25 22:35:09 -050077 auto position_attached = BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050078 [composite](const VectorValue& p) {
79 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malita094ccde2017-12-30 12:27:00 -050080 });
Florin Malitafc807c82018-01-25 22:35:09 -050081 auto scale_attached = BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050082 [composite](const VectorValue& s) {
83 composite->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
Florin Malita094ccde2017-12-30 12:27:00 -050084 });
Florin Malita1eb98db2018-01-26 15:03:38 -050085
86 auto* jrotation = &t["r"];
87 if (jrotation->isNull()) {
88 // 3d rotations have separate rx,ry,rz components. While we don't fully support them,
89 // we can still make use of rz.
90 jrotation = &t["rz"];
91 }
92 auto rotation_attached = BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050093 [composite](const ScalarValue& r) {
94 composite->setRotation(r);
Florin Malita094ccde2017-12-30 12:27:00 -050095 });
Florin Malitafc807c82018-01-25 22:35:09 -050096 auto skew_attached = BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -050097 [composite](const ScalarValue& sk) {
98 composite->setSkew(sk);
Florin Malita094ccde2017-12-30 12:27:00 -050099 });
Florin Malitafc807c82018-01-25 22:35:09 -0500100 auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500101 [composite](const ScalarValue& sa) {
102 composite->setSkewAxis(sa);
Florin Malita094ccde2017-12-30 12:27:00 -0500103 });
104
105 if (!anchor_attached &&
106 !position_attached &&
107 !scale_attached &&
108 !rotation_attached &&
109 !skew_attached &&
110 !skewaxis_attached) {
111 LogFail(t, "Could not parse transform");
Florin Malita18eafd92018-01-04 21:11:55 -0500112 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500113 }
114
Florin Malita18eafd92018-01-04 21:11:55 -0500115 return matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500116}
117
Florin Malitac0034172018-01-08 16:42:59 -0500118sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachContext* ctx,
119 sk_sp<sksg::RenderNode> childNode) {
120 if (!jtransform.isObject() || !childNode)
121 return childNode;
122
123 // This is more peeky than other attachers, because we want to avoid redundant opacity
124 // nodes for the extremely common case of static opaciy == 100.
125 const auto& opacity = jtransform["o"];
126 if (opacity.isObject() &&
Florin Malitacf8ed522018-01-25 15:27:33 -0500127 !ParseDefault(opacity["a"], true) &&
128 ParseDefault(opacity["k"], -1) == 100) {
Florin Malitac0034172018-01-08 16:42:59 -0500129 // Ignoring static full opacity.
130 return childNode;
131 }
132
133 auto opacityNode = sksg::OpacityEffect::Make(childNode);
Florin Malitafc807c82018-01-25 22:35:09 -0500134 BindProperty<ScalarValue>(opacity, &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500135 [opacityNode](const ScalarValue& o) {
Florin Malitac0034172018-01-08 16:42:59 -0500136 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500137 opacityNode->setOpacity(o * 0.01f);
Florin Malitac0034172018-01-08 16:42:59 -0500138 });
139
140 return opacityNode;
141}
142
Florin Malita094ccde2017-12-30 12:27:00 -0500143sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
144
Florin Malita25366fa2018-01-23 13:37:59 -0500145sk_sp<sksg::Path> AttachPath(const Json::Value& jpath, AttachContext* ctx) {
146 auto path_node = sksg::Path::Make();
Florin Malitafc807c82018-01-25 22:35:09 -0500147 return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
148 [path_node](const ShapeValue& p) { path_node->setPath(p); })
Florin Malita25366fa2018-01-23 13:37:59 -0500149 ? path_node
150 : nullptr;
151}
152
Florin Malita094ccde2017-12-30 12:27:00 -0500153sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
154 SkASSERT(jpath.isObject());
155
Florin Malita25366fa2018-01-23 13:37:59 -0500156 return AttachPath(jpath["ks"], ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500157}
158
Florin Malita2e1d7e22018-01-02 10:40:00 -0500159sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
160 SkASSERT(jrect.isObject());
161
162 auto rect_node = sksg::RRect::Make();
163 auto composite = sk_make_sp<CompositeRRect>(rect_node);
164
Florin Malitafc807c82018-01-25 22:35:09 -0500165 auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500166 [composite](const VectorValue& p) {
167 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500168 });
Florin Malitafc807c82018-01-25 22:35:09 -0500169 auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500170 [composite](const VectorValue& s) {
171 composite->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
Florin Malitaf9590922018-01-09 11:56:09 -0500172 });
Florin Malitafc807c82018-01-25 22:35:09 -0500173 auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500174 [composite](const ScalarValue& r) {
175 composite->setRadius(SkSize::Make(r, r));
Florin Malitaf9590922018-01-09 11:56:09 -0500176 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500177
178 if (!p_attached && !s_attached && !r_attached) {
179 return nullptr;
180 }
181
Florin Malitafbc13f12018-01-04 10:26:35 -0500182 return rect_node;
183}
184
185sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
186 SkASSERT(jellipse.isObject());
187
188 auto rect_node = sksg::RRect::Make();
189 auto composite = sk_make_sp<CompositeRRect>(rect_node);
190
Florin Malitafc807c82018-01-25 22:35:09 -0500191 auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500192 [composite](const VectorValue& p) {
193 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500194 });
Florin Malitafc807c82018-01-25 22:35:09 -0500195 auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500196 [composite](const VectorValue& s) {
Florin Malitaf9590922018-01-09 11:56:09 -0500197 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
Florin Malita2518a0a2018-01-24 18:29:00 -0500198 composite->setSize(sz);
199 composite->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
Florin Malitaf9590922018-01-09 11:56:09 -0500200 });
Florin Malitafbc13f12018-01-04 10:26:35 -0500201
202 if (!p_attached && !s_attached) {
203 return nullptr;
204 }
205
Florin Malita2e1d7e22018-01-02 10:40:00 -0500206 return rect_node;
207}
208
Florin Malita02a32b02018-01-04 11:27:09 -0500209sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
210 SkASSERT(jstar.isObject());
211
212 static constexpr CompositePolyStar::Type gTypes[] = {
213 CompositePolyStar::Type::kStar, // "sy": 1
214 CompositePolyStar::Type::kPoly, // "sy": 2
215 };
216
Florin Malitacf8ed522018-01-25 15:27:33 -0500217 const auto type = ParseDefault(jstar["sy"], 0) - 1;
Florin Malita02a32b02018-01-04 11:27:09 -0500218 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
219 LogFail(jstar, "Unknown polystar type");
220 return nullptr;
221 }
222
223 auto path_node = sksg::Path::Make();
224 auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
225
Florin Malitafc807c82018-01-25 22:35:09 -0500226 BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500227 [composite](const VectorValue& p) {
228 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500229 });
Florin Malitafc807c82018-01-25 22:35:09 -0500230 BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500231 [composite](const ScalarValue& pt) {
232 composite->setPointCount(pt);
Florin Malitaf9590922018-01-09 11:56:09 -0500233 });
Florin Malitafc807c82018-01-25 22:35:09 -0500234 BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500235 [composite](const ScalarValue& ir) {
236 composite->setInnerRadius(ir);
Florin Malitaf9590922018-01-09 11:56:09 -0500237 });
Florin Malitafc807c82018-01-25 22:35:09 -0500238 BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500239 [composite](const ScalarValue& otr) {
240 composite->setOuterRadius(otr);
Florin Malita9661b982018-01-06 14:25:49 -0500241 });
Florin Malitafc807c82018-01-25 22:35:09 -0500242 BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500243 [composite](const ScalarValue& is) {
244 composite->setInnerRoundness(is);
Florin Malita9661b982018-01-06 14:25:49 -0500245 });
Florin Malitafc807c82018-01-25 22:35:09 -0500246 BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500247 [composite](const ScalarValue& os) {
248 composite->setOuterRoundness(os);
Florin Malita9661b982018-01-06 14:25:49 -0500249 });
Florin Malitafc807c82018-01-25 22:35:09 -0500250 BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500251 [composite](const ScalarValue& r) {
252 composite->setRotation(r);
Florin Malitaf9590922018-01-09 11:56:09 -0500253 });
Florin Malita02a32b02018-01-04 11:27:09 -0500254
255 return path_node;
256}
257
Florin Malita6aaee592018-01-12 12:25:09 -0500258sk_sp<sksg::Color> AttachColor(const Json::Value& obj, AttachContext* ctx) {
Florin Malita094ccde2017-12-30 12:27:00 -0500259 SkASSERT(obj.isObject());
260
261 auto color_node = sksg::Color::Make(SK_ColorBLACK);
Florin Malitafc807c82018-01-25 22:35:09 -0500262 auto color_attached = BindProperty<VectorValue>(obj["c"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500263 [color_node](const VectorValue& c) {
264 color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
Florin Malitaf9590922018-01-09 11:56:09 -0500265 });
Florin Malita094ccde2017-12-30 12:27:00 -0500266
Florin Malita1586d852018-01-12 14:27:39 -0500267 return color_attached ? color_node : nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500268}
269
Florin Malita6aaee592018-01-12 12:25:09 -0500270sk_sp<sksg::Gradient> AttachGradient(const Json::Value& obj, AttachContext* ctx) {
271 SkASSERT(obj.isObject());
Florin Malita094ccde2017-12-30 12:27:00 -0500272
Florin Malita6aaee592018-01-12 12:25:09 -0500273 const auto& stops = obj["g"];
274 if (!stops.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500275 return nullptr;
276
Florin Malitacf8ed522018-01-25 15:27:33 -0500277 const auto stopCount = ParseDefault(stops["p"], -1);
Florin Malita6aaee592018-01-12 12:25:09 -0500278 if (stopCount < 0)
279 return nullptr;
280
281 sk_sp<sksg::Gradient> gradient_node;
282 sk_sp<CompositeGradient> composite;
283
Florin Malitacf8ed522018-01-25 15:27:33 -0500284 if (ParseDefault(obj["t"], 1) == 1) {
Florin Malita6aaee592018-01-12 12:25:09 -0500285 auto linear_node = sksg::LinearGradient::Make();
286 composite = sk_make_sp<CompositeLinearGradient>(linear_node, stopCount);
287 gradient_node = std::move(linear_node);
288 } else {
289 auto radial_node = sksg::RadialGradient::Make();
290 composite = sk_make_sp<CompositeRadialGradient>(radial_node, stopCount);
291
292 // TODO: highlight, angle
293 gradient_node = std::move(radial_node);
294 }
295
Florin Malitafc807c82018-01-25 22:35:09 -0500296 BindProperty<VectorValue>(stops["k"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500297 [composite](const VectorValue& stops) {
298 composite->setColorStops(stops);
Florin Malita6aaee592018-01-12 12:25:09 -0500299 });
Florin Malitafc807c82018-01-25 22:35:09 -0500300 BindProperty<VectorValue>(obj["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500301 [composite](const VectorValue& s) {
302 composite->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
Florin Malita6aaee592018-01-12 12:25:09 -0500303 });
Florin Malitafc807c82018-01-25 22:35:09 -0500304 BindProperty<VectorValue>(obj["e"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500305 [composite](const VectorValue& e) {
306 composite->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
Florin Malita6aaee592018-01-12 12:25:09 -0500307 });
308
309 return gradient_node;
310}
311
Florin Malita1586d852018-01-12 14:27:39 -0500312sk_sp<sksg::PaintNode> AttachPaint(const Json::Value& jpaint, AttachContext* ctx,
Florin Malita6aaee592018-01-12 12:25:09 -0500313 sk_sp<sksg::PaintNode> paint_node) {
314 if (paint_node) {
315 paint_node->setAntiAlias(true);
316
Florin Malitafc807c82018-01-25 22:35:09 -0500317 BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500318 [paint_node](const ScalarValue& o) {
Florin Malita1586d852018-01-12 14:27:39 -0500319 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500320 paint_node->setOpacity(o * 0.01f);
Florin Malita1586d852018-01-12 14:27:39 -0500321 });
Florin Malita6aaee592018-01-12 12:25:09 -0500322 }
323
324 return paint_node;
325}
326
327sk_sp<sksg::PaintNode> AttachStroke(const Json::Value& jstroke, AttachContext* ctx,
328 sk_sp<sksg::PaintNode> stroke_node) {
329 SkASSERT(jstroke.isObject());
330
331 if (!stroke_node)
332 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500333
334 stroke_node->setStyle(SkPaint::kStroke_Style);
335
Florin Malitafc807c82018-01-25 22:35:09 -0500336 auto width_attached = BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500337 [stroke_node](const ScalarValue& w) {
338 stroke_node->setStrokeWidth(w);
Florin Malitaf9590922018-01-09 11:56:09 -0500339 });
Florin Malita094ccde2017-12-30 12:27:00 -0500340 if (!width_attached)
341 return nullptr;
342
Florin Malitacf8ed522018-01-25 15:27:33 -0500343 stroke_node->setStrokeMiter(ParseDefault(jstroke["ml"], 4.0f));
Florin Malita094ccde2017-12-30 12:27:00 -0500344
345 static constexpr SkPaint::Join gJoins[] = {
346 SkPaint::kMiter_Join,
347 SkPaint::kRound_Join,
348 SkPaint::kBevel_Join,
349 };
Florin Malitacf8ed522018-01-25 15:27:33 -0500350 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault(jstroke["lj"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500351 0, SK_ARRAY_COUNT(gJoins) - 1)]);
352
353 static constexpr SkPaint::Cap gCaps[] = {
354 SkPaint::kButt_Cap,
355 SkPaint::kRound_Cap,
356 SkPaint::kSquare_Cap,
357 };
Florin Malitacf8ed522018-01-25 15:27:33 -0500358 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500359 0, SK_ARRAY_COUNT(gCaps) - 1)]);
360
361 return stroke_node;
362}
363
Florin Malita6aaee592018-01-12 12:25:09 -0500364sk_sp<sksg::PaintNode> AttachColorFill(const Json::Value& jfill, AttachContext* ctx) {
365 SkASSERT(jfill.isObject());
366
367 return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
368}
369
370sk_sp<sksg::PaintNode> AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) {
371 SkASSERT(jfill.isObject());
372
373 return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
374}
375
376sk_sp<sksg::PaintNode> AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) {
377 SkASSERT(jstroke.isObject());
378
379 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
380}
381
382sk_sp<sksg::PaintNode> AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) {
383 SkASSERT(jstroke.isObject());
384
385 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
386}
387
Florin Malitae6345d92018-01-03 23:37:54 -0500388std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
389 const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
390 std::vector<sk_sp<sksg::GeometryNode>> merged;
391
392 static constexpr sksg::Merge::Mode gModes[] = {
393 sksg::Merge::Mode::kMerge, // "mm": 1
394 sksg::Merge::Mode::kUnion, // "mm": 2
395 sksg::Merge::Mode::kDifference, // "mm": 3
396 sksg::Merge::Mode::kIntersect, // "mm": 4
397 sksg::Merge::Mode::kXOR , // "mm": 5
398 };
399
Florin Malitacf8ed522018-01-25 15:27:33 -0500400 const auto mode = gModes[SkTPin<int>(ParseDefault(jmerge["mm"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500401 0, SK_ARRAY_COUNT(gModes) - 1)];
Florin Malitae6345d92018-01-03 23:37:54 -0500402 merged.push_back(sksg::Merge::Make(std::move(geos), mode));
403
Florin Malitae6345d92018-01-03 23:37:54 -0500404 return merged;
405}
406
Florin Malita51b8c892018-01-07 08:54:24 -0500407std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
408 const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
409
410 enum class Mode {
411 kMerged, // "m": 1
412 kSeparate, // "m": 2
413 } gModes[] = { Mode::kMerged, Mode::kSeparate };
414
Florin Malitacf8ed522018-01-25 15:27:33 -0500415 const auto mode = gModes[SkTPin<int>(ParseDefault(jtrim["m"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500416 0, SK_ARRAY_COUNT(gModes) - 1)];
417
418 std::vector<sk_sp<sksg::GeometryNode>> inputs;
419 if (mode == Mode::kMerged) {
420 inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge));
421 } else {
422 inputs = std::move(geos);
423 }
424
425 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
426 trimmed.reserve(inputs.size());
427 for (const auto& i : inputs) {
428 const auto trim = sksg::TrimEffect::Make(i);
429 trimmed.push_back(trim);
Florin Malitafc807c82018-01-25 22:35:09 -0500430 BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500431 [trim](const ScalarValue& s) {
432 trim->setStart(s * 0.01f);
Florin Malita51b8c892018-01-07 08:54:24 -0500433 });
Florin Malitafc807c82018-01-25 22:35:09 -0500434 BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500435 [trim](const ScalarValue& e) {
436 trim->setEnd(e * 0.01f);
Florin Malita51b8c892018-01-07 08:54:24 -0500437 });
Florin Malitafc807c82018-01-25 22:35:09 -0500438 BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500439 [trim](const ScalarValue& o) {
440 trim->setOffset(o / 360);
Florin Malita51b8c892018-01-07 08:54:24 -0500441 });
442 }
443
444 return trimmed;
445}
446
Florin Malita094ccde2017-12-30 12:27:00 -0500447using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
448static constexpr GeometryAttacherT gGeometryAttachers[] = {
449 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500450 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500451 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500452 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500453};
454
455using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
456static constexpr PaintAttacherT gPaintAttachers[] = {
Florin Malita6aaee592018-01-12 12:25:09 -0500457 AttachColorFill,
458 AttachColorStroke,
459 AttachGradientFill,
460 AttachGradientStroke,
Florin Malita094ccde2017-12-30 12:27:00 -0500461};
462
Florin Malitae6345d92018-01-03 23:37:54 -0500463using GeometryEffectAttacherT =
464 std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
465 AttachContext*,
466 std::vector<sk_sp<sksg::GeometryNode>>&&);
467static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
468 AttachMergeGeometryEffect,
Florin Malita51b8c892018-01-07 08:54:24 -0500469 AttachTrimGeometryEffect,
Florin Malitae6345d92018-01-03 23:37:54 -0500470};
471
Florin Malita094ccde2017-12-30 12:27:00 -0500472enum class ShapeType {
473 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500474 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500475 kPaint,
476 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500477 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500478};
479
480struct ShapeInfo {
481 const char* fTypeString;
482 ShapeType fShapeType;
483 uint32_t fAttacherIndex; // index into respective attacher tables
484};
485
486const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
487 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500488 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500489 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
490 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
Florin Malita16d0ad02018-01-19 15:07:29 -0500491 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
Florin Malita6aaee592018-01-12 12:25:09 -0500492 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
Florin Malitae6345d92018-01-03 23:37:54 -0500493 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500494 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malitae6345d92018-01-03 23:37:54 -0500495 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500496 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500497 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
Florin Malita51b8c892018-01-07 08:54:24 -0500498 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
Florin Malita16d0ad02018-01-19 15:07:29 -0500499 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
Florin Malita094ccde2017-12-30 12:27:00 -0500500 };
501
502 if (!shape.isObject())
503 return nullptr;
504
505 const auto& type = shape["ty"];
506 if (!type.isString())
507 return nullptr;
508
509 const auto* info = bsearch(type.asCString(),
510 gShapeInfo,
511 SK_ARRAY_COUNT(gShapeInfo),
512 sizeof(ShapeInfo),
513 [](const void* key, const void* info) {
514 return strcmp(static_cast<const char*>(key),
515 static_cast<const ShapeInfo*>(info)->fTypeString);
516 });
517
518 return static_cast<const ShapeInfo*>(info);
519}
520
Florin Malita16d0ad02018-01-19 15:07:29 -0500521struct GeometryEffectRec {
522 const Json::Value& fJson;
523 GeometryEffectAttacherT fAttach;
524};
525
Florin Malitaca4439f2018-01-23 10:31:59 -0500526struct AttachShapeContext {
527 AttachShapeContext(AttachContext* ctx,
528 std::vector<sk_sp<sksg::GeometryNode>>* geos,
529 std::vector<GeometryEffectRec>* effects,
530 size_t committedAnimators)
531 : fCtx(ctx)
532 , fGeometryStack(geos)
533 , fGeometryEffectStack(effects)
534 , fCommittedAnimators(committedAnimators) {}
535
536 AttachContext* fCtx;
537 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
538 std::vector<GeometryEffectRec>* fGeometryEffectStack;
539 size_t fCommittedAnimators;
540};
541
542sk_sp<sksg::RenderNode> AttachShape(const Json::Value& jshape, AttachShapeContext* shapeCtx) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500543 if (!jshape.isArray())
Florin Malita094ccde2017-12-30 12:27:00 -0500544 return nullptr;
545
Florin Malitaca4439f2018-01-23 10:31:59 -0500546 SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
Florin Malita094ccde2017-12-30 12:27:00 -0500547
Florin Malita16d0ad02018-01-19 15:07:29 -0500548 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
549 sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
550 sk_sp<sksg::Matrix> shape_matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500551
Florin Malita16d0ad02018-01-19 15:07:29 -0500552 struct ShapeRec {
553 const Json::Value& fJson;
554 const ShapeInfo& fInfo;
555 };
556
557 // First pass (bottom->top):
558 //
559 // * pick up the group transform and opacity
560 // * push local geometry effects onto the stack
561 // * store recs for next pass
562 //
563 std::vector<ShapeRec> recs;
564 for (Json::ArrayIndex i = 0; i < jshape.size(); ++i) {
565 const auto& s = jshape[jshape.size() - 1 - i];
Florin Malita094ccde2017-12-30 12:27:00 -0500566 const auto* info = FindShapeInfo(s);
567 if (!info) {
568 LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
569 continue;
570 }
571
Florin Malita16d0ad02018-01-19 15:07:29 -0500572 recs.push_back({ s, *info });
573
Florin Malita094ccde2017-12-30 12:27:00 -0500574 switch (info->fShapeType) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500575 case ShapeType::kTransform:
Florin Malitaca4439f2018-01-23 10:31:59 -0500576 if ((shape_matrix = AttachMatrix(s, shapeCtx->fCtx, nullptr))) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500577 shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
578 }
Florin Malitaca4439f2018-01-23 10:31:59 -0500579 shape_wrapper = AttachOpacity(s, shapeCtx->fCtx, std::move(shape_wrapper));
Florin Malita16d0ad02018-01-19 15:07:29 -0500580 break;
581 case ShapeType::kGeometryEffect:
582 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500583 shapeCtx->fGeometryEffectStack->push_back(
Florin Malita16d0ad02018-01-19 15:07:29 -0500584 { s, gGeometryEffectAttachers[info->fAttacherIndex] });
585 break;
586 default:
587 break;
588 }
589 }
590
591 // Second pass (top -> bottom, after 2x reverse):
592 //
593 // * track local geometry
594 // * emit local paints
595 //
596 std::vector<sk_sp<sksg::GeometryNode>> geos;
597 std::vector<sk_sp<sksg::RenderNode >> draws;
598 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
599 switch (rec->fInfo.fShapeType) {
Florin Malita094ccde2017-12-30 12:27:00 -0500600 case ShapeType::kGeometry: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500601 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500602 if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
603 shapeCtx->fCtx)) {
Florin Malita094ccde2017-12-30 12:27:00 -0500604 geos.push_back(std::move(geo));
605 }
606 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500607 case ShapeType::kGeometryEffect: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500608 // Apply the current effect and pop from the stack.
609 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaee6de4b2018-01-21 11:13:17 -0500610 if (!geos.empty()) {
611 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaca4439f2018-01-23 10:31:59 -0500612 shapeCtx->fCtx,
Florin Malitaee6de4b2018-01-21 11:13:17 -0500613 std::move(geos));
614 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500615
Florin Malitaca4439f2018-01-23 10:31:59 -0500616 SkASSERT(shapeCtx->fGeometryEffectStack->back().fJson == rec->fJson);
617 SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
Florin Malita16d0ad02018-01-19 15:07:29 -0500618 gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
Florin Malitaca4439f2018-01-23 10:31:59 -0500619 shapeCtx->fGeometryEffectStack->pop_back();
Florin Malita094ccde2017-12-30 12:27:00 -0500620 } break;
621 case ShapeType::kGroup: {
Florin Malitaca4439f2018-01-23 10:31:59 -0500622 AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
623 &geos,
624 shapeCtx->fGeometryEffectStack,
625 shapeCtx->fCommittedAnimators);
626 if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500627 draws.push_back(std::move(subgroup));
Florin Malitaca4439f2018-01-23 10:31:59 -0500628 SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
629 shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -0500630 }
631 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500632 case ShapeType::kPaint: {
633 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500634 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
Florin Malita16d0ad02018-01-19 15:07:29 -0500635 if (!paint || geos.empty())
636 break;
637
638 auto drawGeos = geos;
639
640 // Apply all pending effects from the stack.
Florin Malitaca4439f2018-01-23 10:31:59 -0500641 for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
642 it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
643 drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
Florin Malita18eafd92018-01-04 21:11:55 -0500644 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500645
646 // If we still have multiple geos, reduce using 'merge'.
647 auto geo = drawGeos.size() > 1
648 ? sksg::Merge::Make(std::move(drawGeos), sksg::Merge::Mode::kMerge)
649 : drawGeos[0];
650
651 SkASSERT(geo);
652 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malitaca4439f2018-01-23 10:31:59 -0500653 shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
Florin Malitadacc02b2017-12-31 09:12:31 -0500654 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500655 default:
656 break;
Florin Malita094ccde2017-12-30 12:27:00 -0500657 }
658 }
659
Florin Malita16d0ad02018-01-19 15:07:29 -0500660 // By now we should have popped all local geometry effects.
Florin Malitaca4439f2018-01-23 10:31:59 -0500661 SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita16d0ad02018-01-19 15:07:29 -0500662
663 // Push transformed local geometries to parent list, for subsequent paints.
664 for (const auto& geo : geos) {
Florin Malitaca4439f2018-01-23 10:31:59 -0500665 shapeCtx->fGeometryStack->push_back(shape_matrix
Florin Malita16d0ad02018-01-19 15:07:29 -0500666 ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
667 : std::move(geo));
Florin Malita094ccde2017-12-30 12:27:00 -0500668 }
669
Florin Malita16d0ad02018-01-19 15:07:29 -0500670 // Emit local draws reversed (bottom->top, per spec).
671 for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
672 shape_group->addChild(std::move(*it));
Florin Malita2a8275b2018-01-02 12:52:43 -0500673 }
674
Florin Malita16d0ad02018-01-19 15:07:29 -0500675 return draws.empty() ? nullptr : shape_wrapper;
Florin Malita094ccde2017-12-30 12:27:00 -0500676}
677
Florin Malitaeb87d672018-01-29 15:28:24 -0500678sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx,
679 float* time_bias, float* time_scale) {
680 SkASSERT(jlayer.isObject());
Florin Malita094ccde2017-12-30 12:27:00 -0500681
Florin Malitacf8ed522018-01-25 15:27:33 -0500682 SkString refId;
Florin Malitaeb87d672018-01-29 15:28:24 -0500683 if (!Parse(jlayer["refId"], &refId) || refId.isEmpty()) {
Florin Malitadd22cf962018-01-29 15:42:01 +0000684 LOG("!! Comp layer missing refId\n");
Florin Malita094ccde2017-12-30 12:27:00 -0500685 return nullptr;
686 }
687
Florin Malitadd22cf962018-01-29 15:42:01 +0000688 const auto* comp = ctx->fAssets.find(refId);
689 if (!comp) {
690 LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
Florin Malita094ccde2017-12-30 12:27:00 -0500691 return nullptr;
692 }
693
Florin Malitaeb87d672018-01-29 15:28:24 -0500694 const auto start_time = ParseDefault(jlayer["st"], 0.0f),
695 stretch_time = ParseDefault(jlayer["sr"], 1.0f);
696
697 *time_bias = -start_time;
698 *time_scale = 1 / stretch_time;
699 if (SkScalarIsNaN(*time_scale)) {
700 *time_scale = 1;
701 }
702
Florin Malitadd22cf962018-01-29 15:42:01 +0000703 // TODO: cycle detection
704 return AttachComposition(**comp, ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500705}
706
Florin Malitaeb87d672018-01-29 15:28:24 -0500707sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& jlayer, AttachContext*,
708 float*, float*) {
Florin Malita0e66fba2018-01-09 17:10:18 -0500709 SkASSERT(jlayer.isObject());
Florin Malita094ccde2017-12-30 12:27:00 -0500710
Florin Malitacf8ed522018-01-25 15:27:33 -0500711 const auto size = SkSize::Make(ParseDefault(jlayer["sw"], 0.0f),
712 ParseDefault(jlayer["sh"], 0.0f));
713 const auto hex = ParseDefault(jlayer["sc"], SkString());
Florin Malita0e66fba2018-01-09 17:10:18 -0500714 uint32_t c;
715 if (size.isEmpty() ||
716 !hex.startsWith("#") ||
717 !SkParse::FindHex(hex.c_str() + 1, &c)) {
718 LogFail(jlayer, "Could not parse solid layer");
719 return nullptr;
720 }
721
722 const SkColor color = 0xff000000 | c;
723
724 return sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeSize(size)),
725 sksg::Color::Make(color));
Florin Malita094ccde2017-12-30 12:27:00 -0500726}
727
Florin Malita49328072018-01-08 12:51:12 -0500728sk_sp<sksg::RenderNode> AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) {
729 SkASSERT(jimage.isObject());
730
Florin Malitacf8ed522018-01-25 15:27:33 -0500731 const auto name = ParseDefault(jimage["p"], SkString()),
732 path = ParseDefault(jimage["u"], SkString());
Florin Malita49328072018-01-08 12:51:12 -0500733 if (name.isEmpty())
734 return nullptr;
735
736 // TODO: plumb resource paths explicitly to ResourceProvider?
737 const auto resName = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
738 const auto resStream = ctx->fResources.openStream(resName.c_str());
739 if (!resStream || !resStream->hasLength()) {
740 LOG("!! Could not load image resource: %s\n", resName.c_str());
741 return nullptr;
742 }
743
744 // TODO: non-intrisic image sizing
745 return sksg::Image::Make(
746 SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
747}
748
Florin Malitaeb87d672018-01-29 15:28:24 -0500749sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx,
750 float*, float*) {
Florin Malitadd22cf962018-01-29 15:42:01 +0000751 SkASSERT(layer.isObject());
752
753 SkString refId;
754 if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
755 LOG("!! Image layer missing refId\n");
756 return nullptr;
757 }
758
759 const auto* jimage = ctx->fAssets.find(refId);
760 if (!jimage) {
761 LOG("!! Image asset not found: '%s'\n", refId.c_str());
762 return nullptr;
763 }
764
765 return AttachImageAsset(**jimage, ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500766}
767
Florin Malitaeb87d672018-01-29 15:28:24 -0500768sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*, float*, float*) {
Florin Malita094ccde2017-12-30 12:27:00 -0500769 SkASSERT(layer.isObject());
770
Florin Malita18eafd92018-01-04 21:11:55 -0500771 // Null layers are used solely to drive dependent transforms,
772 // but we use free-floating sksg::Matrices for that purpose.
Florin Malita094ccde2017-12-30 12:27:00 -0500773 return nullptr;
774}
775
Florin Malitaeb87d672018-01-29 15:28:24 -0500776sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx,
777 float*, float*) {
Florin Malita094ccde2017-12-30 12:27:00 -0500778 SkASSERT(layer.isObject());
779
Florin Malita16d0ad02018-01-19 15:07:29 -0500780 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
781 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malitaca4439f2018-01-23 10:31:59 -0500782 AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
783 auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
784
785 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
786 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
787 // due to attached animators. To avoid this, we track committed animators and discard the
788 // orphans here.
789 SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
790 ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
791
792 return shapeNode;
Florin Malita094ccde2017-12-30 12:27:00 -0500793}
794
Florin Malitaeb87d672018-01-29 15:28:24 -0500795sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*, float*, float*) {
Florin Malita094ccde2017-12-30 12:27:00 -0500796 SkASSERT(layer.isObject());
797
798 LOG("?? Text layer stub\n");
799 return nullptr;
800}
801
Florin Malita18eafd92018-01-04 21:11:55 -0500802struct AttachLayerContext {
803 AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
804 : fLayerList(jlayers), fCtx(ctx) {}
805
Florin Malita4a490682018-01-28 14:27:51 -0500806 const Json::Value& fLayerList;
807 AttachContext* fCtx;
808 SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
809 sk_sp<sksg::RenderNode> fCurrentMatte;
Florin Malita18eafd92018-01-04 21:11:55 -0500810
Florin Malita4a490682018-01-28 14:27:51 -0500811 sk_sp<sksg::Matrix> AttachParentLayerMatrix(const Json::Value& jlayer) {
812 SkASSERT(jlayer.isObject());
Florin Malita18eafd92018-01-04 21:11:55 -0500813 SkASSERT(fLayerList.isArray());
814
Florin Malita4a490682018-01-28 14:27:51 -0500815 const auto parent_index = ParseDefault(jlayer["parent"], -1);
816 if (parent_index < 0)
Florin Malita18eafd92018-01-04 21:11:55 -0500817 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500818
Florin Malita4a490682018-01-28 14:27:51 -0500819 if (auto* m = fLayerMatrixMap.find(parent_index))
820 return *m;
Florin Malita18eafd92018-01-04 21:11:55 -0500821
822 for (const auto& l : fLayerList) {
823 if (!l.isObject()) {
824 continue;
825 }
826
Florin Malita4a490682018-01-28 14:27:51 -0500827 if (ParseDefault(l["ind"], -1) == parent_index) {
828 return this->AttachLayerMatrix(l);
Florin Malita18eafd92018-01-04 21:11:55 -0500829 }
830 }
831
832 return nullptr;
833 }
834
835 sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
836 SkASSERT(jlayer.isObject());
837
Florin Malita4a490682018-01-28 14:27:51 -0500838 const auto layer_index = ParseDefault(jlayer["ind"], -1);
839 if (layer_index < 0)
840 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500841
Florin Malita4a490682018-01-28 14:27:51 -0500842 if (auto* m = fLayerMatrixMap.find(layer_index))
843 return *m;
Florin Malita18eafd92018-01-04 21:11:55 -0500844
Florin Malita4a490682018-01-28 14:27:51 -0500845 // Add a stub entry to break recursion cycles.
846 fLayerMatrixMap.set(layer_index, nullptr);
Florin Malita18eafd92018-01-04 21:11:55 -0500847
Florin Malita4a490682018-01-28 14:27:51 -0500848 auto parent_matrix = this->AttachParentLayerMatrix(jlayer);
Florin Malita18eafd92018-01-04 21:11:55 -0500849
Florin Malita4a490682018-01-28 14:27:51 -0500850 return *fLayerMatrixMap.set(layer_index,
851 AttachMatrix(jlayer["ks"],
852 fCtx,
853 this->AttachParentLayerMatrix(jlayer)));
Florin Malita18eafd92018-01-04 21:11:55 -0500854 }
855};
856
Florin Malita25366fa2018-01-23 13:37:59 -0500857SkBlendMode MaskBlendMode(char mode) {
858 switch (mode) {
859 case 'a': return SkBlendMode::kSrcOver; // Additive
860 case 's': return SkBlendMode::kExclusion; // Subtract
861 case 'i': return SkBlendMode::kDstIn; // Intersect
862 case 'l': return SkBlendMode::kLighten; // Lighten
863 case 'd': return SkBlendMode::kDarken; // Darken
864 case 'f': return SkBlendMode::kDifference; // Difference
865 default: break;
866 }
867
868 return SkBlendMode::kSrcOver;
869}
870
871sk_sp<sksg::RenderNode> AttachMask(const Json::Value& jmask,
872 AttachContext* ctx,
873 sk_sp<sksg::RenderNode> childNode) {
874 if (!jmask.isArray())
875 return childNode;
876
877 auto mask_group = sksg::Group::Make();
878
879 for (const auto& m : jmask) {
880 if (!m.isObject())
881 continue;
882
Florin Malitacf8ed522018-01-25 15:27:33 -0500883 const auto inverted = ParseDefault(m["inv"], false);
Florin Malita25366fa2018-01-23 13:37:59 -0500884 // TODO
885 if (inverted) {
886 LogFail(m, "Unsupported inverse mask");
887 continue;
888 }
889
890 auto mask_path = AttachPath(m["pt"], ctx);
891 if (!mask_path) {
892 LogFail(m, "Could not parse mask path");
893 continue;
894 }
895
Florin Malitacf8ed522018-01-25 15:27:33 -0500896 SkString mode;
897 if (!Parse(m["mode"], &mode) ||
898 mode.size() != 1 ||
899 !strcmp(mode.c_str(), "n")) { // "None" masks have no effect.
Florin Malita25366fa2018-01-23 13:37:59 -0500900 continue;
Florin Malitacf8ed522018-01-25 15:27:33 -0500901 }
Florin Malita25366fa2018-01-23 13:37:59 -0500902
903 auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
904 mask_paint->setBlendMode(MaskBlendMode(mode.c_str()[0]));
Florin Malitafc807c82018-01-25 22:35:09 -0500905 BindProperty<ScalarValue>(m["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500906 [mask_paint](const ScalarValue& o) { mask_paint->setOpacity(o * 0.01f); });
Florin Malita25366fa2018-01-23 13:37:59 -0500907
908 mask_group->addChild(sksg::Draw::Make(std::move(mask_path), std::move(mask_paint)));
909 }
910
911 return mask_group->empty()
912 ? childNode
913 : sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
914}
915
Florin Malita18eafd92018-01-04 21:11:55 -0500916sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
917 AttachLayerContext* layerCtx) {
918 if (!jlayer.isObject())
Florin Malita094ccde2017-12-30 12:27:00 -0500919 return nullptr;
920
Florin Malitaeb87d672018-01-29 15:28:24 -0500921 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*,
922 float* time_bias, float* time_scale);
Florin Malita094ccde2017-12-30 12:27:00 -0500923 static constexpr LayerAttacher gLayerAttachers[] = {
924 AttachCompLayer, // 'ty': 0
925 AttachSolidLayer, // 'ty': 1
926 AttachImageLayer, // 'ty': 2
927 AttachNullLayer, // 'ty': 3
928 AttachShapeLayer, // 'ty': 4
929 AttachTextLayer, // 'ty': 5
930 };
931
Florin Malitacf8ed522018-01-25 15:27:33 -0500932 int type = ParseDefault(jlayer["ty"], -1);
Florin Malita094ccde2017-12-30 12:27:00 -0500933 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
934 return nullptr;
935 }
936
Florin Malitacca86f32018-01-29 10:49:49 -0500937 sksg::AnimatorList layer_animators;
938 AttachContext local_ctx =
939 { layerCtx->fCtx->fResources, layerCtx->fCtx->fAssets, layer_animators};
940
Florin Malitaeb87d672018-01-29 15:28:24 -0500941 // Layer attachers may adjust these.
942 float time_bias = 0,
943 time_scale = 1;
944
Florin Malita71cba8f2018-01-09 08:07:14 -0500945 // Layer content.
Florin Malitaeb87d672018-01-29 15:28:24 -0500946 auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale);
Florin Malita25366fa2018-01-23 13:37:59 -0500947 // Optional layer mask.
Florin Malitacca86f32018-01-29 10:49:49 -0500948 layer = AttachMask(jlayer["masksProperties"], &local_ctx, std::move(layer));
Florin Malita25366fa2018-01-23 13:37:59 -0500949 // Optional layer transform.
Florin Malita71cba8f2018-01-09 08:07:14 -0500950 if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
Florin Malita71cba8f2018-01-09 08:07:14 -0500951 layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
952 }
953 // Optional layer opacity.
Florin Malitacca86f32018-01-29 10:49:49 -0500954 layer = AttachOpacity(jlayer["ks"], &local_ctx, std::move(layer));
Florin Malita18eafd92018-01-04 21:11:55 -0500955
Florin Malitaeb87d672018-01-29 15:28:24 -0500956 class LayerController final : public sksg::GroupAnimator {
Florin Malita71cba8f2018-01-09 08:07:14 -0500957 public:
Florin Malitaeb87d672018-01-29 15:28:24 -0500958 LayerController(sksg::AnimatorList&& layer_animators,
959 sk_sp<sksg::OpacityEffect> controlNode,
960 float in, float out,
961 float time_bias, float time_scale)
Florin Malitacca86f32018-01-29 10:49:49 -0500962 : INHERITED(std::move(layer_animators))
963 , fControlNode(std::move(controlNode))
Florin Malita71cba8f2018-01-09 08:07:14 -0500964 , fIn(in)
Florin Malitaeb87d672018-01-29 15:28:24 -0500965 , fOut(out)
966 , fTimeBias(time_bias)
967 , fTimeScale(time_scale) {}
Florin Malita71cba8f2018-01-09 08:07:14 -0500968
Florin Malita35efaa82018-01-22 12:57:06 -0500969 void onTick(float t) override {
Florin Malitacca86f32018-01-29 10:49:49 -0500970 const auto active = (t >= fIn && t <= fOut);
971
Florin Malita71cba8f2018-01-09 08:07:14 -0500972 // Keep the layer fully transparent except for its [in..out] lifespan.
973 // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
Florin Malitacca86f32018-01-29 10:49:49 -0500974 fControlNode->setOpacity(active ? 1 : 0);
975
976 // Dispatch ticks only while active.
977 if (active)
Florin Malitaeb87d672018-01-29 15:28:24 -0500978 this->INHERITED::onTick((t + fTimeBias) * fTimeScale);
Florin Malita71cba8f2018-01-09 08:07:14 -0500979 }
980
981 private:
982 const sk_sp<sksg::OpacityEffect> fControlNode;
983 const float fIn,
Florin Malitaeb87d672018-01-29 15:28:24 -0500984 fOut,
985 fTimeBias,
986 fTimeScale;
Florin Malitacca86f32018-01-29 10:49:49 -0500987
988 using INHERITED = sksg::GroupAnimator;
Florin Malita71cba8f2018-01-09 08:07:14 -0500989 };
990
Florin Malitaeb87d672018-01-29 15:28:24 -0500991 auto controller_node = sksg::OpacityEffect::Make(std::move(layer));
992 const auto in = ParseDefault(jlayer["ip"], 0.0f),
993 out = ParseDefault(jlayer["op"], in);
Florin Malita71cba8f2018-01-09 08:07:14 -0500994
Florin Malitaeb87d672018-01-29 15:28:24 -0500995 if (!jlayer["tm"].isNull()) {
996 LogFail(jlayer["tm"], "Unsupported time remapping");
997 }
998
999 if (in >= out || !controller_node)
Florin Malita71cba8f2018-01-09 08:07:14 -05001000 return nullptr;
1001
Florin Malitacca86f32018-01-29 10:49:49 -05001002 layerCtx->fCtx->fAnimators.push_back(
Florin Malitaeb87d672018-01-29 15:28:24 -05001003 skstd::make_unique<LayerController>(std::move(layer_animators),
1004 controller_node,
1005 in,
1006 out,
1007 time_bias,
1008 time_scale));
Florin Malita71cba8f2018-01-09 08:07:14 -05001009
Florin Malitacf8ed522018-01-25 15:27:33 -05001010 if (ParseDefault(jlayer["td"], false)) {
Florin Malita5f9102f2018-01-10 13:36:22 -05001011 // This layer is a matte. We apply it as a mask to the next layer.
Florin Malitaeb87d672018-01-29 15:28:24 -05001012 layerCtx->fCurrentMatte = std::move(controller_node);
Florin Malita5f9102f2018-01-10 13:36:22 -05001013 return nullptr;
1014 }
1015
1016 if (layerCtx->fCurrentMatte) {
1017 // There is a pending matte. Apply and reset.
Florin Malitaeb87d672018-01-29 15:28:24 -05001018 return sksg::MaskEffect::Make(std::move(controller_node),
1019 std::move(layerCtx->fCurrentMatte));
Florin Malita5f9102f2018-01-10 13:36:22 -05001020 }
1021
Florin Malitaeb87d672018-01-29 15:28:24 -05001022 return controller_node;
Florin Malita094ccde2017-12-30 12:27:00 -05001023}
1024
1025sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
1026 if (!comp.isObject())
1027 return nullptr;
1028
Florin Malita18eafd92018-01-04 21:11:55 -05001029 const auto& jlayers = comp["layers"];
1030 if (!jlayers.isArray())
1031 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -05001032
Florin Malita18eafd92018-01-04 21:11:55 -05001033 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
1034 AttachLayerContext layerCtx(jlayers, ctx);
1035
1036 for (const auto& l : jlayers) {
1037 if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -05001038 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -05001039 }
1040 }
1041
Florin Malita2a8275b2018-01-02 12:52:43 -05001042 if (layers.empty()) {
1043 return nullptr;
1044 }
1045
1046 // Layers are painted in bottom->top order.
1047 auto comp_group = sksg::Group::Make();
1048 for (int i = layers.count() - 1; i >= 0; --i) {
1049 comp_group->addChild(std::move(layers[i]));
1050 }
1051
1052 LOG("** Attached composition '%s': %d layers.\n",
Florin Malitacf8ed522018-01-25 15:27:33 -05001053 ParseDefault(comp["id"], SkString()).c_str(), layers.count());
Florin Malita2a8275b2018-01-02 12:52:43 -05001054
Florin Malita094ccde2017-12-30 12:27:00 -05001055 return comp_group;
1056}
1057
1058} // namespace
1059
Florin Malita49328072018-01-08 12:51:12 -05001060std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
Florin Malita094ccde2017-12-30 12:27:00 -05001061 if (!stream->hasLength()) {
1062 // TODO: handle explicit buffering?
1063 LOG("!! cannot parse streaming content\n");
1064 return nullptr;
1065 }
1066
1067 Json::Value json;
1068 {
1069 auto data = SkData::MakeFromStream(stream, stream->getLength());
1070 if (!data) {
1071 LOG("!! could not read stream\n");
1072 return nullptr;
1073 }
1074
1075 Json::Reader reader;
1076
1077 auto dataStart = static_cast<const char*>(data->data());
1078 if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
1079 LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
1080 return nullptr;
1081 }
1082 }
1083
Florin Malitacf8ed522018-01-25 15:27:33 -05001084 const auto version = ParseDefault(json["v"], SkString());
1085 const auto size = SkSize::Make(ParseDefault(json["w"], 0.0f),
1086 ParseDefault(json["h"], 0.0f));
1087 const auto fps = ParseDefault(json["fr"], -1.0f);
Florin Malita094ccde2017-12-30 12:27:00 -05001088
1089 if (size.isEmpty() || version.isEmpty() || fps < 0) {
1090 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
1091 version.c_str(), size.width(), size.height(), fps);
1092 return nullptr;
1093 }
1094
Florin Malita49328072018-01-08 12:51:12 -05001095 return std::unique_ptr<Animation>(new Animation(res, std::move(version), size, fps, json));
Florin Malita094ccde2017-12-30 12:27:00 -05001096}
1097
Florin Malita49328072018-01-08 12:51:12 -05001098std::unique_ptr<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
1099 class DirectoryResourceProvider final : public ResourceProvider {
1100 public:
1101 explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
1102
1103 std::unique_ptr<SkStream> openStream(const char resource[]) const override {
1104 const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
1105 return SkStream::MakeFromFile(resPath.c_str());
1106 }
1107
1108 private:
1109 const SkString fDir;
1110 };
1111
1112 const auto jsonStream = SkStream::MakeFromFile(path);
1113 if (!jsonStream)
1114 return nullptr;
1115
1116 std::unique_ptr<ResourceProvider> defaultProvider;
1117 if (!res) {
1118 defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
1119 }
1120
1121 return Make(jsonStream.get(), res ? *res : *defaultProvider);
1122}
1123
1124Animation::Animation(const ResourceProvider& resources,
1125 SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
Florin Malita094ccde2017-12-30 12:27:00 -05001126 : fVersion(std::move(version))
1127 , fSize(size)
1128 , fFrameRate(fps)
Florin Malitacf8ed522018-01-25 15:27:33 -05001129 , fInPoint(ParseDefault(json["ip"], 0.0f))
1130 , fOutPoint(SkTMax(ParseDefault(json["op"], SK_ScalarMax), fInPoint)) {
Florin Malita094ccde2017-12-30 12:27:00 -05001131
1132 AssetMap assets;
1133 for (const auto& asset : json["assets"]) {
1134 if (!asset.isObject()) {
1135 continue;
1136 }
1137
Florin Malitadd22cf962018-01-29 15:42:01 +00001138 assets.set(ParseDefault(asset["id"], SkString()), &asset);
Florin Malita094ccde2017-12-30 12:27:00 -05001139 }
1140
Florin Malitacca86f32018-01-29 10:49:49 -05001141 sksg::AnimatorList animators;
Florin Malita35efaa82018-01-22 12:57:06 -05001142 AttachContext ctx = { resources, assets, animators };
1143 auto root = AttachComposition(json, &ctx);
1144
1145 LOG("** Attached %d animators\n", animators.size());
1146
1147 fScene = sksg::Scene::Make(std::move(root), std::move(animators));
Florin Malita094ccde2017-12-30 12:27:00 -05001148
Florin Malitadb385732018-01-09 12:19:32 -05001149 // In case the client calls render before the first tick.
1150 this->animationTick(0);
Florin Malita094ccde2017-12-30 12:27:00 -05001151}
1152
1153Animation::~Animation() = default;
1154
Florin Malita35efaa82018-01-22 12:57:06 -05001155void Animation::setShowInval(bool show) {
1156 if (fScene) {
1157 fScene->setShowInval(show);
1158 }
1159}
1160
Mike Reed29859872018-01-08 08:25:27 -05001161void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
Florin Malita35efaa82018-01-22 12:57:06 -05001162 if (!fScene)
Florin Malita094ccde2017-12-30 12:27:00 -05001163 return;
1164
Mike Reed29859872018-01-08 08:25:27 -05001165 SkAutoCanvasRestore restore(canvas, true);
1166 const SkRect srcR = SkRect::MakeSize(this->size());
1167 if (dstR) {
1168 canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
1169 }
1170 canvas->clipRect(srcR);
Florin Malita35efaa82018-01-22 12:57:06 -05001171 fScene->render(canvas);
Florin Malita094ccde2017-12-30 12:27:00 -05001172}
1173
1174void Animation::animationTick(SkMSec ms) {
Florin Malita35efaa82018-01-22 12:57:06 -05001175 if (!fScene)
1176 return;
1177
Florin Malita094ccde2017-12-30 12:27:00 -05001178 // 't' in the BM model really means 'frame #'
1179 auto t = static_cast<float>(ms) * fFrameRate / 1000;
1180
1181 t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
1182
Florin Malita35efaa82018-01-22 12:57:06 -05001183 fScene->animate(t);
Florin Malita094ccde2017-12-30 12:27:00 -05001184}
1185
Florin Malita54f65c42018-01-16 17:04:30 -05001186} // namespace skottie