blob: 88e42d4e58441865e8efef65a523370146c44ef7 [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 Malita094ccde2017-12-30 12:27:00 -050011#include "SkData.h"
Florin Malita49328072018-01-08 12:51:12 -050012#include "SkImage.h"
Florin Malita094ccde2017-12-30 12:27:00 -050013#include "SkMakeUnique.h"
Florin Malita49328072018-01-08 12:51:12 -050014#include "SkOSPath.h"
Florin Malita094ccde2017-12-30 12:27:00 -050015#include "SkPaint.h"
Florin Malita0e66fba2018-01-09 17:10:18 -050016#include "SkParse.h"
Florin Malita094ccde2017-12-30 12:27:00 -050017#include "SkPoint.h"
Florin Malita38ea40e2018-01-29 16:31:14 -050018#include "SkSGClipEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050019#include "SkSGColor.h"
20#include "SkSGDraw.h"
Florin Malita16d0ad02018-01-19 15:07:29 -050021#include "SkSGGeometryTransform.h"
Florin Malita6aaee592018-01-12 12:25:09 -050022#include "SkSGGradient.h"
Florin Malita094ccde2017-12-30 12:27:00 -050023#include "SkSGGroup.h"
Florin Malita49328072018-01-08 12:51:12 -050024#include "SkSGImage.h"
25#include "SkSGInvalidationController.h"
Florin Malita5f9102f2018-01-10 13:36:22 -050026#include "SkSGMaskEffect.h"
Florin Malitae6345d92018-01-03 23:37:54 -050027#include "SkSGMerge.h"
Florin Malitac0034172018-01-08 16:42:59 -050028#include "SkSGOpacityEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050029#include "SkSGPath.h"
Florin Malita2e1d7e22018-01-02 10:40:00 -050030#include "SkSGRect.h"
Florin Malita41dff6e2018-04-30 23:08:15 -040031#include "SkSGRoundEffect.h"
Florin Malita35efaa82018-01-22 12:57:06 -050032#include "SkSGScene.h"
Florin Malita094ccde2017-12-30 12:27:00 -050033#include "SkSGTransform.h"
Florin Malita51b8c892018-01-07 08:54:24 -050034#include "SkSGTrimEffect.h"
Florin Malita094ccde2017-12-30 12:27:00 -050035#include "SkStream.h"
36#include "SkTArray.h"
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040037#include "SkTHash.h"
Florin Malita5dfd0692018-07-02 15:05:26 -040038#include "SkTLazy.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040039#include "SkTime.h"
40#include "SkTo.h"
41#include "SkottieAdapter.h"
42#include "SkottieAnimator.h"
43#include "SkottieJson.h"
44#include "SkottieValue.h"
Florin Malita094ccde2017-12-30 12:27:00 -050045
46#include <cmath>
Florin Malitae6345d92018-01-03 23:37:54 -050047#include <vector>
48
Florin Malita094ccde2017-12-30 12:27:00 -050049#include "stdlib.h"
50
Florin Malita54f65c42018-01-16 17:04:30 -050051namespace skottie {
Florin Malita094ccde2017-12-30 12:27:00 -050052
Florin Malitacf8ed522018-01-25 15:27:33 -050053#define LOG SkDebugf
54
Florin Malita094ccde2017-12-30 12:27:00 -050055namespace {
56
Florin Malita0cc01b72018-05-10 18:40:35 -040057struct AssetInfo {
Florin Malita7d42c442018-06-14 16:16:01 -040058 const skjson::ObjectValue* fAsset;
59 mutable bool fIsAttaching; // Used for cycle detection
Florin Malita0cc01b72018-05-10 18:40:35 -040060};
61
62using AssetMap = SkTHashMap<SkString, AssetInfo>;
Florin Malita094ccde2017-12-30 12:27:00 -050063
64struct AttachContext {
Florin Malitacca86f32018-01-29 10:49:49 -050065 const ResourceProvider& fResources;
66 const AssetMap& fAssets;
Florin Malita5dfd0692018-07-02 15:05:26 -040067 const float fDuration,
68 fFrameRate;
Florin Malitacca86f32018-01-29 10:49:49 -050069 sksg::AnimatorList& fAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -050070};
71
Florin Malita7d42c442018-06-14 16:16:01 -040072bool LogFail(const skjson::Value& json, const char* msg) {
Florin Malitafa7e9a82018-05-04 15:10:54 -040073 const auto dump = json.toString();
Florin Malita20880782018-05-09 11:35:00 -040074 LOG("!! %s: %s\n", msg, dump.c_str());
Florin Malita094ccde2017-12-30 12:27:00 -050075 return false;
76}
77
Florin Malita7d42c442018-06-14 16:16:01 -040078sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AttachContext* ctx,
79 sk_sp<sksg::Matrix> parentMatrix) {
Florin Malita3be2e102018-07-10 10:20:36 -040080 static const VectorValue g_default_vec_0 = { 0, 0},
81 g_default_vec_100 = {100, 100};
82
Florin Malita13ac1942018-07-12 17:48:36 -040083 auto matrix = sksg::Matrix::Make(SkMatrix::I(), parentMatrix);
Florin Malitaa6e30f72018-03-23 13:41:58 -040084 auto adapter = sk_make_sp<TransformAdapter>(matrix);
Florin Malita3be2e102018-07-10 10:20:36 -040085
86 auto bound = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -040087 [adapter](const VectorValue& a) {
88 adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
Florin Malita3be2e102018-07-10 10:20:36 -040089 }, g_default_vec_0);
90 bound |= BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -040091 [adapter](const VectorValue& p) {
92 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malita3be2e102018-07-10 10:20:36 -040093 }, g_default_vec_0);
94 bound |= BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -040095 [adapter](const VectorValue& s) {
96 adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
Florin Malita3be2e102018-07-10 10:20:36 -040097 }, g_default_vec_100);
Florin Malita1eb98db2018-01-26 15:03:38 -050098
Florin Malita7d42c442018-06-14 16:16:01 -040099 const auto* jrotation = &t["r"];
100 if (jrotation->is<skjson::NullValue>()) {
Florin Malita1eb98db2018-01-26 15:03:38 -0500101 // 3d rotations have separate rx,ry,rz components. While we don't fully support them,
102 // we can still make use of rz.
Florin Malita7d42c442018-06-14 16:16:01 -0400103 jrotation = &t["rz"];
Florin Malita1eb98db2018-01-26 15:03:38 -0500104 }
Florin Malita3be2e102018-07-10 10:20:36 -0400105 bound |= BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400106 [adapter](const ScalarValue& r) {
107 adapter->setRotation(r);
Florin Malita3be2e102018-07-10 10:20:36 -0400108 }, 0.0f);
109 bound |= BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400110 [adapter](const ScalarValue& sk) {
111 adapter->setSkew(sk);
Florin Malita3be2e102018-07-10 10:20:36 -0400112 }, 0.0f);
113 bound |= BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400114 [adapter](const ScalarValue& sa) {
115 adapter->setSkewAxis(sa);
Florin Malita3be2e102018-07-10 10:20:36 -0400116 }, 0.0f);
Florin Malita094ccde2017-12-30 12:27:00 -0500117
Florin Malita13ac1942018-07-12 17:48:36 -0400118 return bound ? matrix : parentMatrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500119}
120
Florin Malita7d42c442018-06-14 16:16:01 -0400121sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue& jtransform, AttachContext* ctx,
Florin Malitac0034172018-01-08 16:42:59 -0500122 sk_sp<sksg::RenderNode> childNode) {
Florin Malita7d42c442018-06-14 16:16:01 -0400123 if (!childNode)
124 return nullptr;
Florin Malitac0034172018-01-08 16:42:59 -0500125
Florin Malitac0034172018-01-08 16:42:59 -0500126 auto opacityNode = sksg::OpacityEffect::Make(childNode);
Florin Malita7ac2e3b2018-05-09 14:54:39 -0400127
128 if (!BindProperty<ScalarValue>(jtransform["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500129 [opacityNode](const ScalarValue& o) {
Florin Malitac0034172018-01-08 16:42:59 -0500130 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500131 opacityNode->setOpacity(o * 0.01f);
Florin Malitaa7e78ee2018-07-10 11:13:06 -0400132 }, 100.0f)) {
Florin Malita7ac2e3b2018-05-09 14:54:39 -0400133 // We can ignore static full opacity.
134 return childNode;
135 }
Florin Malitac0034172018-01-08 16:42:59 -0500136
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400137 return std::move(opacityNode);
Florin Malitac0034172018-01-08 16:42:59 -0500138}
139
Florin Malita7d42c442018-06-14 16:16:01 -0400140sk_sp<sksg::RenderNode> AttachComposition(const skjson::ObjectValue&, AttachContext* ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500141
Florin Malita7d42c442018-06-14 16:16:01 -0400142sk_sp<sksg::Path> AttachPath(const skjson::Value& jpath, AttachContext* ctx) {
Florin Malita25366fa2018-01-23 13:37:59 -0500143 auto path_node = sksg::Path::Make();
Florin Malitafc807c82018-01-25 22:35:09 -0500144 return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
Florin Malitac353ee22018-04-30 21:49:41 -0400145 [path_node](const ShapeValue& p) {
Florin Malita502c3ff2018-07-03 18:10:39 -0400146 // FillType is tracked in the SG node, not in keyframes -- make sure we preserve it.
147 auto path = ValueTraits<ShapeValue>::As<SkPath>(p);
148 path.setFillType(path_node->getFillType());
149 path_node->setPath(path);
Florin Malitac353ee22018-04-30 21:49:41 -0400150 })
Florin Malita25366fa2018-01-23 13:37:59 -0500151 ? path_node
152 : nullptr;
153}
154
Florin Malita7d42c442018-06-14 16:16:01 -0400155sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath, AttachContext* ctx) {
Florin Malita25366fa2018-01-23 13:37:59 -0500156 return AttachPath(jpath["ks"], ctx);
Florin Malita094ccde2017-12-30 12:27:00 -0500157}
158
Florin Malita7d42c442018-06-14 16:16:01 -0400159sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
160 AttachContext* ctx) {
Florin Malita2e1d7e22018-01-02 10:40:00 -0500161 auto rect_node = sksg::RRect::Make();
Florin Malitaa6e30f72018-03-23 13:41:58 -0400162 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
Florin Malita2e1d7e22018-01-02 10:40:00 -0500163
Florin Malitafc807c82018-01-25 22:35:09 -0500164 auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400165 [adapter](const VectorValue& p) {
Florin Malitaa7e78ee2018-07-10 11:13:06 -0400166 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500167 });
Florin Malitafc807c82018-01-25 22:35:09 -0500168 auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400169 [adapter](const VectorValue& s) {
170 adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
Florin Malitaf9590922018-01-09 11:56:09 -0500171 });
Florin Malitafc807c82018-01-25 22:35:09 -0500172 auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400173 [adapter](const ScalarValue& r) {
174 adapter->setRadius(SkSize::Make(r, r));
Florin Malitaf9590922018-01-09 11:56:09 -0500175 });
Florin Malita2e1d7e22018-01-02 10:40:00 -0500176
177 if (!p_attached && !s_attached && !r_attached) {
178 return nullptr;
179 }
180
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400181 return std::move(rect_node);
Florin Malitafbc13f12018-01-04 10:26:35 -0500182}
183
Florin Malita7d42c442018-06-14 16:16:01 -0400184sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
185 AttachContext* ctx) {
Florin Malitafbc13f12018-01-04 10:26:35 -0500186 auto rect_node = sksg::RRect::Make();
Florin Malitaa6e30f72018-03-23 13:41:58 -0400187 auto adapter = sk_make_sp<RRectAdapter>(rect_node);
Florin Malitafbc13f12018-01-04 10:26:35 -0500188
Florin Malitafc807c82018-01-25 22:35:09 -0500189 auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400190 [adapter](const VectorValue& p) {
191 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500192 });
Florin Malitafc807c82018-01-25 22:35:09 -0500193 auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400194 [adapter](const VectorValue& s) {
Florin Malitaf9590922018-01-09 11:56:09 -0500195 const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
Florin Malitaa6e30f72018-03-23 13:41:58 -0400196 adapter->setSize(sz);
197 adapter->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
Florin Malitaf9590922018-01-09 11:56:09 -0500198 });
Florin Malitafbc13f12018-01-04 10:26:35 -0500199
200 if (!p_attached && !s_attached) {
201 return nullptr;
202 }
203
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400204 return std::move(rect_node);
Florin Malita2e1d7e22018-01-02 10:40:00 -0500205}
206
Florin Malita7d42c442018-06-14 16:16:01 -0400207sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
208 AttachContext* ctx) {
Florin Malitaa6e30f72018-03-23 13:41:58 -0400209 static constexpr PolyStarAdapter::Type gTypes[] = {
210 PolyStarAdapter::Type::kStar, // "sy": 1
211 PolyStarAdapter::Type::kPoly, // "sy": 2
Florin Malita02a32b02018-01-04 11:27:09 -0500212 };
213
Florin Malita7d42c442018-06-14 16:16:01 -0400214 const auto type = ParseDefault<int>(jstar["sy"], 0) - 1;
Florin Malita02a32b02018-01-04 11:27:09 -0500215 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
216 LogFail(jstar, "Unknown polystar type");
217 return nullptr;
218 }
219
220 auto path_node = sksg::Path::Make();
Florin Malitaa6e30f72018-03-23 13:41:58 -0400221 auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
Florin Malita02a32b02018-01-04 11:27:09 -0500222
Florin Malitafc807c82018-01-25 22:35:09 -0500223 BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400224 [adapter](const VectorValue& p) {
225 adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
Florin Malitaf9590922018-01-09 11:56:09 -0500226 });
Florin Malitafc807c82018-01-25 22:35:09 -0500227 BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400228 [adapter](const ScalarValue& pt) {
229 adapter->setPointCount(pt);
Florin Malitaf9590922018-01-09 11:56:09 -0500230 });
Florin Malitafc807c82018-01-25 22:35:09 -0500231 BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400232 [adapter](const ScalarValue& ir) {
233 adapter->setInnerRadius(ir);
Florin Malitaf9590922018-01-09 11:56:09 -0500234 });
Florin Malitafc807c82018-01-25 22:35:09 -0500235 BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400236 [adapter](const ScalarValue& otr) {
237 adapter->setOuterRadius(otr);
Florin Malita9661b982018-01-06 14:25:49 -0500238 });
Florin Malitafc807c82018-01-25 22:35:09 -0500239 BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400240 [adapter](const ScalarValue& is) {
241 adapter->setInnerRoundness(is);
Florin Malita9661b982018-01-06 14:25:49 -0500242 });
Florin Malitafc807c82018-01-25 22:35:09 -0500243 BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400244 [adapter](const ScalarValue& os) {
245 adapter->setOuterRoundness(os);
Florin Malita9661b982018-01-06 14:25:49 -0500246 });
Florin Malitafc807c82018-01-25 22:35:09 -0500247 BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400248 [adapter](const ScalarValue& r) {
249 adapter->setRotation(r);
Florin Malitaf9590922018-01-09 11:56:09 -0500250 });
Florin Malita02a32b02018-01-04 11:27:09 -0500251
Kevin Lubickf7621cb2018-04-16 15:51:44 -0400252 return std::move(path_node);
Florin Malita02a32b02018-01-04 11:27:09 -0500253}
254
Florin Malita7d42c442018-06-14 16:16:01 -0400255sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AttachContext* ctx) {
Florin Malita094ccde2017-12-30 12:27:00 -0500256 auto color_node = sksg::Color::Make(SK_ColorBLACK);
Florin Malita7d42c442018-06-14 16:16:01 -0400257 auto color_attached = BindProperty<VectorValue>(jcolor["c"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500258 [color_node](const VectorValue& c) {
259 color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
Florin Malitaf9590922018-01-09 11:56:09 -0500260 });
Florin Malita094ccde2017-12-30 12:27:00 -0500261
Florin Malita1586d852018-01-12 14:27:39 -0500262 return color_attached ? color_node : nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500263}
264
Florin Malita7d42c442018-06-14 16:16:01 -0400265sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AttachContext* ctx) {
266 const skjson::ObjectValue* stops = jgrad["g"];
267 if (!stops)
Florin Malita094ccde2017-12-30 12:27:00 -0500268 return nullptr;
269
Florin Malita7d42c442018-06-14 16:16:01 -0400270 const auto stopCount = ParseDefault<int>((*stops)["p"], -1);
Florin Malita6aaee592018-01-12 12:25:09 -0500271 if (stopCount < 0)
272 return nullptr;
273
274 sk_sp<sksg::Gradient> gradient_node;
Florin Malitaa6e30f72018-03-23 13:41:58 -0400275 sk_sp<GradientAdapter> adapter;
Florin Malita6aaee592018-01-12 12:25:09 -0500276
Florin Malita7d42c442018-06-14 16:16:01 -0400277 if (ParseDefault<int>(jgrad["t"], 1) == 1) {
Florin Malita6aaee592018-01-12 12:25:09 -0500278 auto linear_node = sksg::LinearGradient::Make();
Florin Malitaa6e30f72018-03-23 13:41:58 -0400279 adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
Florin Malita6aaee592018-01-12 12:25:09 -0500280 gradient_node = std::move(linear_node);
281 } else {
282 auto radial_node = sksg::RadialGradient::Make();
Florin Malitaa6e30f72018-03-23 13:41:58 -0400283 adapter = sk_make_sp<RadialGradientAdapter>(radial_node, stopCount);
Florin Malita6aaee592018-01-12 12:25:09 -0500284
285 // TODO: highlight, angle
286 gradient_node = std::move(radial_node);
287 }
288
Florin Malita7d42c442018-06-14 16:16:01 -0400289 BindProperty<VectorValue>((*stops)["k"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400290 [adapter](const VectorValue& stops) {
291 adapter->setColorStops(stops);
Florin Malita6aaee592018-01-12 12:25:09 -0500292 });
Florin Malita7d42c442018-06-14 16:16:01 -0400293 BindProperty<VectorValue>(jgrad["s"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400294 [adapter](const VectorValue& s) {
295 adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
Florin Malita6aaee592018-01-12 12:25:09 -0500296 });
Florin Malita7d42c442018-06-14 16:16:01 -0400297 BindProperty<VectorValue>(jgrad["e"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400298 [adapter](const VectorValue& e) {
299 adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
Florin Malita6aaee592018-01-12 12:25:09 -0500300 });
301
302 return gradient_node;
303}
304
Florin Malita7d42c442018-06-14 16:16:01 -0400305sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint, AttachContext* ctx,
Florin Malita6aaee592018-01-12 12:25:09 -0500306 sk_sp<sksg::PaintNode> paint_node) {
307 if (paint_node) {
308 paint_node->setAntiAlias(true);
309
Florin Malitafc807c82018-01-25 22:35:09 -0500310 BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500311 [paint_node](const ScalarValue& o) {
Florin Malita1586d852018-01-12 14:27:39 -0500312 // BM opacity is [0..100]
Florin Malita2518a0a2018-01-24 18:29:00 -0500313 paint_node->setOpacity(o * 0.01f);
Florin Malita1586d852018-01-12 14:27:39 -0500314 });
Florin Malita6aaee592018-01-12 12:25:09 -0500315 }
316
317 return paint_node;
318}
319
Florin Malita7d42c442018-06-14 16:16:01 -0400320sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx,
Florin Malita6aaee592018-01-12 12:25:09 -0500321 sk_sp<sksg::PaintNode> stroke_node) {
Florin Malita6aaee592018-01-12 12:25:09 -0500322 if (!stroke_node)
323 return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -0500324
325 stroke_node->setStyle(SkPaint::kStroke_Style);
326
Florin Malitafc807c82018-01-25 22:35:09 -0500327 auto width_attached = BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
Florin Malita2518a0a2018-01-24 18:29:00 -0500328 [stroke_node](const ScalarValue& w) {
329 stroke_node->setStrokeWidth(w);
Florin Malitaf9590922018-01-09 11:56:09 -0500330 });
Florin Malita094ccde2017-12-30 12:27:00 -0500331 if (!width_attached)
332 return nullptr;
333
Florin Malita7d42c442018-06-14 16:16:01 -0400334 stroke_node->setStrokeMiter(ParseDefault<SkScalar>(jstroke["ml"], 4.0f));
Florin Malita094ccde2017-12-30 12:27:00 -0500335
336 static constexpr SkPaint::Join gJoins[] = {
337 SkPaint::kMiter_Join,
338 SkPaint::kRound_Join,
339 SkPaint::kBevel_Join,
340 };
Florin Malita7d42c442018-06-14 16:16:01 -0400341 stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault<int>(jstroke["lj"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500342 0, SK_ARRAY_COUNT(gJoins) - 1)]);
343
344 static constexpr SkPaint::Cap gCaps[] = {
345 SkPaint::kButt_Cap,
346 SkPaint::kRound_Cap,
347 SkPaint::kSquare_Cap,
348 };
Florin Malita7d42c442018-06-14 16:16:01 -0400349 stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
Florin Malita094ccde2017-12-30 12:27:00 -0500350 0, SK_ARRAY_COUNT(gCaps) - 1)]);
351
352 return stroke_node;
353}
354
Florin Malita7d42c442018-06-14 16:16:01 -0400355sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
Florin Malita6aaee592018-01-12 12:25:09 -0500356 return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
357}
358
Florin Malita7d42c442018-06-14 16:16:01 -0400359sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
Florin Malita6aaee592018-01-12 12:25:09 -0500360 return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
361}
362
Florin Malita7d42c442018-06-14 16:16:01 -0400363sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx) {
Florin Malita6aaee592018-01-12 12:25:09 -0500364 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
365}
366
Florin Malita7d42c442018-06-14 16:16:01 -0400367sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
368 AttachContext* ctx) {
Florin Malita6aaee592018-01-12 12:25:09 -0500369 return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
370}
371
Florin Malita418e6582018-07-09 16:20:47 -0400372sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
373 std::vector<sksg::Merge::Rec> merge_recs;
374 merge_recs.reserve(geos.size());
375
376 for (const auto& geo : geos) {
377 merge_recs.push_back(
378 {std::move(geo), merge_recs.empty() ? sksg::Merge::Mode::kMerge : mode});
379 }
380
381 return sksg::Merge::Make(std::move(merge_recs));
382}
383
Florin Malitae6345d92018-01-03 23:37:54 -0500384std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
Florin Malita7d42c442018-06-14 16:16:01 -0400385 const skjson::ObjectValue& jmerge, AttachContext*,
386 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
Florin Malitae6345d92018-01-03 23:37:54 -0500387 static constexpr sksg::Merge::Mode gModes[] = {
388 sksg::Merge::Mode::kMerge, // "mm": 1
389 sksg::Merge::Mode::kUnion, // "mm": 2
390 sksg::Merge::Mode::kDifference, // "mm": 3
391 sksg::Merge::Mode::kIntersect, // "mm": 4
392 sksg::Merge::Mode::kXOR , // "mm": 5
393 };
394
Florin Malita7d42c442018-06-14 16:16:01 -0400395 const auto mode = gModes[SkTPin<int>(ParseDefault<int>(jmerge["mm"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500396 0, SK_ARRAY_COUNT(gModes) - 1)];
Florin Malita418e6582018-07-09 16:20:47 -0400397
398 std::vector<sk_sp<sksg::GeometryNode>> merged;
399 merged.push_back(Merge(std::move(geos), mode));
Florin Malitae6345d92018-01-03 23:37:54 -0500400
Florin Malitae6345d92018-01-03 23:37:54 -0500401 return merged;
402}
403
Florin Malita51b8c892018-01-07 08:54:24 -0500404std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
Florin Malita7d42c442018-06-14 16:16:01 -0400405 const skjson::ObjectValue& jtrim, AttachContext* ctx,
406 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
Florin Malita51b8c892018-01-07 08:54:24 -0500407
408 enum class Mode {
409 kMerged, // "m": 1
410 kSeparate, // "m": 2
411 } gModes[] = { Mode::kMerged, Mode::kSeparate };
412
Florin Malita7d42c442018-06-14 16:16:01 -0400413 const auto mode = gModes[SkTPin<int>(ParseDefault<int>(jtrim["m"], 1) - 1,
Florin Malita51b8c892018-01-07 08:54:24 -0500414 0, SK_ARRAY_COUNT(gModes) - 1)];
415
416 std::vector<sk_sp<sksg::GeometryNode>> inputs;
417 if (mode == Mode::kMerged) {
Florin Malita418e6582018-07-09 16:20:47 -0400418 inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge));
Florin Malita51b8c892018-01-07 08:54:24 -0500419 } else {
420 inputs = std::move(geos);
421 }
422
423 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
424 trimmed.reserve(inputs.size());
425 for (const auto& i : inputs) {
Florin Malita69526b02018-03-22 12:20:02 -0400426 const auto trimEffect = sksg::TrimEffect::Make(i);
427 trimmed.push_back(trimEffect);
428
Florin Malitaa6e30f72018-03-23 13:41:58 -0400429 const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
Florin Malitafc807c82018-01-25 22:35:09 -0500430 BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400431 [adapter](const ScalarValue& s) {
432 adapter->setStart(s);
Florin Malita51b8c892018-01-07 08:54:24 -0500433 });
Florin Malitafc807c82018-01-25 22:35:09 -0500434 BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400435 [adapter](const ScalarValue& e) {
436 adapter->setEnd(e);
Florin Malita51b8c892018-01-07 08:54:24 -0500437 });
Florin Malitafc807c82018-01-25 22:35:09 -0500438 BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
Florin Malitaa6e30f72018-03-23 13:41:58 -0400439 [adapter](const ScalarValue& o) {
440 adapter->setOffset(o);
Florin Malita51b8c892018-01-07 08:54:24 -0500441 });
442 }
443
444 return trimmed;
445}
446
Florin Malita41dff6e2018-04-30 23:08:15 -0400447std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
Florin Malita7d42c442018-06-14 16:16:01 -0400448 const skjson::ObjectValue& jtrim, AttachContext* ctx,
449 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
Florin Malita41dff6e2018-04-30 23:08:15 -0400450
451 std::vector<sk_sp<sksg::GeometryNode>> rounded;
452 rounded.reserve(geos.size());
453
454 for (const auto& g : geos) {
455 const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
456 rounded.push_back(roundEffect);
457
458 BindProperty<ScalarValue>(jtrim["r"], &ctx->fAnimators,
459 [roundEffect](const ScalarValue& r) {
460 roundEffect->setRadius(r);
461 });
462 }
463
464 return rounded;
465}
466
Florin Malita7d42c442018-06-14 16:16:01 -0400467using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&, AttachContext*);
Florin Malita094ccde2017-12-30 12:27:00 -0500468static constexpr GeometryAttacherT gGeometryAttachers[] = {
469 AttachPathGeometry,
Florin Malita2e1d7e22018-01-02 10:40:00 -0500470 AttachRRectGeometry,
Florin Malitafbc13f12018-01-04 10:26:35 -0500471 AttachEllipseGeometry,
Florin Malita02a32b02018-01-04 11:27:09 -0500472 AttachPolystarGeometry,
Florin Malita094ccde2017-12-30 12:27:00 -0500473};
474
Florin Malita7d42c442018-06-14 16:16:01 -0400475using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&, AttachContext*);
Florin Malita094ccde2017-12-30 12:27:00 -0500476static constexpr PaintAttacherT gPaintAttachers[] = {
Florin Malita6aaee592018-01-12 12:25:09 -0500477 AttachColorFill,
478 AttachColorStroke,
479 AttachGradientFill,
480 AttachGradientStroke,
Florin Malita094ccde2017-12-30 12:27:00 -0500481};
482
Florin Malitae6345d92018-01-03 23:37:54 -0500483using GeometryEffectAttacherT =
Florin Malita7d42c442018-06-14 16:16:01 -0400484 std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
Florin Malitae6345d92018-01-03 23:37:54 -0500485 AttachContext*,
486 std::vector<sk_sp<sksg::GeometryNode>>&&);
487static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
488 AttachMergeGeometryEffect,
Florin Malita51b8c892018-01-07 08:54:24 -0500489 AttachTrimGeometryEffect,
Florin Malita41dff6e2018-04-30 23:08:15 -0400490 AttachRoundGeometryEffect,
Florin Malitae6345d92018-01-03 23:37:54 -0500491};
492
Florin Malita094ccde2017-12-30 12:27:00 -0500493enum class ShapeType {
494 kGeometry,
Florin Malitae6345d92018-01-03 23:37:54 -0500495 kGeometryEffect,
Florin Malita094ccde2017-12-30 12:27:00 -0500496 kPaint,
497 kGroup,
Florin Malitadacc02b2017-12-31 09:12:31 -0500498 kTransform,
Florin Malita094ccde2017-12-30 12:27:00 -0500499};
500
501struct ShapeInfo {
502 const char* fTypeString;
503 ShapeType fShapeType;
504 uint32_t fAttacherIndex; // index into respective attacher tables
505};
506
Florin Malita7d42c442018-06-14 16:16:01 -0400507const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) {
Florin Malita094ccde2017-12-30 12:27:00 -0500508 static constexpr ShapeInfo gShapeInfo[] = {
Florin Malitafbc13f12018-01-04 10:26:35 -0500509 { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500510 { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
511 { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
Florin Malita16d0ad02018-01-19 15:07:29 -0500512 { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
Florin Malita6aaee592018-01-12 12:25:09 -0500513 { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
Florin Malitae6345d92018-01-03 23:37:54 -0500514 { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
Florin Malita02a32b02018-01-04 11:27:09 -0500515 { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
Florin Malita41dff6e2018-04-30 23:08:15 -0400516 { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect
Florin Malitae6345d92018-01-03 23:37:54 -0500517 { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
Florin Malita02a32b02018-01-04 11:27:09 -0500518 { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
Florin Malita6aaee592018-01-12 12:25:09 -0500519 { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
Florin Malita51b8c892018-01-07 08:54:24 -0500520 { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
Florin Malita16d0ad02018-01-19 15:07:29 -0500521 { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
Florin Malita094ccde2017-12-30 12:27:00 -0500522 };
523
Florin Malita20880782018-05-09 11:35:00 -0400524 SkString type;
Florin Malita7d42c442018-06-14 16:16:01 -0400525 if (!Parse<SkString>(jshape["ty"], &type) || type.isEmpty())
Florin Malita094ccde2017-12-30 12:27:00 -0500526 return nullptr;
527
Florin Malitafa7e9a82018-05-04 15:10:54 -0400528 const auto* info = bsearch(type.c_str(),
Florin Malita094ccde2017-12-30 12:27:00 -0500529 gShapeInfo,
530 SK_ARRAY_COUNT(gShapeInfo),
531 sizeof(ShapeInfo),
532 [](const void* key, const void* info) {
533 return strcmp(static_cast<const char*>(key),
534 static_cast<const ShapeInfo*>(info)->fTypeString);
535 });
536
537 return static_cast<const ShapeInfo*>(info);
538}
539
Florin Malita16d0ad02018-01-19 15:07:29 -0500540struct GeometryEffectRec {
Florin Malita7d42c442018-06-14 16:16:01 -0400541 const skjson::ObjectValue& fJson;
542 GeometryEffectAttacherT fAttach;
Florin Malita16d0ad02018-01-19 15:07:29 -0500543};
544
Florin Malitaca4439f2018-01-23 10:31:59 -0500545struct AttachShapeContext {
546 AttachShapeContext(AttachContext* ctx,
547 std::vector<sk_sp<sksg::GeometryNode>>* geos,
548 std::vector<GeometryEffectRec>* effects,
549 size_t committedAnimators)
550 : fCtx(ctx)
551 , fGeometryStack(geos)
552 , fGeometryEffectStack(effects)
553 , fCommittedAnimators(committedAnimators) {}
554
555 AttachContext* fCtx;
556 std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
557 std::vector<GeometryEffectRec>* fGeometryEffectStack;
558 size_t fCommittedAnimators;
559};
560
Florin Malita7d42c442018-06-14 16:16:01 -0400561sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape, AttachShapeContext* shapeCtx) {
562 if (!jshape)
Florin Malita094ccde2017-12-30 12:27:00 -0500563 return nullptr;
564
Florin Malitaca4439f2018-01-23 10:31:59 -0500565 SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
Florin Malita094ccde2017-12-30 12:27:00 -0500566
Florin Malita16d0ad02018-01-19 15:07:29 -0500567 sk_sp<sksg::Group> shape_group = sksg::Group::Make();
568 sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
569 sk_sp<sksg::Matrix> shape_matrix;
Florin Malita094ccde2017-12-30 12:27:00 -0500570
Florin Malita16d0ad02018-01-19 15:07:29 -0500571 struct ShapeRec {
Florin Malita7d42c442018-06-14 16:16:01 -0400572 const skjson::ObjectValue& fJson;
573 const ShapeInfo& fInfo;
Florin Malita16d0ad02018-01-19 15:07:29 -0500574 };
575
576 // First pass (bottom->top):
577 //
578 // * pick up the group transform and opacity
579 // * push local geometry effects onto the stack
580 // * store recs for next pass
581 //
582 std::vector<ShapeRec> recs;
Florin Malita7d42c442018-06-14 16:16:01 -0400583 for (size_t i = 0; i < jshape->size(); ++i) {
584 const skjson::ObjectValue* shape = (*jshape)[jshape->size() - 1 - i];
585 if (!shape) continue;
586
587 const auto* info = FindShapeInfo(*shape);
Florin Malita094ccde2017-12-30 12:27:00 -0500588 if (!info) {
Florin Malita7d42c442018-06-14 16:16:01 -0400589 LogFail((*shape)["ty"], "Unknown shape");
Florin Malita094ccde2017-12-30 12:27:00 -0500590 continue;
591 }
592
Florin Malita7d42c442018-06-14 16:16:01 -0400593 recs.push_back({ *shape, *info });
Florin Malita16d0ad02018-01-19 15:07:29 -0500594
Florin Malita094ccde2017-12-30 12:27:00 -0500595 switch (info->fShapeType) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500596 case ShapeType::kTransform:
Florin Malita7d42c442018-06-14 16:16:01 -0400597 if ((shape_matrix = AttachMatrix(*shape, shapeCtx->fCtx, nullptr))) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500598 shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
599 }
Florin Malita7d42c442018-06-14 16:16:01 -0400600 shape_wrapper = AttachOpacity(*shape, shapeCtx->fCtx, std::move(shape_wrapper));
Florin Malita16d0ad02018-01-19 15:07:29 -0500601 break;
602 case ShapeType::kGeometryEffect:
603 SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500604 shapeCtx->fGeometryEffectStack->push_back(
Florin Malita7d42c442018-06-14 16:16:01 -0400605 { *shape, gGeometryEffectAttachers[info->fAttacherIndex] });
Florin Malita16d0ad02018-01-19 15:07:29 -0500606 break;
607 default:
608 break;
609 }
610 }
611
612 // Second pass (top -> bottom, after 2x reverse):
613 //
614 // * track local geometry
615 // * emit local paints
616 //
617 std::vector<sk_sp<sksg::GeometryNode>> geos;
618 std::vector<sk_sp<sksg::RenderNode >> draws;
619 for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
620 switch (rec->fInfo.fShapeType) {
Florin Malita094ccde2017-12-30 12:27:00 -0500621 case ShapeType::kGeometry: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500622 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500623 if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
624 shapeCtx->fCtx)) {
Florin Malita094ccde2017-12-30 12:27:00 -0500625 geos.push_back(std::move(geo));
626 }
627 } break;
Florin Malitae6345d92018-01-03 23:37:54 -0500628 case ShapeType::kGeometryEffect: {
Florin Malita16d0ad02018-01-19 15:07:29 -0500629 // Apply the current effect and pop from the stack.
630 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
Florin Malitaee6de4b2018-01-21 11:13:17 -0500631 if (!geos.empty()) {
632 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
Florin Malitaca4439f2018-01-23 10:31:59 -0500633 shapeCtx->fCtx,
Florin Malitaee6de4b2018-01-21 11:13:17 -0500634 std::move(geos));
635 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500636
Florin Malita7d42c442018-06-14 16:16:01 -0400637 SkASSERT(&shapeCtx->fGeometryEffectStack->back().fJson == &rec->fJson);
Florin Malitaca4439f2018-01-23 10:31:59 -0500638 SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
Florin Malita16d0ad02018-01-19 15:07:29 -0500639 gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
Florin Malitaca4439f2018-01-23 10:31:59 -0500640 shapeCtx->fGeometryEffectStack->pop_back();
Florin Malita094ccde2017-12-30 12:27:00 -0500641 } break;
642 case ShapeType::kGroup: {
Florin Malitaca4439f2018-01-23 10:31:59 -0500643 AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
644 &geos,
645 shapeCtx->fGeometryEffectStack,
646 shapeCtx->fCommittedAnimators);
647 if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500648 draws.push_back(std::move(subgroup));
Florin Malitaca4439f2018-01-23 10:31:59 -0500649 SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
650 shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
Florin Malita094ccde2017-12-30 12:27:00 -0500651 }
652 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500653 case ShapeType::kPaint: {
654 SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
Florin Malitaca4439f2018-01-23 10:31:59 -0500655 auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
Florin Malita16d0ad02018-01-19 15:07:29 -0500656 if (!paint || geos.empty())
657 break;
658
659 auto drawGeos = geos;
660
661 // Apply all pending effects from the stack.
Florin Malitaca4439f2018-01-23 10:31:59 -0500662 for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
663 it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
664 drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
Florin Malita18eafd92018-01-04 21:11:55 -0500665 }
Florin Malita16d0ad02018-01-19 15:07:29 -0500666
667 // If we still have multiple geos, reduce using 'merge'.
668 auto geo = drawGeos.size() > 1
Florin Malita418e6582018-07-09 16:20:47 -0400669 ? Merge(std::move(drawGeos), sksg::Merge::Mode::kMerge)
Florin Malita16d0ad02018-01-19 15:07:29 -0500670 : drawGeos[0];
671
672 SkASSERT(geo);
673 draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
Florin Malitaca4439f2018-01-23 10:31:59 -0500674 shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
Florin Malitadacc02b2017-12-31 09:12:31 -0500675 } break;
Florin Malita16d0ad02018-01-19 15:07:29 -0500676 default:
677 break;
Florin Malita094ccde2017-12-30 12:27:00 -0500678 }
679 }
680
Florin Malita16d0ad02018-01-19 15:07:29 -0500681 // By now we should have popped all local geometry effects.
Florin Malitaca4439f2018-01-23 10:31:59 -0500682 SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
Florin Malita16d0ad02018-01-19 15:07:29 -0500683
684 // Push transformed local geometries to parent list, for subsequent paints.
685 for (const auto& geo : geos) {
Florin Malitaca4439f2018-01-23 10:31:59 -0500686 shapeCtx->fGeometryStack->push_back(shape_matrix
Florin Malita16d0ad02018-01-19 15:07:29 -0500687 ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
688 : std::move(geo));
Florin Malita094ccde2017-12-30 12:27:00 -0500689 }
690
Florin Malita16d0ad02018-01-19 15:07:29 -0500691 // Emit local draws reversed (bottom->top, per spec).
692 for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
693 shape_group->addChild(std::move(*it));
Florin Malita2a8275b2018-01-02 12:52:43 -0500694 }
695
Florin Malita16d0ad02018-01-19 15:07:29 -0500696 return draws.empty() ? nullptr : shape_wrapper;
Florin Malita094ccde2017-12-30 12:27:00 -0500697}
698
Florin Malita1022f742018-02-23 11:10:22 -0500699sk_sp<sksg::RenderNode> AttachNestedAnimation(const char* path, AttachContext* ctx) {
700 class SkottieSGAdapter final : public sksg::RenderNode {
701 public:
702 explicit SkottieSGAdapter(sk_sp<Animation> animation)
703 : fAnimation(std::move(animation)) {
704 SkASSERT(fAnimation);
705 }
706
707 protected:
708 SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
709 return SkRect::MakeSize(fAnimation->size());
710 }
711
712 void onRender(SkCanvas* canvas) const override {
713 fAnimation->render(canvas);
714 }
715
716 private:
717 const sk_sp<Animation> fAnimation;
718 };
719
720 class SkottieAnimatorAdapter final : public sksg::Animator {
721 public:
Florin Malita911ae402018-05-31 16:45:29 -0400722 SkottieAnimatorAdapter(sk_sp<Animation> animation, float time_scale)
Florin Malita1022f742018-02-23 11:10:22 -0500723 : fAnimation(std::move(animation))
Florin Malita911ae402018-05-31 16:45:29 -0400724 , fTimeScale(time_scale) {
Florin Malita1022f742018-02-23 11:10:22 -0500725 SkASSERT(fAnimation);
Florin Malita1022f742018-02-23 11:10:22 -0500726 }
727
728 protected:
729 void onTick(float t) {
Florin Malita911ae402018-05-31 16:45:29 -0400730 // TODO: we prolly need more sophisticated timeline mapping for nested animations.
731 fAnimation->seek(t * fTimeScale);
Florin Malita1022f742018-02-23 11:10:22 -0500732 }
733
734 private:
735 const sk_sp<Animation> fAnimation;
Florin Malita911ae402018-05-31 16:45:29 -0400736 const float fTimeScale;
Florin Malita1022f742018-02-23 11:10:22 -0500737 };
738
739 const auto resStream = ctx->fResources.openStream(path);
740 if (!resStream || !resStream->hasLength()) {
741 LOG("!! Could not open: %s\n", path);
742 return nullptr;
743 }
744
Florin Malitac83a0de2018-05-31 12:17:55 -0400745 auto animation = Animation::Make(resStream.get(), &ctx->fResources);
Florin Malita1022f742018-02-23 11:10:22 -0500746 if (!animation) {
747 LOG("!! Could not load nested animation: %s\n", path);
748 return nullptr;
749 }
750
Florin Malita911ae402018-05-31 16:45:29 -0400751
752 ctx->fAnimators.push_back(
753 skstd::make_unique<SkottieAnimatorAdapter>(animation,
754 animation->duration() / ctx->fDuration));
Florin Malita1022f742018-02-23 11:10:22 -0500755
756 return sk_make_sp<SkottieSGAdapter>(std::move(animation));
757}
758
Florin Malita7d42c442018-06-14 16:16:01 -0400759sk_sp<sksg::RenderNode> AttachAssetRef(const skjson::ObjectValue& jlayer, AttachContext* ctx,
760 sk_sp<sksg::RenderNode>(*attach_proc)(const skjson::ObjectValue& comp, AttachContext* ctx)) {
Florin Malita0cc01b72018-05-10 18:40:35 -0400761
Florin Malita7d42c442018-06-14 16:16:01 -0400762 const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
Florin Malita0cc01b72018-05-10 18:40:35 -0400763 if (refId.isEmpty()) {
764 LOG("!! Layer missing refId\n");
765 return nullptr;
766 }
767
768 if (refId.startsWith("$")) {
769 return AttachNestedAnimation(refId.c_str() + 1, ctx);
770 }
771
772 const auto* asset_info = ctx->fAssets.find(refId);
773 if (!asset_info) {
774 LOG("!! Asset not found: '%s'\n", refId.c_str());
775 return nullptr;
776 }
777
778 if (asset_info->fIsAttaching) {
779 LOG("!! Asset cycle detected for: '%s'\n", refId.c_str());
780 return nullptr;
781 }
782
783 asset_info->fIsAttaching = true;
Florin Malita7d42c442018-06-14 16:16:01 -0400784 auto asset = attach_proc(*asset_info->fAsset, ctx);
Florin Malita0cc01b72018-05-10 18:40:35 -0400785 asset_info->fIsAttaching = false;
786
787 return asset;
788}
789
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400790sk_sp<sksg::RenderNode> AttachCompLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx) {
Florin Malita5dfd0692018-07-02 15:05:26 -0400791 const skjson::ObjectValue* time_remap = jlayer["tm"];
Florin Malita7d42c442018-06-14 16:16:01 -0400792 const auto start_time = ParseDefault<float>(jlayer["st"], 0.0f),
793 stretch_time = ParseDefault<float>(jlayer["sr"], 1.0f);
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400794 const auto requires_time_mapping = !SkScalarNearlyEqual(start_time , 0) ||
Florin Malita5dfd0692018-07-02 15:05:26 -0400795 !SkScalarNearlyEqual(stretch_time, 1) ||
796 time_remap;
Florin Malitaeb87d672018-01-29 15:28:24 -0500797
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400798 sksg::AnimatorList local_animators;
799 AttachContext local_ctx = { ctx->fResources,
800 ctx->fAssets,
801 ctx->fDuration,
Florin Malita5dfd0692018-07-02 15:05:26 -0400802 ctx->fFrameRate,
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400803 requires_time_mapping ? local_animators : ctx->fAnimators };
804
805 auto comp_layer = AttachAssetRef(jlayer, &local_ctx, AttachComposition);
806
Florin Malita5dfd0692018-07-02 15:05:26 -0400807 // Applies a bias/scale/remap t-adjustment to child animators.
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400808 class CompTimeMapper final : public sksg::GroupAnimator {
809 public:
810 CompTimeMapper(sksg::AnimatorList&& layer_animators, float time_bias, float time_scale)
811 : INHERITED(std::move(layer_animators))
812 , fTimeBias(time_bias)
813 , fTimeScale(time_scale) {}
814
815 void onTick(float t) override {
Florin Malita5dfd0692018-07-02 15:05:26 -0400816 // When time remapping is active, |t| is driven externally.
817 if (fRemappedTime.isValid()) {
818 t = *fRemappedTime.get();
819 }
820
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400821 this->INHERITED::onTick((t + fTimeBias) * fTimeScale);
822 }
Florin Malita5dfd0692018-07-02 15:05:26 -0400823
824 void remapTime(float t) { fRemappedTime.set(t); }
825
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400826 private:
Florin Malita5dfd0692018-07-02 15:05:26 -0400827 const float fTimeBias,
828 fTimeScale;
829 SkTLazy<float> fRemappedTime;
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400830
831 using INHERITED = sksg::GroupAnimator;
832 };
833
834 if (requires_time_mapping) {
835 const auto t_bias = -start_time,
836 t_scale = sk_ieee_float_divide(1, stretch_time);
Florin Malita5dfd0692018-07-02 15:05:26 -0400837 auto time_mapper = skstd::make_unique<CompTimeMapper>(std::move(local_animators),
838 t_bias, t_scale);
839 if (time_remap) {
840 // The lambda below captures a raw pointer to the mapper object. That should be safe,
841 // because both the lambda and the mapper are scoped/owned by ctx->fAnimators.
842 auto* raw_mapper = time_mapper.get();
843 auto frame_rate = ctx->fFrameRate;
844 BindProperty<ScalarValue>(*time_remap, &ctx->fAnimators,
845 [raw_mapper, frame_rate](const ScalarValue& t) {
846 raw_mapper->remapTime(t * frame_rate);
847 });
848 }
849 ctx->fAnimators.push_back(std::move(time_mapper));
Florin Malitaeb87d672018-01-29 15:28:24 -0500850 }
851
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400852 return comp_layer;
Florin Malita094ccde2017-12-30 12:27:00 -0500853}
854
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400855sk_sp<sksg::RenderNode> AttachSolidLayer(const skjson::ObjectValue& jlayer, AttachContext*) {
Florin Malita7d42c442018-06-14 16:16:01 -0400856 const auto size = SkSize::Make(ParseDefault<float>(jlayer["sw"], 0.0f),
857 ParseDefault<float>(jlayer["sh"], 0.0f));
858 const auto hex = ParseDefault<SkString>(jlayer["sc"], SkString());
Florin Malita0e66fba2018-01-09 17:10:18 -0500859 uint32_t c;
860 if (size.isEmpty() ||
861 !hex.startsWith("#") ||
862 !SkParse::FindHex(hex.c_str() + 1, &c)) {
863 LogFail(jlayer, "Could not parse solid layer");
864 return nullptr;
865 }
866
867 const SkColor color = 0xff000000 | c;
868
869 return sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeSize(size)),
870 sksg::Color::Make(color));
Florin Malita094ccde2017-12-30 12:27:00 -0500871}
872
Florin Malita7d42c442018-06-14 16:16:01 -0400873sk_sp<sksg::RenderNode> AttachImageAsset(const skjson::ObjectValue& jimage, AttachContext* ctx) {
874 const auto name = ParseDefault<SkString>(jimage["p"], SkString()),
875 path = ParseDefault<SkString>(jimage["u"], SkString());
Florin Malita49328072018-01-08 12:51:12 -0500876 if (name.isEmpty())
877 return nullptr;
878
879 // TODO: plumb resource paths explicitly to ResourceProvider?
880 const auto resName = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
881 const auto resStream = ctx->fResources.openStream(resName.c_str());
882 if (!resStream || !resStream->hasLength()) {
883 LOG("!! Could not load image resource: %s\n", resName.c_str());
884 return nullptr;
885 }
886
887 // TODO: non-intrisic image sizing
888 return sksg::Image::Make(
889 SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
890}
891
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400892sk_sp<sksg::RenderNode> AttachImageLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx) {
Florin Malita0cc01b72018-05-10 18:40:35 -0400893 return AttachAssetRef(jlayer, ctx, AttachImageAsset);
Florin Malita094ccde2017-12-30 12:27:00 -0500894}
895
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400896sk_sp<sksg::RenderNode> AttachNullLayer(const skjson::ObjectValue& layer, AttachContext*) {
Florin Malita18eafd92018-01-04 21:11:55 -0500897 // Null layers are used solely to drive dependent transforms,
898 // but we use free-floating sksg::Matrices for that purpose.
Florin Malita094ccde2017-12-30 12:27:00 -0500899 return nullptr;
900}
901
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400902sk_sp<sksg::RenderNode> AttachShapeLayer(const skjson::ObjectValue& layer, AttachContext* ctx) {
Florin Malita16d0ad02018-01-19 15:07:29 -0500903 std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
904 std::vector<GeometryEffectRec> geometryEffectStack;
Florin Malitaca4439f2018-01-23 10:31:59 -0500905 AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
906 auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
907
908 // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
909 // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
910 // due to attached animators. To avoid this, we track committed animators and discard the
911 // orphans here.
912 SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
913 ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
914
915 return shapeNode;
Florin Malita094ccde2017-12-30 12:27:00 -0500916}
917
Florin Malita8c5f9ef2018-06-20 11:19:53 -0400918sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue& layer, AttachContext*) {
Florin Malita094ccde2017-12-30 12:27:00 -0500919 LOG("?? Text layer stub\n");
920 return nullptr;
921}
922
Florin Malita18eafd92018-01-04 21:11:55 -0500923struct AttachLayerContext {
Florin Malita7d42c442018-06-14 16:16:01 -0400924 AttachLayerContext(const skjson::ArrayValue& jlayers, AttachContext* ctx)
925 : fLayerList(jlayers), fCtx(ctx) {}
Florin Malita18eafd92018-01-04 21:11:55 -0500926
Florin Malita7d42c442018-06-14 16:16:01 -0400927 const skjson::ArrayValue& fLayerList;
Florin Malita4a490682018-01-28 14:27:51 -0500928 AttachContext* fCtx;
929 SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
930 sk_sp<sksg::RenderNode> fCurrentMatte;
Florin Malita18eafd92018-01-04 21:11:55 -0500931
Florin Malita7d42c442018-06-14 16:16:01 -0400932 sk_sp<sksg::Matrix> AttachLayerMatrix(const skjson::ObjectValue& jlayer) {
933 const auto layer_index = ParseDefault<int>(jlayer["ind"], -1);
Florin Malita4a490682018-01-28 14:27:51 -0500934 if (layer_index < 0)
935 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500936
Florin Malita4a490682018-01-28 14:27:51 -0500937 if (auto* m = fLayerMatrixMap.find(layer_index))
938 return *m;
Florin Malita18eafd92018-01-04 21:11:55 -0500939
Florin Malita2919b612018-05-10 12:44:07 -0400940 return this->AttachLayerMatrixImpl(jlayer, layer_index);
941 }
942
943private:
Florin Malita7d42c442018-06-14 16:16:01 -0400944 sk_sp<sksg::Matrix> AttachParentLayerMatrix(const skjson::ObjectValue& jlayer,
945 int layer_index) {
946 const auto parent_index = ParseDefault<int>(jlayer["parent"], -1);
Florin Malita2919b612018-05-10 12:44:07 -0400947 if (parent_index < 0 || parent_index == layer_index)
948 return nullptr;
949
950 if (auto* m = fLayerMatrixMap.find(parent_index))
951 return *m;
952
Florin Malita7d42c442018-06-14 16:16:01 -0400953 for (const skjson::ObjectValue* l : fLayerList) {
954 if (!l) continue;
955
956 if (ParseDefault<int>((*l)["ind"], -1) == parent_index) {
957 return this->AttachLayerMatrixImpl(*l, parent_index);
Florin Malita2919b612018-05-10 12:44:07 -0400958 }
959 }
960
961 return nullptr;
962 }
963
Florin Malita7d42c442018-06-14 16:16:01 -0400964 sk_sp<sksg::Matrix> AttachLayerMatrixImpl(const skjson::ObjectValue& jlayer, int layer_index) {
Florin Malita2919b612018-05-10 12:44:07 -0400965 SkASSERT(!fLayerMatrixMap.find(layer_index));
966
Florin Malita4a490682018-01-28 14:27:51 -0500967 // Add a stub entry to break recursion cycles.
968 fLayerMatrixMap.set(layer_index, nullptr);
Florin Malita18eafd92018-01-04 21:11:55 -0500969
Florin Malita2919b612018-05-10 12:44:07 -0400970 auto parent_matrix = this->AttachParentLayerMatrix(jlayer, layer_index);
Florin Malita18eafd92018-01-04 21:11:55 -0500971
Florin Malita7d42c442018-06-14 16:16:01 -0400972 if (const skjson::ObjectValue* jtransform = jlayer["ks"]) {
973 return *fLayerMatrixMap.set(layer_index, AttachMatrix(*jtransform, fCtx,
974 std::move(parent_matrix)));
975
976 }
977 return nullptr;
Florin Malita18eafd92018-01-04 21:11:55 -0500978 }
979};
980
Florin Malita8deab3a2018-06-29 13:05:03 -0400981struct MaskInfo {
Florin Malita418e6582018-07-09 16:20:47 -0400982 SkBlendMode fBlendMode; // used when masking with layers/blending
983 sksg::Merge::Mode fMergeMode; // used when clipping
984 bool fInvertGeometry;
Florin Malita8deab3a2018-06-29 13:05:03 -0400985};
986
987const MaskInfo* GetMaskInfo(char mode) {
Florin Malita418e6582018-07-09 16:20:47 -0400988 static constexpr MaskInfo k_add_info =
989 { SkBlendMode::kSrcOver , sksg::Merge::Mode::kUnion , false };
990 static constexpr MaskInfo k_int_info =
991 { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , false };
Florin Malita8deab3a2018-06-29 13:05:03 -0400992 // AE 'subtract' is the same as 'intersect' + inverted geometry
993 // (draws the opacity-adjusted paint *outside* the shape).
Florin Malita418e6582018-07-09 16:20:47 -0400994 static constexpr MaskInfo k_sub_info =
995 { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , true };
996 static constexpr MaskInfo k_dif_info =
997 { SkBlendMode::kDifference, sksg::Merge::Mode::kDifference, false };
Florin Malita8deab3a2018-06-29 13:05:03 -0400998
Florin Malita25366fa2018-01-23 13:37:59 -0500999 switch (mode) {
Florin Malita8deab3a2018-06-29 13:05:03 -04001000 case 'a': return &k_add_info;
1001 case 'f': return &k_dif_info;
1002 case 'i': return &k_int_info;
1003 case 's': return &k_sub_info;
Florin Malita25366fa2018-01-23 13:37:59 -05001004 default: break;
1005 }
1006
Florin Malita8deab3a2018-06-29 13:05:03 -04001007 return nullptr;
Florin Malita25366fa2018-01-23 13:37:59 -05001008}
1009
Florin Malita7d42c442018-06-14 16:16:01 -04001010sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
Florin Malita25366fa2018-01-23 13:37:59 -05001011 AttachContext* ctx,
1012 sk_sp<sksg::RenderNode> childNode) {
Florin Malita7d42c442018-06-14 16:16:01 -04001013 if (!jmask) return childNode;
Florin Malita25366fa2018-01-23 13:37:59 -05001014
Florin Malita0c51c212018-04-26 14:13:14 -04001015 struct MaskRecord {
Florin Malita418e6582018-07-09 16:20:47 -04001016 sk_sp<sksg::Path> mask_path; // for clipping and masking
1017 sk_sp<sksg::Color> mask_paint; // for masking
1018 sksg::Merge::Mode merge_mode; // for clipping
Florin Malita0c51c212018-04-26 14:13:14 -04001019 };
1020
1021 SkSTArray<4, MaskRecord, true> mask_stack;
1022
Florin Malita53619352018-06-21 12:28:14 -04001023 bool has_opacity = false;
Florin Malita25366fa2018-01-23 13:37:59 -05001024
Florin Malita7d42c442018-06-14 16:16:01 -04001025 for (const skjson::ObjectValue* m : *jmask) {
1026 if (!m) continue;
Florin Malita25366fa2018-01-23 13:37:59 -05001027
Florin Malita8deab3a2018-06-29 13:05:03 -04001028 SkString mode;
1029 if (!Parse<SkString>((*m)["mode"], &mode) || mode.size() != 1) {
1030 LogFail((*m)["mode"], "Invalid mask mode");
1031 continue;
1032 }
1033
1034 if (mode[0] == 'n') {
1035 // "None" masks have no effect.
1036 continue;
1037 }
1038
1039 const auto* mask_info = GetMaskInfo(mode[0]);
1040 if (!mask_info) {
1041 LOG("?? Unsupported mask mode: '%c'\n", mode[0]);
1042 continue;
1043 }
1044
Florin Malita7d42c442018-06-14 16:16:01 -04001045 auto mask_path = AttachPath((*m)["pt"], ctx);
Florin Malita25366fa2018-01-23 13:37:59 -05001046 if (!mask_path) {
Florin Malita7d42c442018-06-14 16:16:01 -04001047 LogFail(*m, "Could not parse mask path");
Florin Malita25366fa2018-01-23 13:37:59 -05001048 continue;
1049 }
1050
Florin Malita8deab3a2018-06-29 13:05:03 -04001051 // "inv" is cumulative with mask info fInvertGeometry
1052 const auto inverted =
1053 (mask_info->fInvertGeometry != ParseDefault<bool>((*m)["inv"], false));
1054 mask_path->setFillType(inverted ? SkPath::kInverseWinding_FillType
1055 : SkPath::kWinding_FillType);
Florin Malita25366fa2018-01-23 13:37:59 -05001056
1057 auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
Florin Malitaaa71c892018-01-30 09:27:01 -05001058 mask_paint->setAntiAlias(true);
Florin Malita8deab3a2018-06-29 13:05:03 -04001059 // First mask in the stack initializes the mask buffer.
1060 mask_paint->setBlendMode(mask_stack.empty() ? SkBlendMode::kSrc
1061 : mask_info->fBlendMode);
Florin Malita0c51c212018-04-26 14:13:14 -04001062
Florin Malita53619352018-06-21 12:28:14 -04001063 has_opacity |= BindProperty<ScalarValue>((*m)["o"], &ctx->fAnimators,
1064 [mask_paint](const ScalarValue& o) {
1065 mask_paint->setOpacity(o * 0.01f);
Florin Malitaa7e78ee2018-07-10 11:13:06 -04001066 }, 100.0f);
Florin Malita0c51c212018-04-26 14:13:14 -04001067
Florin Malita418e6582018-07-09 16:20:47 -04001068 mask_stack.push_back({mask_path, mask_paint, mask_info->fMergeMode});
Florin Malita25366fa2018-01-23 13:37:59 -05001069 }
1070
Florin Malita0c51c212018-04-26 14:13:14 -04001071 if (mask_stack.empty())
1072 return childNode;
1073
Florin Malita418e6582018-07-09 16:20:47 -04001074 // If the masks are fully opaque, we can clip.
1075 if (!has_opacity) {
1076 sk_sp<sksg::GeometryNode> clip_node;
1077
1078 if (mask_stack.count() == 1) {
1079 // Single path -> just clip.
1080 clip_node = std::move(mask_stack.front().mask_path);
1081 } else {
1082 // Multiple clip paths -> merge.
1083 std::vector<sksg::Merge::Rec> merge_recs;
1084 merge_recs.reserve(SkToSizeT(mask_stack.count()));
1085
1086 for (const auto& mask : mask_stack) {
1087 const auto mode = merge_recs.empty() ? sksg::Merge::Mode::kMerge : mask.merge_mode;
1088 merge_recs.push_back({std::move(mask.mask_path), mode});
1089 }
1090 clip_node = sksg::Merge::Make(std::move(merge_recs));
1091 }
1092
1093 return sksg::ClipEffect::Make(std::move(childNode), std::move(clip_node), true);
Florin Malita0c51c212018-04-26 14:13:14 -04001094 }
1095
1096 auto mask_group = sksg::Group::Make();
1097 for (const auto& rec : mask_stack) {
1098 mask_group->addChild(sksg::Draw::Make(std::move(rec.mask_path),
1099 std::move(rec.mask_paint)));
1100
1101 }
1102
1103 return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
Florin Malita25366fa2018-01-23 13:37:59 -05001104}
1105
Florin Malita7d42c442018-06-14 16:16:01 -04001106sk_sp<sksg::RenderNode> AttachLayer(const skjson::ObjectValue* jlayer,
1107 AttachLayerContext* layerCtx) {
1108 if (!jlayer) return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -05001109
Florin Malita4cbfb302018-07-10 14:34:29 -04001110 if (!(*jlayer)["ef"].is<skjson::NullValue>()) {
1111 LOG("?? Unsupported layer effect.\n");
1112 }
1113
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001114 using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const skjson::ObjectValue&, AttachContext*);
Florin Malita094ccde2017-12-30 12:27:00 -05001115 static constexpr LayerAttacher gLayerAttachers[] = {
1116 AttachCompLayer, // 'ty': 0
1117 AttachSolidLayer, // 'ty': 1
1118 AttachImageLayer, // 'ty': 2
1119 AttachNullLayer, // 'ty': 3
1120 AttachShapeLayer, // 'ty': 4
1121 AttachTextLayer, // 'ty': 5
1122 };
1123
Florin Malita7d42c442018-06-14 16:16:01 -04001124 int type = ParseDefault<int>((*jlayer)["ty"], -1);
Florin Malita094ccde2017-12-30 12:27:00 -05001125 if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
1126 return nullptr;
1127 }
1128
Florin Malitacca86f32018-01-29 10:49:49 -05001129 sksg::AnimatorList layer_animators;
Florin Malita1022f742018-02-23 11:10:22 -05001130 AttachContext local_ctx = { layerCtx->fCtx->fResources,
1131 layerCtx->fCtx->fAssets,
Florin Malita911ae402018-05-31 16:45:29 -04001132 layerCtx->fCtx->fDuration,
Florin Malita5dfd0692018-07-02 15:05:26 -04001133 layerCtx->fCtx->fFrameRate,
Florin Malita1022f742018-02-23 11:10:22 -05001134 layer_animators};
Florin Malitacca86f32018-01-29 10:49:49 -05001135
Florin Malita71cba8f2018-01-09 08:07:14 -05001136 // Layer content.
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001137 auto layer = gLayerAttachers[type](*jlayer, &local_ctx);
Florin Malita38ea40e2018-01-29 16:31:14 -05001138
1139 // Clip layers with explicit dimensions.
Florin Malitafa7e9a82018-05-04 15:10:54 -04001140 float w = 0, h = 0;
Florin Malita7d42c442018-06-14 16:16:01 -04001141 if (Parse<float>((*jlayer)["w"], &w) && Parse<float>((*jlayer)["h"], &h)) {
Florin Malita38ea40e2018-01-29 16:31:14 -05001142 layer = sksg::ClipEffect::Make(std::move(layer),
1143 sksg::Rect::Make(SkRect::MakeWH(w, h)),
1144 true);
1145 }
1146
Florin Malita25366fa2018-01-23 13:37:59 -05001147 // Optional layer mask.
Florin Malita7d42c442018-06-14 16:16:01 -04001148 layer = AttachMask((*jlayer)["masksProperties"], &local_ctx, std::move(layer));
Florin Malita38ea40e2018-01-29 16:31:14 -05001149
Florin Malita25366fa2018-01-23 13:37:59 -05001150 // Optional layer transform.
Florin Malita7d42c442018-06-14 16:16:01 -04001151 if (auto layerMatrix = layerCtx->AttachLayerMatrix(*jlayer)) {
Florin Malita71cba8f2018-01-09 08:07:14 -05001152 layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
1153 }
Florin Malita38ea40e2018-01-29 16:31:14 -05001154
Florin Malita71cba8f2018-01-09 08:07:14 -05001155 // Optional layer opacity.
Florin Malita7d42c442018-06-14 16:16:01 -04001156 // TODO: de-dupe this "ks" lookup with matrix above.
1157 if (const skjson::ObjectValue* jtransform = (*jlayer)["ks"]) {
1158 layer = AttachOpacity(*jtransform, &local_ctx, std::move(layer));
1159 }
Florin Malita18eafd92018-01-04 21:11:55 -05001160
Florin Malitaeb87d672018-01-29 15:28:24 -05001161 class LayerController final : public sksg::GroupAnimator {
Florin Malita71cba8f2018-01-09 08:07:14 -05001162 public:
Florin Malitaeb87d672018-01-29 15:28:24 -05001163 LayerController(sksg::AnimatorList&& layer_animators,
1164 sk_sp<sksg::OpacityEffect> controlNode,
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001165 float in, float out)
Florin Malitacca86f32018-01-29 10:49:49 -05001166 : INHERITED(std::move(layer_animators))
1167 , fControlNode(std::move(controlNode))
Florin Malita71cba8f2018-01-09 08:07:14 -05001168 , fIn(in)
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001169 , fOut(out) {}
Florin Malita71cba8f2018-01-09 08:07:14 -05001170
Florin Malita35efaa82018-01-22 12:57:06 -05001171 void onTick(float t) override {
Florin Malitacca86f32018-01-29 10:49:49 -05001172 const auto active = (t >= fIn && t <= fOut);
1173
Florin Malita71cba8f2018-01-09 08:07:14 -05001174 // Keep the layer fully transparent except for its [in..out] lifespan.
1175 // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
Florin Malitacca86f32018-01-29 10:49:49 -05001176 fControlNode->setOpacity(active ? 1 : 0);
1177
1178 // Dispatch ticks only while active.
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001179 if (active) this->INHERITED::onTick(t);
Florin Malita71cba8f2018-01-09 08:07:14 -05001180 }
1181
1182 private:
1183 const sk_sp<sksg::OpacityEffect> fControlNode;
1184 const float fIn,
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001185 fOut;
Florin Malitacca86f32018-01-29 10:49:49 -05001186
1187 using INHERITED = sksg::GroupAnimator;
Florin Malita71cba8f2018-01-09 08:07:14 -05001188 };
1189
Florin Malitaeb87d672018-01-29 15:28:24 -05001190 auto controller_node = sksg::OpacityEffect::Make(std::move(layer));
Florin Malita7d42c442018-06-14 16:16:01 -04001191 const auto in = ParseDefault<float>((*jlayer)["ip"], 0.0f),
1192 out = ParseDefault<float>((*jlayer)["op"], in);
Florin Malita71cba8f2018-01-09 08:07:14 -05001193
Florin Malitaeb87d672018-01-29 15:28:24 -05001194 if (in >= out || !controller_node)
Florin Malita71cba8f2018-01-09 08:07:14 -05001195 return nullptr;
1196
Florin Malitacca86f32018-01-29 10:49:49 -05001197 layerCtx->fCtx->fAnimators.push_back(
Florin Malita8c5f9ef2018-06-20 11:19:53 -04001198 skstd::make_unique<LayerController>(std::move(layer_animators), controller_node, in, out));
Florin Malita71cba8f2018-01-09 08:07:14 -05001199
Florin Malita7d42c442018-06-14 16:16:01 -04001200 if (ParseDefault<bool>((*jlayer)["td"], false)) {
Florin Malita5f9102f2018-01-10 13:36:22 -05001201 // This layer is a matte. We apply it as a mask to the next layer.
Florin Malitaeb87d672018-01-29 15:28:24 -05001202 layerCtx->fCurrentMatte = std::move(controller_node);
Florin Malita5f9102f2018-01-10 13:36:22 -05001203 return nullptr;
1204 }
1205
1206 if (layerCtx->fCurrentMatte) {
1207 // There is a pending matte. Apply and reset.
Florin Malitaa016be92018-03-05 14:01:41 -05001208 static constexpr sksg::MaskEffect::Mode gMaskModes[] = {
1209 sksg::MaskEffect::Mode::kNormal, // tt: 1
1210 sksg::MaskEffect::Mode::kInvert, // tt: 2
1211 };
Florin Malita7d42c442018-06-14 16:16:01 -04001212 const auto matteType = ParseDefault<int>((*jlayer)["tt"], 1) - 1;
Florin Malitaa016be92018-03-05 14:01:41 -05001213
1214 if (matteType >= 0 && matteType < SkTo<int>(SK_ARRAY_COUNT(gMaskModes))) {
1215 return sksg::MaskEffect::Make(std::move(controller_node),
1216 std::move(layerCtx->fCurrentMatte),
1217 gMaskModes[matteType]);
1218 }
1219 layerCtx->fCurrentMatte.reset();
Florin Malita5f9102f2018-01-10 13:36:22 -05001220 }
1221
Kevin Lubickf7621cb2018-04-16 15:51:44 -04001222 return std::move(controller_node);
Florin Malita094ccde2017-12-30 12:27:00 -05001223}
1224
Florin Malita7d42c442018-06-14 16:16:01 -04001225sk_sp<sksg::RenderNode> AttachComposition(const skjson::ObjectValue& comp, AttachContext* ctx) {
1226 const skjson::ArrayValue* jlayers = comp["layers"];
1227 if (!jlayers) return nullptr;
Florin Malita094ccde2017-12-30 12:27:00 -05001228
Florin Malita18eafd92018-01-04 21:11:55 -05001229 SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
Florin Malita7d42c442018-06-14 16:16:01 -04001230 AttachLayerContext layerCtx(*jlayers, ctx);
Florin Malita18eafd92018-01-04 21:11:55 -05001231
Florin Malita7d42c442018-06-14 16:16:01 -04001232 for (const auto& l : *jlayers) {
Florin Malita18eafd92018-01-04 21:11:55 -05001233 if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
Florin Malita2a8275b2018-01-02 12:52:43 -05001234 layers.push_back(std::move(layer_fragment));
Florin Malita094ccde2017-12-30 12:27:00 -05001235 }
1236 }
1237
Florin Malita2a8275b2018-01-02 12:52:43 -05001238 if (layers.empty()) {
1239 return nullptr;
1240 }
1241
1242 // Layers are painted in bottom->top order.
1243 auto comp_group = sksg::Group::Make();
1244 for (int i = layers.count() - 1; i >= 0; --i) {
1245 comp_group->addChild(std::move(layers[i]));
1246 }
1247
Kevin Lubickf7621cb2018-04-16 15:51:44 -04001248 return std::move(comp_group);
Florin Malita094ccde2017-12-30 12:27:00 -05001249}
1250
1251} // namespace
1252
Florin Malitac83a0de2018-05-31 12:17:55 -04001253sk_sp<Animation> Animation::Make(SkStream* stream, const ResourceProvider* provider, Stats* stats) {
Florin Malita6eb85a12018-04-30 10:32:18 -04001254 Stats stats_storage;
1255 if (!stats)
1256 stats = &stats_storage;
1257 memset(stats, 0, sizeof(struct Stats));
1258
Florin Malita094ccde2017-12-30 12:27:00 -05001259 if (!stream->hasLength()) {
1260 // TODO: handle explicit buffering?
1261 LOG("!! cannot parse streaming content\n");
1262 return nullptr;
1263 }
1264
Florin Malitafa7e9a82018-05-04 15:10:54 -04001265 stats->fJsonSize = stream->getLength();
Florin Malita6eb85a12018-04-30 10:32:18 -04001266 const auto t0 = SkTime::GetMSecs();
1267
Florin Malita7d42c442018-06-14 16:16:01 -04001268 auto data = SkData::MakeFromStream(stream, stream->getLength());
1269 if (!data) {
1270 SkDebugf("!! Failed to read the input stream.\n");
Florin Malitafa7e9a82018-05-04 15:10:54 -04001271 return nullptr;
Florin Malita7d42c442018-06-14 16:16:01 -04001272 }
1273
1274 const skjson::DOM dom(static_cast<const char*>(data->data()), data->size());
1275 if (!dom.root().is<skjson::ObjectValue>()) {
1276 // TODO: more error info.
1277 SkDebugf("!! Failed to parse JSON input.\n");
1278 return nullptr;
1279 }
1280 const auto& json = dom.root().as<skjson::ObjectValue>();
Florin Malita094ccde2017-12-30 12:27:00 -05001281
Florin Malita6eb85a12018-04-30 10:32:18 -04001282 const auto t1 = SkTime::GetMSecs();
1283 stats->fJsonParseTimeMS = t1 - t0;
1284
Florin Malita7d42c442018-06-14 16:16:01 -04001285 const auto version = ParseDefault<SkString>(json["v"], SkString());
1286 const auto size = SkSize::Make(ParseDefault<float>(json["w"], 0.0f),
1287 ParseDefault<float>(json["h"], 0.0f));
1288 const auto fps = ParseDefault<float>(json["fr"], -1.0f);
Florin Malita094ccde2017-12-30 12:27:00 -05001289
Florin Malita1022f742018-02-23 11:10:22 -05001290 if (size.isEmpty() || version.isEmpty() || fps <= 0) {
Florin Malita094ccde2017-12-30 12:27:00 -05001291 LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
1292 version.c_str(), size.width(), size.height(), fps);
1293 return nullptr;
1294 }
1295
Florin Malitac83a0de2018-05-31 12:17:55 -04001296 class NullResourceProvider final : public ResourceProvider {
1297 std::unique_ptr<SkStream> openStream(const char[]) const { return nullptr; }
1298 };
1299
Kevin Lubickf14bc982018-06-01 13:00:35 -04001300 NullResourceProvider null_provider;
Florin Malitac83a0de2018-05-31 12:17:55 -04001301 const auto anim = sk_sp<Animation>(new Animation(provider ? *provider : null_provider,
1302 std::move(version), size, fps, json, stats));
Florin Malita6eb85a12018-04-30 10:32:18 -04001303 const auto t2 = SkTime::GetMSecs();
1304 stats->fSceneParseTimeMS = t2 - t1;
1305 stats->fTotalLoadTimeMS = t2 - t0;
1306
1307 return anim;
Florin Malita094ccde2017-12-30 12:27:00 -05001308}
1309
Florin Malita6eb85a12018-04-30 10:32:18 -04001310sk_sp<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res,
1311 Stats* stats) {
Florin Malita49328072018-01-08 12:51:12 -05001312 class DirectoryResourceProvider final : public ResourceProvider {
1313 public:
1314 explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
1315
1316 std::unique_ptr<SkStream> openStream(const char resource[]) const override {
1317 const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
1318 return SkStream::MakeFromFile(resPath.c_str());
1319 }
1320
1321 private:
1322 const SkString fDir;
1323 };
1324
1325 const auto jsonStream = SkStream::MakeFromFile(path);
1326 if (!jsonStream)
1327 return nullptr;
1328
1329 std::unique_ptr<ResourceProvider> defaultProvider;
1330 if (!res) {
1331 defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
1332 }
1333
Florin Malitac83a0de2018-05-31 12:17:55 -04001334 return Make(jsonStream.get(), res ? res : defaultProvider.get(), stats);
Florin Malita49328072018-01-08 12:51:12 -05001335}
1336
1337Animation::Animation(const ResourceProvider& resources,
Florin Malita7d42c442018-06-14 16:16:01 -04001338 SkString version, const SkSize& size, SkScalar fps,
1339 const skjson::ObjectValue& json, Stats* stats)
Florin Malita094ccde2017-12-30 12:27:00 -05001340 : fVersion(std::move(version))
1341 , fSize(size)
1342 , fFrameRate(fps)
Florin Malita7d42c442018-06-14 16:16:01 -04001343 , fInPoint(ParseDefault<float>(json["ip"], 0.0f))
1344 , fOutPoint(SkTMax(ParseDefault<float>(json["op"], SK_ScalarMax), fInPoint)) {
Florin Malita094ccde2017-12-30 12:27:00 -05001345
1346 AssetMap assets;
Florin Malita7d42c442018-06-14 16:16:01 -04001347 if (const skjson::ArrayValue* jassets = json["assets"]) {
1348 for (const skjson::ObjectValue* asset : *jassets) {
1349 if (asset) {
1350 assets.set(ParseDefault<SkString>((*asset)["id"], SkString()), { asset, false });
1351 }
Florin Malita094ccde2017-12-30 12:27:00 -05001352 }
Florin Malita094ccde2017-12-30 12:27:00 -05001353 }
1354
Florin Malitacca86f32018-01-29 10:49:49 -05001355 sksg::AnimatorList animators;
Florin Malita5dfd0692018-07-02 15:05:26 -04001356 AttachContext ctx = { resources, assets, this->duration(), fFrameRate, animators };
Florin Malita35efaa82018-01-22 12:57:06 -05001357 auto root = AttachComposition(json, &ctx);
1358
Florin Malita6eb85a12018-04-30 10:32:18 -04001359 stats->fAnimatorCount = animators.size();
Florin Malita35efaa82018-01-22 12:57:06 -05001360
1361 fScene = sksg::Scene::Make(std::move(root), std::move(animators));
Florin Malita094ccde2017-12-30 12:27:00 -05001362
Florin Malitadb385732018-01-09 12:19:32 -05001363 // In case the client calls render before the first tick.
Florin Malitaa33447d2018-05-29 13:46:54 -04001364 this->seek(0);
Florin Malita094ccde2017-12-30 12:27:00 -05001365}
1366
1367Animation::~Animation() = default;
1368
Florin Malita35efaa82018-01-22 12:57:06 -05001369void Animation::setShowInval(bool show) {
1370 if (fScene) {
1371 fScene->setShowInval(show);
1372 }
1373}
1374
Mike Reed29859872018-01-08 08:25:27 -05001375void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
Florin Malita35efaa82018-01-22 12:57:06 -05001376 if (!fScene)
Florin Malita094ccde2017-12-30 12:27:00 -05001377 return;
1378
Mike Reed29859872018-01-08 08:25:27 -05001379 SkAutoCanvasRestore restore(canvas, true);
1380 const SkRect srcR = SkRect::MakeSize(this->size());
1381 if (dstR) {
1382 canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
1383 }
1384 canvas->clipRect(srcR);
Florin Malita35efaa82018-01-22 12:57:06 -05001385 fScene->render(canvas);
Florin Malita094ccde2017-12-30 12:27:00 -05001386}
1387
Florin Malitaa33447d2018-05-29 13:46:54 -04001388void Animation::seek(SkScalar t) {
Florin Malita35efaa82018-01-22 12:57:06 -05001389 if (!fScene)
1390 return;
1391
Florin Malitaa33447d2018-05-29 13:46:54 -04001392 fScene->animate(fInPoint + SkTPin(t, 0.0f, 1.0f) * (fOutPoint - fInPoint));
Florin Malita094ccde2017-12-30 12:27:00 -05001393}
1394
Florin Malita54f65c42018-01-16 17:04:30 -05001395} // namespace skottie