• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "Skottie.h"
9 
10 #include "SkCanvas.h"
11 #include "SkJSONCPP.h"
12 #include "SkottieAnimator.h"
13 #include "SkottieParser.h"
14 #include "SkottieProperties.h"
15 #include "SkData.h"
16 #include "SkImage.h"
17 #include "SkMakeUnique.h"
18 #include "SkOSPath.h"
19 #include "SkPaint.h"
20 #include "SkParse.h"
21 #include "SkPoint.h"
22 #include "SkSGClipEffect.h"
23 #include "SkSGColor.h"
24 #include "SkSGDraw.h"
25 #include "SkSGGeometryTransform.h"
26 #include "SkSGGradient.h"
27 #include "SkSGGroup.h"
28 #include "SkSGImage.h"
29 #include "SkSGInvalidationController.h"
30 #include "SkSGMaskEffect.h"
31 #include "SkSGMerge.h"
32 #include "SkSGOpacityEffect.h"
33 #include "SkSGPath.h"
34 #include "SkSGRect.h"
35 #include "SkSGScene.h"
36 #include "SkSGTransform.h"
37 #include "SkSGTrimEffect.h"
38 #include "SkStream.h"
39 #include "SkTArray.h"
40 #include "SkTHash.h"
41 
42 #include <cmath>
43 #include <vector>
44 
45 #include "stdlib.h"
46 
47 namespace skottie {
48 
49 #define LOG SkDebugf
50 
51 namespace {
52 
53 using AssetMap = SkTHashMap<SkString, const Json::Value*>;
54 
55 struct AttachContext {
56     const ResourceProvider& fResources;
57     const AssetMap&         fAssets;
58     sksg::AnimatorList&     fAnimators;
59 };
60 
LogFail(const Json::Value & json,const char * msg)61 bool LogFail(const Json::Value& json, const char* msg) {
62     const auto dump = json.toStyledString();
63     LOG("!! %s: %s", msg, dump.c_str());
64     return false;
65 }
66 
AttachMatrix(const Json::Value & t,AttachContext * ctx,sk_sp<sksg::Matrix> parentMatrix)67 sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
68                                         sk_sp<sksg::Matrix> parentMatrix) {
69     if (!t.isObject())
70         return nullptr;
71 
72     auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
73     auto composite = sk_make_sp<CompositeTransform>(matrix);
74     auto anchor_attached = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
75             [composite](const VectorValue& a) {
76                 composite->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
77             });
78     auto position_attached = BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
79             [composite](const VectorValue& p) {
80                 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
81             });
82     auto scale_attached = BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
83             [composite](const VectorValue& s) {
84                 composite->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
85             });
86 
87     auto* jrotation = &t["r"];
88     if (jrotation->isNull()) {
89         // 3d rotations have separate rx,ry,rz components.  While we don't fully support them,
90         // we can still make use of rz.
91         jrotation = &t["rz"];
92     }
93     auto rotation_attached = BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
94             [composite](const ScalarValue& r) {
95                 composite->setRotation(r);
96             });
97     auto skew_attached = BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
98             [composite](const ScalarValue& sk) {
99                 composite->setSkew(sk);
100             });
101     auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
102             [composite](const ScalarValue& sa) {
103                 composite->setSkewAxis(sa);
104             });
105 
106     if (!anchor_attached &&
107         !position_attached &&
108         !scale_attached &&
109         !rotation_attached &&
110         !skew_attached &&
111         !skewaxis_attached) {
112         LogFail(t, "Could not parse transform");
113         return nullptr;
114     }
115 
116     return matrix;
117 }
118 
AttachOpacity(const Json::Value & jtransform,AttachContext * ctx,sk_sp<sksg::RenderNode> childNode)119 sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachContext* ctx,
120                                       sk_sp<sksg::RenderNode> childNode) {
121     if (!jtransform.isObject() || !childNode)
122         return childNode;
123 
124     // This is more peeky than other attachers, because we want to avoid redundant opacity
125     // nodes for the extremely common case of static opaciy == 100.
126     const auto& opacity = jtransform["o"];
127     if (opacity.isObject() &&
128         !ParseDefault(opacity["a"], true) &&
129         ParseDefault(opacity["k"], -1) == 100) {
130         // Ignoring static full opacity.
131         return childNode;
132     }
133 
134     auto opacityNode = sksg::OpacityEffect::Make(childNode);
135     BindProperty<ScalarValue>(opacity, &ctx->fAnimators,
136         [opacityNode](const ScalarValue& o) {
137             // BM opacity is [0..100]
138             opacityNode->setOpacity(o * 0.01f);
139         });
140 
141     return opacityNode;
142 }
143 
144 sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
145 
AttachPath(const Json::Value & jpath,AttachContext * ctx)146 sk_sp<sksg::Path> AttachPath(const Json::Value& jpath, AttachContext* ctx) {
147     auto path_node = sksg::Path::Make();
148     return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
149             [path_node](const ShapeValue& p) { path_node->setPath(p); })
150         ? path_node
151         : nullptr;
152 }
153 
AttachPathGeometry(const Json::Value & jpath,AttachContext * ctx)154 sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
155     SkASSERT(jpath.isObject());
156 
157     return AttachPath(jpath["ks"], ctx);
158 }
159 
AttachRRectGeometry(const Json::Value & jrect,AttachContext * ctx)160 sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
161     SkASSERT(jrect.isObject());
162 
163     auto rect_node = sksg::RRect::Make();
164     auto composite = sk_make_sp<CompositeRRect>(rect_node);
165 
166     auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
167         [composite](const VectorValue& p) {
168                 composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
169         });
170     auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
171         [composite](const VectorValue& s) {
172             composite->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
173         });
174     auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
175         [composite](const ScalarValue& r) {
176             composite->setRadius(SkSize::Make(r, r));
177         });
178 
179     if (!p_attached && !s_attached && !r_attached) {
180         return nullptr;
181     }
182 
183     return rect_node;
184 }
185 
AttachEllipseGeometry(const Json::Value & jellipse,AttachContext * ctx)186 sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
187     SkASSERT(jellipse.isObject());
188 
189     auto rect_node = sksg::RRect::Make();
190     auto composite = sk_make_sp<CompositeRRect>(rect_node);
191 
192     auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
193         [composite](const VectorValue& p) {
194             composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
195         });
196     auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
197         [composite](const VectorValue& s) {
198             const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
199             composite->setSize(sz);
200             composite->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
201         });
202 
203     if (!p_attached && !s_attached) {
204         return nullptr;
205     }
206 
207     return rect_node;
208 }
209 
AttachPolystarGeometry(const Json::Value & jstar,AttachContext * ctx)210 sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
211     SkASSERT(jstar.isObject());
212 
213     static constexpr CompositePolyStar::Type gTypes[] = {
214         CompositePolyStar::Type::kStar, // "sy": 1
215         CompositePolyStar::Type::kPoly, // "sy": 2
216     };
217 
218     const auto type = ParseDefault(jstar["sy"], 0) - 1;
219     if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
220         LogFail(jstar, "Unknown polystar type");
221         return nullptr;
222     }
223 
224     auto path_node = sksg::Path::Make();
225     auto composite = sk_make_sp<CompositePolyStar>(path_node, gTypes[type]);
226 
227     BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
228         [composite](const VectorValue& p) {
229             composite->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
230         });
231     BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
232         [composite](const ScalarValue& pt) {
233             composite->setPointCount(pt);
234         });
235     BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
236         [composite](const ScalarValue& ir) {
237             composite->setInnerRadius(ir);
238         });
239     BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
240         [composite](const ScalarValue& otr) {
241             composite->setOuterRadius(otr);
242         });
243     BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
244         [composite](const ScalarValue& is) {
245             composite->setInnerRoundness(is);
246         });
247     BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
248         [composite](const ScalarValue& os) {
249             composite->setOuterRoundness(os);
250         });
251     BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
252         [composite](const ScalarValue& r) {
253             composite->setRotation(r);
254         });
255 
256     return path_node;
257 }
258 
AttachColor(const Json::Value & obj,AttachContext * ctx)259 sk_sp<sksg::Color> AttachColor(const Json::Value& obj, AttachContext* ctx) {
260     SkASSERT(obj.isObject());
261 
262     auto color_node = sksg::Color::Make(SK_ColorBLACK);
263     auto color_attached = BindProperty<VectorValue>(obj["c"], &ctx->fAnimators,
264         [color_node](const VectorValue& c) {
265             color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
266         });
267 
268     return color_attached ? color_node : nullptr;
269 }
270 
AttachGradient(const Json::Value & obj,AttachContext * ctx)271 sk_sp<sksg::Gradient> AttachGradient(const Json::Value& obj, AttachContext* ctx) {
272     SkASSERT(obj.isObject());
273 
274     const auto& stops = obj["g"];
275     if (!stops.isObject())
276         return nullptr;
277 
278     const auto stopCount = ParseDefault(stops["p"], -1);
279     if (stopCount < 0)
280         return nullptr;
281 
282     sk_sp<sksg::Gradient> gradient_node;
283     sk_sp<CompositeGradient> composite;
284 
285     if (ParseDefault(obj["t"], 1) == 1) {
286         auto linear_node = sksg::LinearGradient::Make();
287         composite = sk_make_sp<CompositeLinearGradient>(linear_node, stopCount);
288         gradient_node = std::move(linear_node);
289     } else {
290         auto radial_node = sksg::RadialGradient::Make();
291         composite = sk_make_sp<CompositeRadialGradient>(radial_node, stopCount);
292 
293         // TODO: highlight, angle
294         gradient_node = std::move(radial_node);
295     }
296 
297     BindProperty<VectorValue>(stops["k"], &ctx->fAnimators,
298         [composite](const VectorValue& stops) {
299             composite->setColorStops(stops);
300         });
301     BindProperty<VectorValue>(obj["s"], &ctx->fAnimators,
302         [composite](const VectorValue& s) {
303             composite->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
304         });
305     BindProperty<VectorValue>(obj["e"], &ctx->fAnimators,
306         [composite](const VectorValue& e) {
307             composite->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
308         });
309 
310     return gradient_node;
311 }
312 
AttachPaint(const Json::Value & jpaint,AttachContext * ctx,sk_sp<sksg::PaintNode> paint_node)313 sk_sp<sksg::PaintNode> AttachPaint(const Json::Value& jpaint, AttachContext* ctx,
314                                    sk_sp<sksg::PaintNode> paint_node) {
315     if (paint_node) {
316         paint_node->setAntiAlias(true);
317 
318         BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
319             [paint_node](const ScalarValue& o) {
320                 // BM opacity is [0..100]
321                 paint_node->setOpacity(o * 0.01f);
322         });
323     }
324 
325     return paint_node;
326 }
327 
AttachStroke(const Json::Value & jstroke,AttachContext * ctx,sk_sp<sksg::PaintNode> stroke_node)328 sk_sp<sksg::PaintNode> AttachStroke(const Json::Value& jstroke, AttachContext* ctx,
329                                     sk_sp<sksg::PaintNode> stroke_node) {
330     SkASSERT(jstroke.isObject());
331 
332     if (!stroke_node)
333         return nullptr;
334 
335     stroke_node->setStyle(SkPaint::kStroke_Style);
336 
337     auto width_attached = BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
338         [stroke_node](const ScalarValue& w) {
339             stroke_node->setStrokeWidth(w);
340         });
341     if (!width_attached)
342         return nullptr;
343 
344     stroke_node->setStrokeMiter(ParseDefault(jstroke["ml"], 4.0f));
345 
346     static constexpr SkPaint::Join gJoins[] = {
347         SkPaint::kMiter_Join,
348         SkPaint::kRound_Join,
349         SkPaint::kBevel_Join,
350     };
351     stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault(jstroke["lj"], 1) - 1,
352                                                   0, SK_ARRAY_COUNT(gJoins) - 1)]);
353 
354     static constexpr SkPaint::Cap gCaps[] = {
355         SkPaint::kButt_Cap,
356         SkPaint::kRound_Cap,
357         SkPaint::kSquare_Cap,
358     };
359     stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
360                                                 0, SK_ARRAY_COUNT(gCaps) - 1)]);
361 
362     return stroke_node;
363 }
364 
AttachColorFill(const Json::Value & jfill,AttachContext * ctx)365 sk_sp<sksg::PaintNode> AttachColorFill(const Json::Value& jfill, AttachContext* ctx) {
366     SkASSERT(jfill.isObject());
367 
368     return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
369 }
370 
AttachGradientFill(const Json::Value & jfill,AttachContext * ctx)371 sk_sp<sksg::PaintNode> AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) {
372     SkASSERT(jfill.isObject());
373 
374     return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
375 }
376 
AttachColorStroke(const Json::Value & jstroke,AttachContext * ctx)377 sk_sp<sksg::PaintNode> AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) {
378     SkASSERT(jstroke.isObject());
379 
380     return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
381 }
382 
AttachGradientStroke(const Json::Value & jstroke,AttachContext * ctx)383 sk_sp<sksg::PaintNode> AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) {
384     SkASSERT(jstroke.isObject());
385 
386     return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
387 }
388 
AttachMergeGeometryEffect(const Json::Value & jmerge,AttachContext * ctx,std::vector<sk_sp<sksg::GeometryNode>> && geos)389 std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
390     const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
391     std::vector<sk_sp<sksg::GeometryNode>> merged;
392 
393     static constexpr sksg::Merge::Mode gModes[] = {
394         sksg::Merge::Mode::kMerge,      // "mm": 1
395         sksg::Merge::Mode::kUnion,      // "mm": 2
396         sksg::Merge::Mode::kDifference, // "mm": 3
397         sksg::Merge::Mode::kIntersect,  // "mm": 4
398         sksg::Merge::Mode::kXOR      ,  // "mm": 5
399     };
400 
401     const auto mode = gModes[SkTPin<int>(ParseDefault(jmerge["mm"], 1) - 1,
402                                          0, SK_ARRAY_COUNT(gModes) - 1)];
403     merged.push_back(sksg::Merge::Make(std::move(geos), mode));
404 
405     return merged;
406 }
407 
AttachTrimGeometryEffect(const Json::Value & jtrim,AttachContext * ctx,std::vector<sk_sp<sksg::GeometryNode>> && geos)408 std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
409     const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
410 
411     enum class Mode {
412         kMerged,   // "m": 1
413         kSeparate, // "m": 2
414     } gModes[] = { Mode::kMerged, Mode::kSeparate };
415 
416     const auto mode = gModes[SkTPin<int>(ParseDefault(jtrim["m"], 1) - 1,
417                                          0, SK_ARRAY_COUNT(gModes) - 1)];
418 
419     std::vector<sk_sp<sksg::GeometryNode>> inputs;
420     if (mode == Mode::kMerged) {
421         inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge));
422     } else {
423         inputs = std::move(geos);
424     }
425 
426     std::vector<sk_sp<sksg::GeometryNode>> trimmed;
427     trimmed.reserve(inputs.size());
428     for (const auto& i : inputs) {
429         const auto trim = sksg::TrimEffect::Make(i);
430         trimmed.push_back(trim);
431         BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
432             [trim](const ScalarValue& s) {
433                 trim->setStart(s * 0.01f);
434             });
435         BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
436             [trim](const ScalarValue& e) {
437                 trim->setEnd(e * 0.01f);
438             });
439         BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
440             [trim](const ScalarValue& o) {
441                 trim->setOffset(o / 360);
442             });
443     }
444 
445     return trimmed;
446 }
447 
448 using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
449 static constexpr GeometryAttacherT gGeometryAttachers[] = {
450     AttachPathGeometry,
451     AttachRRectGeometry,
452     AttachEllipseGeometry,
453     AttachPolystarGeometry,
454 };
455 
456 using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
457 static constexpr PaintAttacherT gPaintAttachers[] = {
458     AttachColorFill,
459     AttachColorStroke,
460     AttachGradientFill,
461     AttachGradientStroke,
462 };
463 
464 using GeometryEffectAttacherT =
465     std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
466                                                AttachContext*,
467                                                std::vector<sk_sp<sksg::GeometryNode>>&&);
468 static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
469     AttachMergeGeometryEffect,
470     AttachTrimGeometryEffect,
471 };
472 
473 enum class ShapeType {
474     kGeometry,
475     kGeometryEffect,
476     kPaint,
477     kGroup,
478     kTransform,
479 };
480 
481 struct ShapeInfo {
482     const char* fTypeString;
483     ShapeType   fShapeType;
484     uint32_t    fAttacherIndex; // index into respective attacher tables
485 };
486 
FindShapeInfo(const Json::Value & shape)487 const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
488     static constexpr ShapeInfo gShapeInfo[] = {
489         { "el", ShapeType::kGeometry      , 2 }, // ellipse   -> AttachEllipseGeometry
490         { "fl", ShapeType::kPaint         , 0 }, // fill      -> AttachColorFill
491         { "gf", ShapeType::kPaint         , 2 }, // gfill     -> AttachGradientFill
492         { "gr", ShapeType::kGroup         , 0 }, // group     -> Inline handler
493         { "gs", ShapeType::kPaint         , 3 }, // gstroke   -> AttachGradientStroke
494         { "mm", ShapeType::kGeometryEffect, 0 }, // merge     -> AttachMergeGeometryEffect
495         { "rc", ShapeType::kGeometry      , 1 }, // rrect     -> AttachRRectGeometry
496         { "sh", ShapeType::kGeometry      , 0 }, // shape     -> AttachPathGeometry
497         { "sr", ShapeType::kGeometry      , 3 }, // polystar  -> AttachPolyStarGeometry
498         { "st", ShapeType::kPaint         , 1 }, // stroke    -> AttachColorStroke
499         { "tm", ShapeType::kGeometryEffect, 1 }, // trim      -> AttachTrimGeometryEffect
500         { "tr", ShapeType::kTransform     , 0 }, // transform -> Inline handler
501     };
502 
503     if (!shape.isObject())
504         return nullptr;
505 
506     const auto& type = shape["ty"];
507     if (!type.isString())
508         return nullptr;
509 
510     const auto* info = bsearch(type.asCString(),
511                                gShapeInfo,
512                                SK_ARRAY_COUNT(gShapeInfo),
513                                sizeof(ShapeInfo),
514                                [](const void* key, const void* info) {
515                                   return strcmp(static_cast<const char*>(key),
516                                                 static_cast<const ShapeInfo*>(info)->fTypeString);
517                                });
518 
519     return static_cast<const ShapeInfo*>(info);
520 }
521 
522 struct GeometryEffectRec {
523     const Json::Value&      fJson;
524     GeometryEffectAttacherT fAttach;
525 };
526 
527 struct AttachShapeContext {
AttachShapeContextskottie::__anond877799f0111::AttachShapeContext528     AttachShapeContext(AttachContext* ctx,
529                        std::vector<sk_sp<sksg::GeometryNode>>* geos,
530                        std::vector<GeometryEffectRec>* effects,
531                        size_t committedAnimators)
532         : fCtx(ctx)
533         , fGeometryStack(geos)
534         , fGeometryEffectStack(effects)
535         , fCommittedAnimators(committedAnimators) {}
536 
537     AttachContext*                          fCtx;
538     std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
539     std::vector<GeometryEffectRec>*         fGeometryEffectStack;
540     size_t                                  fCommittedAnimators;
541 };
542 
AttachShape(const Json::Value & jshape,AttachShapeContext * shapeCtx)543 sk_sp<sksg::RenderNode> AttachShape(const Json::Value& jshape, AttachShapeContext* shapeCtx) {
544     if (!jshape.isArray())
545         return nullptr;
546 
547     SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
548 
549     sk_sp<sksg::Group> shape_group = sksg::Group::Make();
550     sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
551     sk_sp<sksg::Matrix> shape_matrix;
552 
553     struct ShapeRec {
554         const Json::Value& fJson;
555         const ShapeInfo&   fInfo;
556     };
557 
558     // First pass (bottom->top):
559     //
560     //   * pick up the group transform and opacity
561     //   * push local geometry effects onto the stack
562     //   * store recs for next pass
563     //
564     std::vector<ShapeRec> recs;
565     for (Json::ArrayIndex i = 0; i < jshape.size(); ++i) {
566         const auto& s = jshape[jshape.size() - 1 - i];
567         const auto* info = FindShapeInfo(s);
568         if (!info) {
569             LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
570             continue;
571         }
572 
573         recs.push_back({ s, *info });
574 
575         switch (info->fShapeType) {
576         case ShapeType::kTransform:
577             if ((shape_matrix = AttachMatrix(s, shapeCtx->fCtx, nullptr))) {
578                 shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
579             }
580             shape_wrapper = AttachOpacity(s, shapeCtx->fCtx, std::move(shape_wrapper));
581             break;
582         case ShapeType::kGeometryEffect:
583             SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
584             shapeCtx->fGeometryEffectStack->push_back(
585                 { s, gGeometryEffectAttachers[info->fAttacherIndex] });
586             break;
587         default:
588             break;
589         }
590     }
591 
592     // Second pass (top -> bottom, after 2x reverse):
593     //
594     //   * track local geometry
595     //   * emit local paints
596     //
597     std::vector<sk_sp<sksg::GeometryNode>> geos;
598     std::vector<sk_sp<sksg::RenderNode  >> draws;
599     for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
600         switch (rec->fInfo.fShapeType) {
601         case ShapeType::kGeometry: {
602             SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
603             if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
604                                                                          shapeCtx->fCtx)) {
605                 geos.push_back(std::move(geo));
606             }
607         } break;
608         case ShapeType::kGeometryEffect: {
609             // Apply the current effect and pop from the stack.
610             SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
611             if (!geos.empty()) {
612                 geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
613                                                                            shapeCtx->fCtx,
614                                                                            std::move(geos));
615             }
616 
617             SkASSERT(shapeCtx->fGeometryEffectStack->back().fJson == rec->fJson);
618             SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
619                      gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
620             shapeCtx->fGeometryEffectStack->pop_back();
621         } break;
622         case ShapeType::kGroup: {
623             AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
624                                              &geos,
625                                              shapeCtx->fGeometryEffectStack,
626                                              shapeCtx->fCommittedAnimators);
627             if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
628                 draws.push_back(std::move(subgroup));
629                 SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
630                 shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
631             }
632         } break;
633         case ShapeType::kPaint: {
634             SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
635             auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
636             if (!paint || geos.empty())
637                 break;
638 
639             auto drawGeos = geos;
640 
641             // Apply all pending effects from the stack.
642             for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
643                  it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
644                 drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
645             }
646 
647             // If we still have multiple geos, reduce using 'merge'.
648             auto geo = drawGeos.size() > 1
649                 ? sksg::Merge::Make(std::move(drawGeos), sksg::Merge::Mode::kMerge)
650                 : drawGeos[0];
651 
652             SkASSERT(geo);
653             draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
654             shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
655         } break;
656         default:
657             break;
658         }
659     }
660 
661     // By now we should have popped all local geometry effects.
662     SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
663 
664     // Push transformed local geometries to parent list, for subsequent paints.
665     for (const auto& geo : geos) {
666         shapeCtx->fGeometryStack->push_back(shape_matrix
667             ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
668             : std::move(geo));
669     }
670 
671     // Emit local draws reversed (bottom->top, per spec).
672     for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
673         shape_group->addChild(std::move(*it));
674     }
675 
676     return draws.empty() ? nullptr : shape_wrapper;
677 }
678 
AttachCompLayer(const Json::Value & jlayer,AttachContext * ctx,float * time_bias,float * time_scale)679 sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx,
680                                         float* time_bias, float* time_scale) {
681     SkASSERT(jlayer.isObject());
682 
683     SkString refId;
684     if (!Parse(jlayer["refId"], &refId) || refId.isEmpty()) {
685         LOG("!! Comp layer missing refId\n");
686         return nullptr;
687     }
688 
689     const auto* comp = ctx->fAssets.find(refId);
690     if (!comp) {
691         LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
692         return nullptr;
693     }
694 
695     const auto start_time = ParseDefault(jlayer["st"], 0.0f),
696              stretch_time = ParseDefault(jlayer["sr"], 1.0f);
697 
698     *time_bias = -start_time;
699     *time_scale = 1 / stretch_time;
700     if (SkScalarIsNaN(*time_scale)) {
701         *time_scale = 1;
702     }
703 
704     // TODO: cycle detection
705     return AttachComposition(**comp, ctx);
706 }
707 
AttachSolidLayer(const Json::Value & jlayer,AttachContext *,float *,float *)708 sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& jlayer, AttachContext*,
709                                          float*, float*) {
710     SkASSERT(jlayer.isObject());
711 
712     const auto size = SkSize::Make(ParseDefault(jlayer["sw"], 0.0f),
713                                    ParseDefault(jlayer["sh"], 0.0f));
714     const auto hex = ParseDefault(jlayer["sc"], SkString());
715     uint32_t c;
716     if (size.isEmpty() ||
717         !hex.startsWith("#") ||
718         !SkParse::FindHex(hex.c_str() + 1, &c)) {
719         LogFail(jlayer, "Could not parse solid layer");
720         return nullptr;
721     }
722 
723     const SkColor color = 0xff000000 | c;
724 
725     return sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeSize(size)),
726                             sksg::Color::Make(color));
727 }
728 
AttachImageAsset(const Json::Value & jimage,AttachContext * ctx)729 sk_sp<sksg::RenderNode> AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) {
730     SkASSERT(jimage.isObject());
731 
732     const auto name = ParseDefault(jimage["p"], SkString()),
733                path = ParseDefault(jimage["u"], SkString());
734     if (name.isEmpty())
735         return nullptr;
736 
737     // TODO: plumb resource paths explicitly to ResourceProvider?
738     const auto resName    = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
739     const auto resStream  = ctx->fResources.openStream(resName.c_str());
740     if (!resStream || !resStream->hasLength()) {
741         LOG("!! Could not load image resource: %s\n", resName.c_str());
742         return nullptr;
743     }
744 
745     // TODO: non-intrisic image sizing
746     return sksg::Image::Make(
747         SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
748 }
749 
AttachImageLayer(const Json::Value & layer,AttachContext * ctx,float *,float *)750 sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx,
751                                          float*, float*) {
752     SkASSERT(layer.isObject());
753 
754     SkString refId;
755     if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
756         LOG("!! Image layer missing refId\n");
757         return nullptr;
758     }
759 
760     const auto* jimage = ctx->fAssets.find(refId);
761     if (!jimage) {
762         LOG("!! Image asset not found: '%s'\n", refId.c_str());
763         return nullptr;
764     }
765 
766     return AttachImageAsset(**jimage, ctx);
767 }
768 
AttachNullLayer(const Json::Value & layer,AttachContext *,float *,float *)769 sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*, float*, float*) {
770     SkASSERT(layer.isObject());
771 
772     // Null layers are used solely to drive dependent transforms,
773     // but we use free-floating sksg::Matrices for that purpose.
774     return nullptr;
775 }
776 
AttachShapeLayer(const Json::Value & layer,AttachContext * ctx,float *,float *)777 sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx,
778                                          float*, float*) {
779     SkASSERT(layer.isObject());
780 
781     std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
782     std::vector<GeometryEffectRec> geometryEffectStack;
783     AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
784     auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
785 
786     // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
787     // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
788     // due to attached animators.  To avoid this, we track committed animators and discard the
789     // orphans here.
790     SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
791     ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
792 
793     return shapeNode;
794 }
795 
AttachTextLayer(const Json::Value & layer,AttachContext *,float *,float *)796 sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*, float*, float*) {
797     SkASSERT(layer.isObject());
798 
799     LOG("?? Text layer stub\n");
800     return nullptr;
801 }
802 
803 struct AttachLayerContext {
AttachLayerContextskottie::__anond877799f0111::AttachLayerContext804     AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
805         : fLayerList(jlayers), fCtx(ctx) {}
806 
807     const Json::Value&                   fLayerList;
808     AttachContext*                       fCtx;
809     SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
810     sk_sp<sksg::RenderNode>              fCurrentMatte;
811 
AttachParentLayerMatrixskottie::__anond877799f0111::AttachLayerContext812     sk_sp<sksg::Matrix> AttachParentLayerMatrix(const Json::Value& jlayer) {
813         SkASSERT(jlayer.isObject());
814         SkASSERT(fLayerList.isArray());
815 
816         const auto parent_index = ParseDefault(jlayer["parent"], -1);
817         if (parent_index < 0)
818             return nullptr;
819 
820         if (auto* m = fLayerMatrixMap.find(parent_index))
821             return *m;
822 
823         for (const auto& l : fLayerList) {
824             if (!l.isObject()) {
825                 continue;
826             }
827 
828             if (ParseDefault(l["ind"], -1) == parent_index) {
829                 return this->AttachLayerMatrix(l);
830             }
831         }
832 
833         return nullptr;
834     }
835 
AttachLayerMatrixskottie::__anond877799f0111::AttachLayerContext836     sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
837         SkASSERT(jlayer.isObject());
838 
839         const auto layer_index = ParseDefault(jlayer["ind"], -1);
840         if (layer_index < 0)
841             return nullptr;
842 
843         if (auto* m = fLayerMatrixMap.find(layer_index))
844             return *m;
845 
846         // Add a stub entry to break recursion cycles.
847         fLayerMatrixMap.set(layer_index, nullptr);
848 
849         auto parent_matrix = this->AttachParentLayerMatrix(jlayer);
850 
851         return *fLayerMatrixMap.set(layer_index,
852                                     AttachMatrix(jlayer["ks"],
853                                                  fCtx,
854                                                  this->AttachParentLayerMatrix(jlayer)));
855     }
856 };
857 
MaskBlendMode(char mode)858 SkBlendMode MaskBlendMode(char mode) {
859     switch (mode) {
860     case 'a': return SkBlendMode::kSrcOver;    // Additive
861     case 's': return SkBlendMode::kExclusion;  // Subtract
862     case 'i': return SkBlendMode::kDstIn;      // Intersect
863     case 'l': return SkBlendMode::kLighten;    // Lighten
864     case 'd': return SkBlendMode::kDarken;     // Darken
865     case 'f': return SkBlendMode::kDifference; // Difference
866     default: break;
867     }
868 
869     return SkBlendMode::kSrcOver;
870 }
871 
AttachMask(const Json::Value & jmask,AttachContext * ctx,sk_sp<sksg::RenderNode> childNode)872 sk_sp<sksg::RenderNode> AttachMask(const Json::Value& jmask,
873                                    AttachContext* ctx,
874                                    sk_sp<sksg::RenderNode> childNode) {
875     if (!jmask.isArray())
876         return childNode;
877 
878     auto mask_group = sksg::Group::Make();
879 
880     for (const auto& m : jmask) {
881         if (!m.isObject())
882             continue;
883 
884         const auto inverted = ParseDefault(m["inv"], false);
885         // TODO
886         if (inverted) {
887             LogFail(m, "Unsupported inverse mask");
888             continue;
889         }
890 
891         auto mask_path = AttachPath(m["pt"], ctx);
892         if (!mask_path) {
893             LogFail(m, "Could not parse mask path");
894             continue;
895         }
896 
897         SkString mode;
898         if (!Parse(m["mode"], &mode) ||
899             mode.size() != 1 ||
900             !strcmp(mode.c_str(), "n")) { // "None" masks have no effect.
901             continue;
902         }
903 
904         auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
905         mask_paint->setAntiAlias(true);
906         mask_paint->setBlendMode(MaskBlendMode(mode.c_str()[0]));
907         BindProperty<ScalarValue>(m["o"], &ctx->fAnimators,
908             [mask_paint](const ScalarValue& o) { mask_paint->setOpacity(o * 0.01f); });
909 
910         mask_group->addChild(sksg::Draw::Make(std::move(mask_path), std::move(mask_paint)));
911     }
912 
913     return mask_group->empty()
914         ? childNode
915         : sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
916 }
917 
AttachLayer(const Json::Value & jlayer,AttachLayerContext * layerCtx)918 sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
919                                     AttachLayerContext* layerCtx) {
920     if (!jlayer.isObject())
921         return nullptr;
922 
923     using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*,
924                                                       float* time_bias, float* time_scale);
925     static constexpr LayerAttacher gLayerAttachers[] = {
926         AttachCompLayer,  // 'ty': 0
927         AttachSolidLayer, // 'ty': 1
928         AttachImageLayer, // 'ty': 2
929         AttachNullLayer,  // 'ty': 3
930         AttachShapeLayer, // 'ty': 4
931         AttachTextLayer,  // 'ty': 5
932     };
933 
934     int type = ParseDefault(jlayer["ty"], -1);
935     if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
936         return nullptr;
937     }
938 
939     sksg::AnimatorList layer_animators;
940     AttachContext local_ctx =
941         { layerCtx->fCtx->fResources, layerCtx->fCtx->fAssets, layer_animators};
942 
943     // Layer attachers may adjust these.
944     float time_bias  = 0,
945           time_scale = 1;
946 
947     // Layer content.
948     auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale);
949 
950     // Clip layers with explicit dimensions.
951     float w, h;
952     if (Parse(jlayer["w"], &w) && Parse(jlayer["h"], &h)) {
953         layer = sksg::ClipEffect::Make(std::move(layer),
954                                        sksg::Rect::Make(SkRect::MakeWH(w, h)),
955                                        true);
956     }
957 
958     // Optional layer mask.
959     layer = AttachMask(jlayer["masksProperties"], &local_ctx, std::move(layer));
960 
961     // Optional layer transform.
962     if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
963         layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
964     }
965 
966     // Optional layer opacity.
967     layer = AttachOpacity(jlayer["ks"], &local_ctx, std::move(layer));
968 
969     class LayerController final : public sksg::GroupAnimator {
970     public:
971         LayerController(sksg::AnimatorList&& layer_animators,
972                         sk_sp<sksg::OpacityEffect> controlNode,
973                         float in, float out,
974                         float time_bias, float time_scale)
975             : INHERITED(std::move(layer_animators))
976             , fControlNode(std::move(controlNode))
977             , fIn(in)
978             , fOut(out)
979             , fTimeBias(time_bias)
980             , fTimeScale(time_scale) {}
981 
982         void onTick(float t) override {
983             const auto active = (t >= fIn && t <= fOut);
984 
985             // Keep the layer fully transparent except for its [in..out] lifespan.
986             // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
987             fControlNode->setOpacity(active ? 1 : 0);
988 
989             // Dispatch ticks only while active.
990             if (active)
991                 this->INHERITED::onTick((t + fTimeBias) * fTimeScale);
992         }
993 
994     private:
995         const sk_sp<sksg::OpacityEffect> fControlNode;
996         const float                      fIn,
997                                          fOut,
998                                          fTimeBias,
999                                          fTimeScale;
1000 
1001         using INHERITED = sksg::GroupAnimator;
1002     };
1003 
1004     auto controller_node = sksg::OpacityEffect::Make(std::move(layer));
1005     const auto        in = ParseDefault(jlayer["ip"], 0.0f),
1006                      out = ParseDefault(jlayer["op"], in);
1007 
1008     if (!jlayer["tm"].isNull()) {
1009         LogFail(jlayer["tm"], "Unsupported time remapping");
1010     }
1011 
1012     if (in >= out || !controller_node)
1013         return nullptr;
1014 
1015     layerCtx->fCtx->fAnimators.push_back(
1016         skstd::make_unique<LayerController>(std::move(layer_animators),
1017                                             controller_node,
1018                                             in,
1019                                             out,
1020                                             time_bias,
1021                                             time_scale));
1022 
1023     if (ParseDefault(jlayer["td"], false)) {
1024         // This layer is a matte.  We apply it as a mask to the next layer.
1025         layerCtx->fCurrentMatte = std::move(controller_node);
1026         return nullptr;
1027     }
1028 
1029     if (layerCtx->fCurrentMatte) {
1030         // There is a pending matte. Apply and reset.
1031         return sksg::MaskEffect::Make(std::move(controller_node),
1032                                       std::move(layerCtx->fCurrentMatte));
1033     }
1034 
1035     return controller_node;
1036 }
1037 
AttachComposition(const Json::Value & comp,AttachContext * ctx)1038 sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
1039     if (!comp.isObject())
1040         return nullptr;
1041 
1042     const auto& jlayers = comp["layers"];
1043     if (!jlayers.isArray())
1044         return nullptr;
1045 
1046     SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
1047     AttachLayerContext                           layerCtx(jlayers, ctx);
1048 
1049     for (const auto& l : jlayers) {
1050         if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
1051             layers.push_back(std::move(layer_fragment));
1052         }
1053     }
1054 
1055     if (layers.empty()) {
1056         return nullptr;
1057     }
1058 
1059     // Layers are painted in bottom->top order.
1060     auto comp_group = sksg::Group::Make();
1061     for (int i = layers.count() - 1; i >= 0; --i) {
1062         comp_group->addChild(std::move(layers[i]));
1063     }
1064 
1065     LOG("** Attached composition '%s': %d layers.\n",
1066         ParseDefault(comp["id"], SkString()).c_str(), layers.count());
1067 
1068     return comp_group;
1069 }
1070 
1071 } // namespace
1072 
Make(SkStream * stream,const ResourceProvider & res)1073 std::unique_ptr<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res) {
1074     if (!stream->hasLength()) {
1075         // TODO: handle explicit buffering?
1076         LOG("!! cannot parse streaming content\n");
1077         return nullptr;
1078     }
1079 
1080     Json::Value json;
1081     {
1082         auto data = SkData::MakeFromStream(stream, stream->getLength());
1083         if (!data) {
1084             LOG("!! could not read stream\n");
1085             return nullptr;
1086         }
1087 
1088         Json::Reader reader;
1089 
1090         auto dataStart = static_cast<const char*>(data->data());
1091         if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
1092             LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
1093             return nullptr;
1094         }
1095     }
1096 
1097     const auto version = ParseDefault(json["v"], SkString());
1098     const auto size    = SkSize::Make(ParseDefault(json["w"], 0.0f),
1099                                       ParseDefault(json["h"], 0.0f));
1100     const auto fps     = ParseDefault(json["fr"], -1.0f);
1101 
1102     if (size.isEmpty() || version.isEmpty() || fps < 0) {
1103         LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
1104             version.c_str(), size.width(), size.height(), fps);
1105         return nullptr;
1106     }
1107 
1108     return std::unique_ptr<Animation>(new Animation(res, std::move(version), size, fps, json));
1109 }
1110 
MakeFromFile(const char path[],const ResourceProvider * res)1111 std::unique_ptr<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res) {
1112     class DirectoryResourceProvider final : public ResourceProvider {
1113     public:
1114         explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
1115 
1116         std::unique_ptr<SkStream> openStream(const char resource[]) const override {
1117             const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
1118             return SkStream::MakeFromFile(resPath.c_str());
1119         }
1120 
1121     private:
1122         const SkString fDir;
1123     };
1124 
1125     const auto jsonStream =  SkStream::MakeFromFile(path);
1126     if (!jsonStream)
1127         return nullptr;
1128 
1129     std::unique_ptr<ResourceProvider> defaultProvider;
1130     if (!res) {
1131         defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
1132     }
1133 
1134     return Make(jsonStream.get(), res ? *res : *defaultProvider);
1135 }
1136 
Animation(const ResourceProvider & resources,SkString version,const SkSize & size,SkScalar fps,const Json::Value & json)1137 Animation::Animation(const ResourceProvider& resources,
1138                      SkString version, const SkSize& size, SkScalar fps, const Json::Value& json)
1139     : fVersion(std::move(version))
1140     , fSize(size)
1141     , fFrameRate(fps)
1142     , fInPoint(ParseDefault(json["ip"], 0.0f))
1143     , fOutPoint(SkTMax(ParseDefault(json["op"], SK_ScalarMax), fInPoint)) {
1144 
1145     AssetMap assets;
1146     for (const auto& asset : json["assets"]) {
1147         if (!asset.isObject()) {
1148             continue;
1149         }
1150 
1151         assets.set(ParseDefault(asset["id"], SkString()), &asset);
1152     }
1153 
1154     sksg::AnimatorList animators;
1155     AttachContext ctx = { resources, assets, animators };
1156     auto root = AttachComposition(json, &ctx);
1157 
1158     LOG("** Attached %d animators\n", animators.size());
1159 
1160     fScene = sksg::Scene::Make(std::move(root), std::move(animators));
1161 
1162     // In case the client calls render before the first tick.
1163     this->animationTick(0);
1164 }
1165 
1166 Animation::~Animation() = default;
1167 
setShowInval(bool show)1168 void Animation::setShowInval(bool show) {
1169     if (fScene) {
1170         fScene->setShowInval(show);
1171     }
1172 }
1173 
render(SkCanvas * canvas,const SkRect * dstR) const1174 void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
1175     if (!fScene)
1176         return;
1177 
1178     SkAutoCanvasRestore restore(canvas, true);
1179     const SkRect srcR = SkRect::MakeSize(this->size());
1180     if (dstR) {
1181         canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
1182     }
1183     canvas->clipRect(srcR);
1184     fScene->render(canvas);
1185 }
1186 
animationTick(SkMSec ms)1187 void Animation::animationTick(SkMSec ms) {
1188     if (!fScene)
1189         return;
1190 
1191     // 't' in the BM model really means 'frame #'
1192     auto t = static_cast<float>(ms) * fFrameRate / 1000;
1193 
1194     t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
1195 
1196     fScene->animate(t);
1197 }
1198 
1199 } // namespace skottie
1200