aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental
diff options
context:
space:
mode:
Diffstat (limited to 'experimental')
-rw-r--r--experimental/skottie/Skottie.cpp1327
-rw-r--r--experimental/skottie/Skottie.h80
-rw-r--r--experimental/skottie/SkottieAdapter.cpp166
-rw-r--r--experimental/skottie/SkottieAdapter.h164
-rw-r--r--experimental/skottie/SkottieAnimator.cpp374
-rw-r--r--experimental/skottie/SkottieAnimator.h29
-rw-r--r--experimental/skottie/SkottieJson.cpp243
-rw-r--r--experimental/skottie/SkottieJson.h76
-rw-r--r--experimental/skottie/SkottieValue.cpp161
-rw-r--r--experimental/skottie/SkottieValue.h64
10 files changed, 0 insertions, 2684 deletions
diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp
deleted file mode 100644
index 8396b5ba90..0000000000
--- a/experimental/skottie/Skottie.cpp
+++ /dev/null
@@ -1,1327 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "Skottie.h"
-
-#include "SkCanvas.h"
-#include "SkottieAdapter.h"
-#include "SkottieAnimator.h"
-#include "SkottieJson.h"
-#include "SkottieValue.h"
-#include "SkData.h"
-#include "SkImage.h"
-#include "SkMakeUnique.h"
-#include "SkOSPath.h"
-#include "SkPaint.h"
-#include "SkParse.h"
-#include "SkPoint.h"
-#include "SkSGClipEffect.h"
-#include "SkSGColor.h"
-#include "SkSGDraw.h"
-#include "SkSGGeometryTransform.h"
-#include "SkSGGradient.h"
-#include "SkSGGroup.h"
-#include "SkSGImage.h"
-#include "SkSGInvalidationController.h"
-#include "SkSGMaskEffect.h"
-#include "SkSGMerge.h"
-#include "SkSGOpacityEffect.h"
-#include "SkSGPath.h"
-#include "SkSGRect.h"
-#include "SkSGRoundEffect.h"
-#include "SkSGScene.h"
-#include "SkSGTransform.h"
-#include "SkSGTrimEffect.h"
-#include "SkStream.h"
-#include "SkTArray.h"
-#include "SkTime.h"
-#include "SkTHash.h"
-
-#include <cmath>
-#include <vector>
-
-#include "stdlib.h"
-
-namespace skottie {
-
-#define LOG SkDebugf
-
-namespace {
-
-struct AssetInfo {
- json::ValueRef fAsset;
- mutable bool fIsAttaching; // Used for cycle detection
-};
-
-using AssetMap = SkTHashMap<SkString, AssetInfo>;
-
-struct AttachContext {
- const ResourceProvider& fResources;
- const AssetMap& fAssets;
- const float fFrameRate;
- sksg::AnimatorList& fAnimators;
-};
-
-bool LogFail(const json::ValueRef& json, const char* msg) {
- const auto dump = json.toString();
- LOG("!! %s: %s\n", msg, dump.c_str());
- return false;
-}
-
-sk_sp<sksg::Matrix> AttachMatrix(const json::ValueRef& t, AttachContext* ctx,
- sk_sp<sksg::Matrix> parentMatrix) {
- if (!t.isObject())
- return nullptr;
-
- auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix));
- auto adapter = sk_make_sp<TransformAdapter>(matrix);
- auto anchor_attached = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
- [adapter](const VectorValue& a) {
- adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
- });
- auto position_attached = BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
- [adapter](const VectorValue& p) {
- adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
- });
- auto scale_attached = BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
- [adapter](const VectorValue& s) {
- adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
- });
-
- auto jrotation = t["r"];
- if (jrotation.isNull()) {
- // 3d rotations have separate rx,ry,rz components. While we don't fully support them,
- // we can still make use of rz.
- jrotation = t["rz"];
- }
- auto rotation_attached = BindProperty<ScalarValue>(jrotation, &ctx->fAnimators,
- [adapter](const ScalarValue& r) {
- adapter->setRotation(r);
- });
- auto skew_attached = BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
- [adapter](const ScalarValue& sk) {
- adapter->setSkew(sk);
- });
- auto skewaxis_attached = BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
- [adapter](const ScalarValue& sa) {
- adapter->setSkewAxis(sa);
- });
-
- if (!anchor_attached &&
- !position_attached &&
- !scale_attached &&
- !rotation_attached &&
- !skew_attached &&
- !skewaxis_attached) {
- LogFail(t, "Could not parse transform");
- return nullptr;
- }
-
- return matrix;
-}
-
-sk_sp<sksg::RenderNode> AttachOpacity(const json::ValueRef& jtransform, AttachContext* ctx,
- sk_sp<sksg::RenderNode> childNode) {
- if (!jtransform.isObject() || !childNode)
- return childNode;
-
- static constexpr ScalarValue kNoopOpacity = 100;
- auto opacityNode = sksg::OpacityEffect::Make(childNode);
-
- if (!BindProperty<ScalarValue>(jtransform["o"], &ctx->fAnimators,
- [opacityNode](const ScalarValue& o) {
- // BM opacity is [0..100]
- opacityNode->setOpacity(o * 0.01f);
- }, &kNoopOpacity)) {
- // We can ignore static full opacity.
- return childNode;
- }
-
- return std::move(opacityNode);
-}
-
-sk_sp<sksg::RenderNode> AttachComposition(const json::ValueRef&, AttachContext* ctx);
-
-sk_sp<sksg::Path> AttachPath(const json::ValueRef& jpath, AttachContext* ctx) {
- auto path_node = sksg::Path::Make();
- return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
- [path_node](const ShapeValue& p) {
- path_node->setPath(ValueTraits<ShapeValue>::As<SkPath>(p));
- })
- ? path_node
- : nullptr;
-}
-
-sk_sp<sksg::GeometryNode> AttachPathGeometry(const json::ValueRef& jpath, AttachContext* ctx) {
- SkASSERT(jpath.isObject());
-
- return AttachPath(jpath["ks"], ctx);
-}
-
-sk_sp<sksg::GeometryNode> AttachRRectGeometry(const json::ValueRef& jrect, AttachContext* ctx) {
- SkASSERT(jrect.isObject());
-
- auto rect_node = sksg::RRect::Make();
- auto adapter = sk_make_sp<RRectAdapter>(rect_node);
-
- auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
- [adapter](const VectorValue& p) {
- adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
- });
- auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
- [adapter](const VectorValue& s) {
- adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
- });
- auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
- [adapter](const ScalarValue& r) {
- adapter->setRadius(SkSize::Make(r, r));
- });
-
- if (!p_attached && !s_attached && !r_attached) {
- return nullptr;
- }
-
- return std::move(rect_node);
-}
-
-sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const json::ValueRef& jellipse, AttachContext* ctx) {
- SkASSERT(jellipse.isObject());
-
- auto rect_node = sksg::RRect::Make();
- auto adapter = sk_make_sp<RRectAdapter>(rect_node);
-
- auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
- [adapter](const VectorValue& p) {
- adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
- });
- auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
- [adapter](const VectorValue& s) {
- const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
- adapter->setSize(sz);
- adapter->setRadius(SkSize::Make(sz.width() / 2, sz.height() / 2));
- });
-
- if (!p_attached && !s_attached) {
- return nullptr;
- }
-
- return std::move(rect_node);
-}
-
-sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const json::ValueRef& jstar, AttachContext* ctx) {
- SkASSERT(jstar.isObject());
-
- static constexpr PolyStarAdapter::Type gTypes[] = {
- PolyStarAdapter::Type::kStar, // "sy": 1
- PolyStarAdapter::Type::kPoly, // "sy": 2
- };
-
- const auto type = jstar["sy"].toDefault<int>(0) - 1;
- if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
- LogFail(jstar, "Unknown polystar type");
- return nullptr;
- }
-
- auto path_node = sksg::Path::Make();
- auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
-
- BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
- [adapter](const VectorValue& p) {
- adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
- });
- BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
- [adapter](const ScalarValue& pt) {
- adapter->setPointCount(pt);
- });
- BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
- [adapter](const ScalarValue& ir) {
- adapter->setInnerRadius(ir);
- });
- BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
- [adapter](const ScalarValue& otr) {
- adapter->setOuterRadius(otr);
- });
- BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
- [adapter](const ScalarValue& is) {
- adapter->setInnerRoundness(is);
- });
- BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
- [adapter](const ScalarValue& os) {
- adapter->setOuterRoundness(os);
- });
- BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
- [adapter](const ScalarValue& r) {
- adapter->setRotation(r);
- });
-
- return std::move(path_node);
-}
-
-sk_sp<sksg::Color> AttachColor(const json::ValueRef& obj, AttachContext* ctx) {
- SkASSERT(obj.isObject());
-
- auto color_node = sksg::Color::Make(SK_ColorBLACK);
- auto color_attached = BindProperty<VectorValue>(obj["c"], &ctx->fAnimators,
- [color_node](const VectorValue& c) {
- color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
- });
-
- return color_attached ? color_node : nullptr;
-}
-
-sk_sp<sksg::Gradient> AttachGradient(const json::ValueRef& obj, AttachContext* ctx) {
- SkASSERT(obj.isObject());
-
- const auto stops = obj["g"];
- if (!stops.isObject())
- return nullptr;
-
- const auto stopCount = stops["p"].toDefault<int>(-1);
- if (stopCount < 0)
- return nullptr;
-
- sk_sp<sksg::Gradient> gradient_node;
- sk_sp<GradientAdapter> adapter;
-
- if (obj["t"].toDefault<int>(1) == 1) {
- auto linear_node = sksg::LinearGradient::Make();
- adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
- gradient_node = std::move(linear_node);
- } else {
- auto radial_node = sksg::RadialGradient::Make();
- adapter = sk_make_sp<RadialGradientAdapter>(radial_node, stopCount);
-
- // TODO: highlight, angle
- gradient_node = std::move(radial_node);
- }
-
- BindProperty<VectorValue>(stops["k"], &ctx->fAnimators,
- [adapter](const VectorValue& stops) {
- adapter->setColorStops(stops);
- });
- BindProperty<VectorValue>(obj["s"], &ctx->fAnimators,
- [adapter](const VectorValue& s) {
- adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
- });
- BindProperty<VectorValue>(obj["e"], &ctx->fAnimators,
- [adapter](const VectorValue& e) {
- adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
- });
-
- return gradient_node;
-}
-
-sk_sp<sksg::PaintNode> AttachPaint(const json::ValueRef& jpaint, AttachContext* ctx,
- sk_sp<sksg::PaintNode> paint_node) {
- if (paint_node) {
- paint_node->setAntiAlias(true);
-
- BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
- [paint_node](const ScalarValue& o) {
- // BM opacity is [0..100]
- paint_node->setOpacity(o * 0.01f);
- });
- }
-
- return paint_node;
-}
-
-sk_sp<sksg::PaintNode> AttachStroke(const json::ValueRef& jstroke, AttachContext* ctx,
- sk_sp<sksg::PaintNode> stroke_node) {
- SkASSERT(jstroke.isObject());
-
- if (!stroke_node)
- return nullptr;
-
- stroke_node->setStyle(SkPaint::kStroke_Style);
-
- auto width_attached = BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
- [stroke_node](const ScalarValue& w) {
- stroke_node->setStrokeWidth(w);
- });
- if (!width_attached)
- return nullptr;
-
- stroke_node->setStrokeMiter(jstroke["ml"].toDefault(4.0f));
-
- static constexpr SkPaint::Join gJoins[] = {
- SkPaint::kMiter_Join,
- SkPaint::kRound_Join,
- SkPaint::kBevel_Join,
- };
- stroke_node->setStrokeJoin(gJoins[SkTPin<int>(jstroke["lj"].toDefault<int>(1) - 1,
- 0, SK_ARRAY_COUNT(gJoins) - 1)]);
-
- static constexpr SkPaint::Cap gCaps[] = {
- SkPaint::kButt_Cap,
- SkPaint::kRound_Cap,
- SkPaint::kSquare_Cap,
- };
- stroke_node->setStrokeCap(gCaps[SkTPin<int>(jstroke["lc"].toDefault<int>(1) - 1,
- 0, SK_ARRAY_COUNT(gCaps) - 1)]);
-
- return stroke_node;
-}
-
-sk_sp<sksg::PaintNode> AttachColorFill(const json::ValueRef& jfill, AttachContext* ctx) {
- SkASSERT(jfill.isObject());
-
- return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
-}
-
-sk_sp<sksg::PaintNode> AttachGradientFill(const json::ValueRef& jfill, AttachContext* ctx) {
- SkASSERT(jfill.isObject());
-
- return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
-}
-
-sk_sp<sksg::PaintNode> AttachColorStroke(const json::ValueRef& jstroke, AttachContext* ctx) {
- SkASSERT(jstroke.isObject());
-
- return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
-}
-
-sk_sp<sksg::PaintNode> AttachGradientStroke(const json::ValueRef& jstroke, AttachContext* ctx) {
- SkASSERT(jstroke.isObject());
-
- return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
-}
-
-std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
- const json::ValueRef& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
- std::vector<sk_sp<sksg::GeometryNode>> merged;
-
- static constexpr sksg::Merge::Mode gModes[] = {
- sksg::Merge::Mode::kMerge, // "mm": 1
- sksg::Merge::Mode::kUnion, // "mm": 2
- sksg::Merge::Mode::kDifference, // "mm": 3
- sksg::Merge::Mode::kIntersect, // "mm": 4
- sksg::Merge::Mode::kXOR , // "mm": 5
- };
-
- const auto mode = gModes[SkTPin<int>(jmerge["mm"].toDefault(1) - 1,
- 0, SK_ARRAY_COUNT(gModes) - 1)];
- merged.push_back(sksg::Merge::Make(std::move(geos), mode));
-
- return merged;
-}
-
-std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
- const json::ValueRef& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
-
- enum class Mode {
- kMerged, // "m": 1
- kSeparate, // "m": 2
- } gModes[] = { Mode::kMerged, Mode::kSeparate };
-
- const auto mode = gModes[SkTPin<int>(jtrim["m"].toDefault(1) - 1,
- 0, SK_ARRAY_COUNT(gModes) - 1)];
-
- std::vector<sk_sp<sksg::GeometryNode>> inputs;
- if (mode == Mode::kMerged) {
- inputs.push_back(sksg::Merge::Make(std::move(geos), sksg::Merge::Mode::kMerge));
- } else {
- inputs = std::move(geos);
- }
-
- std::vector<sk_sp<sksg::GeometryNode>> trimmed;
- trimmed.reserve(inputs.size());
- for (const auto& i : inputs) {
- const auto trimEffect = sksg::TrimEffect::Make(i);
- trimmed.push_back(trimEffect);
-
- const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
- BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
- [adapter](const ScalarValue& s) {
- adapter->setStart(s);
- });
- BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
- [adapter](const ScalarValue& e) {
- adapter->setEnd(e);
- });
- BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
- [adapter](const ScalarValue& o) {
- adapter->setOffset(o);
- });
- }
-
- return trimmed;
-}
-
-std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
- const json::ValueRef& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
-
- std::vector<sk_sp<sksg::GeometryNode>> rounded;
- rounded.reserve(geos.size());
-
- for (const auto& g : geos) {
- const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
- rounded.push_back(roundEffect);
-
- BindProperty<ScalarValue>(jtrim["r"], &ctx->fAnimators,
- [roundEffect](const ScalarValue& r) {
- roundEffect->setRadius(r);
- });
- }
-
- return rounded;
-}
-
-using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const json::ValueRef&, AttachContext*);
-static constexpr GeometryAttacherT gGeometryAttachers[] = {
- AttachPathGeometry,
- AttachRRectGeometry,
- AttachEllipseGeometry,
- AttachPolystarGeometry,
-};
-
-using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const json::ValueRef&, AttachContext*);
-static constexpr PaintAttacherT gPaintAttachers[] = {
- AttachColorFill,
- AttachColorStroke,
- AttachGradientFill,
- AttachGradientStroke,
-};
-
-using GeometryEffectAttacherT =
- std::vector<sk_sp<sksg::GeometryNode>> (*)(const json::ValueRef&,
- AttachContext*,
- std::vector<sk_sp<sksg::GeometryNode>>&&);
-static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
- AttachMergeGeometryEffect,
- AttachTrimGeometryEffect,
- AttachRoundGeometryEffect,
-};
-
-enum class ShapeType {
- kGeometry,
- kGeometryEffect,
- kPaint,
- kGroup,
- kTransform,
-};
-
-struct ShapeInfo {
- const char* fTypeString;
- ShapeType fShapeType;
- uint32_t fAttacherIndex; // index into respective attacher tables
-};
-
-const ShapeInfo* FindShapeInfo(const json::ValueRef& shape) {
- static constexpr ShapeInfo gShapeInfo[] = {
- { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry
- { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill
- { "gf", ShapeType::kPaint , 2 }, // gfill -> AttachGradientFill
- { "gr", ShapeType::kGroup , 0 }, // group -> Inline handler
- { "gs", ShapeType::kPaint , 3 }, // gstroke -> AttachGradientStroke
- { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect
- { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry
- { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect
- { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry
- { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry
- { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke
- { "tm", ShapeType::kGeometryEffect, 1 }, // trim -> AttachTrimGeometryEffect
- { "tr", ShapeType::kTransform , 0 }, // transform -> Inline handler
- };
-
- SkString type;
- if (!shape["ty"].to(&type) || type.isEmpty())
- return nullptr;
-
- const auto* info = bsearch(type.c_str(),
- gShapeInfo,
- SK_ARRAY_COUNT(gShapeInfo),
- sizeof(ShapeInfo),
- [](const void* key, const void* info) {
- return strcmp(static_cast<const char*>(key),
- static_cast<const ShapeInfo*>(info)->fTypeString);
- });
-
- return static_cast<const ShapeInfo*>(info);
-}
-
-struct GeometryEffectRec {
- const json::ValueRef fJson;
- GeometryEffectAttacherT fAttach;
-};
-
-struct AttachShapeContext {
- AttachShapeContext(AttachContext* ctx,
- std::vector<sk_sp<sksg::GeometryNode>>* geos,
- std::vector<GeometryEffectRec>* effects,
- size_t committedAnimators)
- : fCtx(ctx)
- , fGeometryStack(geos)
- , fGeometryEffectStack(effects)
- , fCommittedAnimators(committedAnimators) {}
-
- AttachContext* fCtx;
- std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
- std::vector<GeometryEffectRec>* fGeometryEffectStack;
- size_t fCommittedAnimators;
-};
-
-sk_sp<sksg::RenderNode> AttachShape(const json::ValueRef& jshape, AttachShapeContext* shapeCtx) {
- if (!jshape.isArray())
- return nullptr;
-
- SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
-
- sk_sp<sksg::Group> shape_group = sksg::Group::Make();
- sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
- sk_sp<sksg::Matrix> shape_matrix;
-
- struct ShapeRec {
- const json::ValueRef fJson;
- const ShapeInfo& fInfo;
- };
-
- // First pass (bottom->top):
- //
- // * pick up the group transform and opacity
- // * push local geometry effects onto the stack
- // * store recs for next pass
- //
- std::vector<ShapeRec> recs;
- for (size_t i = 0; i < jshape.size(); ++i) {
- const auto s = jshape[jshape.size() - 1 - i];
- const auto* info = FindShapeInfo(s);
- if (!info) {
- LogFail(s["ty"], "Unknown shape");
- continue;
- }
-
- recs.push_back({ s, *info });
-
- switch (info->fShapeType) {
- case ShapeType::kTransform:
- if ((shape_matrix = AttachMatrix(s, shapeCtx->fCtx, nullptr))) {
- shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
- }
- shape_wrapper = AttachOpacity(s, shapeCtx->fCtx, std::move(shape_wrapper));
- break;
- case ShapeType::kGeometryEffect:
- SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
- shapeCtx->fGeometryEffectStack->push_back(
- { s, gGeometryEffectAttachers[info->fAttacherIndex] });
- break;
- default:
- break;
- }
- }
-
- // Second pass (top -> bottom, after 2x reverse):
- //
- // * track local geometry
- // * emit local paints
- //
- std::vector<sk_sp<sksg::GeometryNode>> geos;
- std::vector<sk_sp<sksg::RenderNode >> draws;
- for (auto rec = recs.rbegin(); rec != recs.rend(); ++rec) {
- switch (rec->fInfo.fShapeType) {
- case ShapeType::kGeometry: {
- SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
- if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
- shapeCtx->fCtx)) {
- geos.push_back(std::move(geo));
- }
- } break;
- case ShapeType::kGeometryEffect: {
- // Apply the current effect and pop from the stack.
- SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
- if (!geos.empty()) {
- geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
- shapeCtx->fCtx,
- std::move(geos));
- }
-
- SkASSERT(shapeCtx->fGeometryEffectStack->back().fJson == rec->fJson);
- SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
- gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
- shapeCtx->fGeometryEffectStack->pop_back();
- } break;
- case ShapeType::kGroup: {
- AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
- &geos,
- shapeCtx->fGeometryEffectStack,
- shapeCtx->fCommittedAnimators);
- if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
- draws.push_back(std::move(subgroup));
- SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
- shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
- }
- } break;
- case ShapeType::kPaint: {
- SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
- auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
- if (!paint || geos.empty())
- break;
-
- auto drawGeos = geos;
-
- // Apply all pending effects from the stack.
- for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
- it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
- drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
- }
-
- // If we still have multiple geos, reduce using 'merge'.
- auto geo = drawGeos.size() > 1
- ? sksg::Merge::Make(std::move(drawGeos), sksg::Merge::Mode::kMerge)
- : drawGeos[0];
-
- SkASSERT(geo);
- draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
- shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
- } break;
- default:
- break;
- }
- }
-
- // By now we should have popped all local geometry effects.
- SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
-
- // Push transformed local geometries to parent list, for subsequent paints.
- for (const auto& geo : geos) {
- shapeCtx->fGeometryStack->push_back(shape_matrix
- ? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
- : std::move(geo));
- }
-
- // Emit local draws reversed (bottom->top, per spec).
- for (auto it = draws.rbegin(); it != draws.rend(); ++it) {
- shape_group->addChild(std::move(*it));
- }
-
- return draws.empty() ? nullptr : shape_wrapper;
-}
-
-sk_sp<sksg::RenderNode> AttachNestedAnimation(const char* path, AttachContext* ctx) {
- class SkottieSGAdapter final : public sksg::RenderNode {
- public:
- explicit SkottieSGAdapter(sk_sp<Animation> animation)
- : fAnimation(std::move(animation)) {
- SkASSERT(fAnimation);
- }
-
- protected:
- SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override {
- return SkRect::MakeSize(fAnimation->size());
- }
-
- void onRender(SkCanvas* canvas) const override {
- fAnimation->render(canvas);
- }
-
- private:
- const sk_sp<Animation> fAnimation;
- };
-
- class SkottieAnimatorAdapter final : public sksg::Animator {
- public:
- SkottieAnimatorAdapter(sk_sp<Animation> animation, float frameRate)
- : fAnimation(std::move(animation))
- , fFrameRate(frameRate) {
- SkASSERT(fAnimation);
- SkASSERT(fFrameRate > 0);
- }
-
- protected:
- void onTick(float t) {
- // map back from frame # to ms.
- const auto t_ms = t * 1000 / fFrameRate;
- fAnimation->animationTick(t_ms);
- }
-
- private:
- const sk_sp<Animation> fAnimation;
- const float fFrameRate;
- };
-
- const auto resStream = ctx->fResources.openStream(path);
- if (!resStream || !resStream->hasLength()) {
- LOG("!! Could not open: %s\n", path);
- return nullptr;
- }
-
- auto animation = Animation::Make(resStream.get(), ctx->fResources);
- if (!animation) {
- LOG("!! Could not load nested animation: %s\n", path);
- return nullptr;
- }
-
- ctx->fAnimators.push_back(skstd::make_unique<SkottieAnimatorAdapter>(animation,
- ctx->fFrameRate));
-
- return sk_make_sp<SkottieSGAdapter>(std::move(animation));
-}
-
-sk_sp<sksg::RenderNode> AttachAssetRef(const json::ValueRef& jlayer, AttachContext* ctx,
- sk_sp<sksg::RenderNode>(*attach_proc)(const json::ValueRef& comp, AttachContext* ctx)) {
-
- const auto refId = jlayer["refId"].toDefault(SkString());
- if (refId.isEmpty()) {
- LOG("!! Layer missing refId\n");
- return nullptr;
- }
-
- if (refId.startsWith("$")) {
- return AttachNestedAnimation(refId.c_str() + 1, ctx);
- }
-
- const auto* asset_info = ctx->fAssets.find(refId);
- if (!asset_info) {
- LOG("!! Asset not found: '%s'\n", refId.c_str());
- return nullptr;
- }
-
- if (asset_info->fIsAttaching) {
- LOG("!! Asset cycle detected for: '%s'\n", refId.c_str());
- return nullptr;
- }
-
- asset_info->fIsAttaching = true;
- auto asset = attach_proc(asset_info->fAsset, ctx);
- asset_info->fIsAttaching = false;
-
- return asset;
-}
-
-sk_sp<sksg::RenderNode> AttachCompLayer(const json::ValueRef& jlayer, AttachContext* ctx,
- float* time_bias, float* time_scale) {
- SkASSERT(jlayer.isObject());
-
- const auto start_time = jlayer["st"].toDefault(0.0f),
- stretch_time = jlayer["sr"].toDefault(1.0f);
-
- *time_bias = -start_time;
- *time_scale = sk_ieee_float_divide(1, stretch_time);
- if (SkScalarIsNaN(*time_scale)) {
- *time_scale = 1;
- }
-
- return AttachAssetRef(jlayer, ctx, AttachComposition);
-}
-
-sk_sp<sksg::RenderNode> AttachSolidLayer(const json::ValueRef& jlayer, AttachContext*,
- float*, float*) {
- SkASSERT(jlayer.isObject());
-
- const auto size = SkSize::Make(jlayer["sw"].toDefault(0.0f),
- jlayer["sh"].toDefault(0.0f));
- const auto hex = jlayer["sc"].toDefault(SkString());
- uint32_t c;
- if (size.isEmpty() ||
- !hex.startsWith("#") ||
- !SkParse::FindHex(hex.c_str() + 1, &c)) {
- LogFail(jlayer, "Could not parse solid layer");
- return nullptr;
- }
-
- const SkColor color = 0xff000000 | c;
-
- return sksg::Draw::Make(sksg::Rect::Make(SkRect::MakeSize(size)),
- sksg::Color::Make(color));
-}
-
-sk_sp<sksg::RenderNode> AttachImageAsset(const json::ValueRef& jimage, AttachContext* ctx) {
- SkASSERT(jimage.isObject());
-
- const auto name = jimage["p"].toDefault(SkString()),
- path = jimage["u"].toDefault(SkString());
- if (name.isEmpty())
- return nullptr;
-
- // TODO: plumb resource paths explicitly to ResourceProvider?
- const auto resName = path.isEmpty() ? name : SkOSPath::Join(path.c_str(), name.c_str());
- const auto resStream = ctx->fResources.openStream(resName.c_str());
- if (!resStream || !resStream->hasLength()) {
- LOG("!! Could not load image resource: %s\n", resName.c_str());
- return nullptr;
- }
-
- // TODO: non-intrisic image sizing
- return sksg::Image::Make(
- SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
-}
-
-sk_sp<sksg::RenderNode> AttachImageLayer(const json::ValueRef& jlayer, AttachContext* ctx,
- float*, float*) {
- SkASSERT(jlayer.isObject());
-
- return AttachAssetRef(jlayer, ctx, AttachImageAsset);
-}
-
-sk_sp<sksg::RenderNode> AttachNullLayer(const json::ValueRef& layer, AttachContext*, float*, float*) {
- SkASSERT(layer.isObject());
-
- // Null layers are used solely to drive dependent transforms,
- // but we use free-floating sksg::Matrices for that purpose.
- return nullptr;
-}
-
-sk_sp<sksg::RenderNode> AttachShapeLayer(const json::ValueRef& layer, AttachContext* ctx,
- float*, float*) {
- SkASSERT(layer.isObject());
-
- std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
- std::vector<GeometryEffectRec> geometryEffectStack;
- AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
- auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
-
- // Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
- // geometries => at the end, we can end up with unused geometries, which are nevertheless alive
- // due to attached animators. To avoid this, we track committed animators and discard the
- // orphans here.
- SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
- ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
-
- return shapeNode;
-}
-
-sk_sp<sksg::RenderNode> AttachTextLayer(const json::ValueRef& layer, AttachContext*, float*, float*) {
- SkASSERT(layer.isObject());
-
- LOG("?? Text layer stub\n");
- return nullptr;
-}
-
-struct AttachLayerContext {
- AttachLayerContext(const json::ValueRef& jlayers, AttachContext* ctx)
- : fLayerList(jlayers), fCtx(ctx) {
- SkASSERT(fLayerList.isArray());
- }
-
- const json::ValueRef fLayerList;
- AttachContext* fCtx;
- SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
- sk_sp<sksg::RenderNode> fCurrentMatte;
-
- sk_sp<sksg::Matrix> AttachLayerMatrix(const json::ValueRef& jlayer) {
- SkASSERT(jlayer.isObject());
-
- const auto layer_index = jlayer["ind"].toDefault<int>(-1);
- if (layer_index < 0)
- return nullptr;
-
- if (auto* m = fLayerMatrixMap.find(layer_index))
- return *m;
-
- return this->AttachLayerMatrixImpl(jlayer, layer_index);
- }
-
-private:
- sk_sp<sksg::Matrix> AttachParentLayerMatrix(const json::ValueRef& jlayer, int layer_index) {
- SkASSERT(jlayer.isObject());
-
- const auto parent_index = jlayer["parent"].toDefault<int>(-1);
- if (parent_index < 0 || parent_index == layer_index)
- return nullptr;
-
- if (auto* m = fLayerMatrixMap.find(parent_index))
- return *m;
-
- for (const json::ValueRef l : fLayerList) {
- if (l["ind"].toDefault<int>(-1) == parent_index) {
- return this->AttachLayerMatrixImpl(l, parent_index);
- }
- }
-
- return nullptr;
- }
-
- sk_sp<sksg::Matrix> AttachLayerMatrixImpl(const json::ValueRef& jlayer, int layer_index) {
- SkASSERT(!fLayerMatrixMap.find(layer_index));
-
- // Add a stub entry to break recursion cycles.
- fLayerMatrixMap.set(layer_index, nullptr);
-
- auto parent_matrix = this->AttachParentLayerMatrix(jlayer, layer_index);
-
- return *fLayerMatrixMap.set(layer_index, AttachMatrix(jlayer["ks"], fCtx, parent_matrix));
- }
-};
-
-SkBlendMode MaskBlendMode(char mode) {
- switch (mode) {
- case 'a': return SkBlendMode::kSrcOver; // Additive
- case 's': return SkBlendMode::kExclusion; // Subtract
- case 'i': return SkBlendMode::kDstIn; // Intersect
- case 'l': return SkBlendMode::kLighten; // Lighten
- case 'd': return SkBlendMode::kDarken; // Darken
- case 'f': return SkBlendMode::kDifference; // Difference
- default: break;
- }
-
- return SkBlendMode::kSrcOver;
-}
-
-sk_sp<sksg::RenderNode> AttachMask(const json::ValueRef& jmask,
- AttachContext* ctx,
- sk_sp<sksg::RenderNode> childNode) {
- if (!jmask.isArray())
- return childNode;
-
- struct MaskRecord {
- sk_sp<sksg::Path> mask_path;
- sk_sp<sksg::Color> mask_paint;
- };
-
- SkSTArray<4, MaskRecord, true> mask_stack;
-
- bool opaque_mask = true;
-
- for (const json::ValueRef m : jmask) {
- if (!m.isObject())
- continue;
-
- auto mask_path = AttachPath(m["pt"], ctx);
- if (!mask_path) {
- LogFail(m, "Could not parse mask path");
- continue;
- }
-
- mask_path->setFillType(m["inv"].toDefault(false)
- ? SkPath::kInverseWinding_FillType
- : SkPath::kWinding_FillType);
-
- SkString mode;
- if (!m["mode"].to(&mode) ||
- mode.size() != 1 ||
- !strcmp(mode.c_str(), "n")) { // "None" masks have no effect.
- continue;
- }
-
- auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
- mask_paint->setAntiAlias(true);
- mask_paint->setBlendMode(MaskBlendMode(mode.c_str()[0]));
-
- const auto animator_count = ctx->fAnimators.size();
- BindProperty<ScalarValue>(m["o"], &ctx->fAnimators,
- [mask_paint](const ScalarValue& o) { mask_paint->setOpacity(o * 0.01f); });
-
- opaque_mask &= (animator_count == ctx->fAnimators.size() && mask_paint->getOpacity() >= 1);
-
- mask_stack.push_back({mask_path, mask_paint});
- }
-
- if (mask_stack.empty())
- return childNode;
-
- if (mask_stack.count() == 1 && opaque_mask) {
- // Single opaque mask => clip path.
- return sksg::ClipEffect::Make(std::move(childNode),
- std::move(mask_stack.front().mask_path),
- true);
- }
-
- auto mask_group = sksg::Group::Make();
- for (const auto& rec : mask_stack) {
- mask_group->addChild(sksg::Draw::Make(std::move(rec.mask_path),
- std::move(rec.mask_paint)));
-
- }
-
- return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
-}
-
-sk_sp<sksg::RenderNode> AttachLayer(const json::ValueRef& jlayer, AttachLayerContext* layerCtx) {
- if (!jlayer.isObject())
- return nullptr;
-
- using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const json::ValueRef&, AttachContext*,
- float* time_bias, float* time_scale);
- static constexpr LayerAttacher gLayerAttachers[] = {
- AttachCompLayer, // 'ty': 0
- AttachSolidLayer, // 'ty': 1
- AttachImageLayer, // 'ty': 2
- AttachNullLayer, // 'ty': 3
- AttachShapeLayer, // 'ty': 4
- AttachTextLayer, // 'ty': 5
- };
-
- int type = jlayer["ty"].toDefault<int>(-1);
- if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
- return nullptr;
- }
-
- sksg::AnimatorList layer_animators;
- AttachContext local_ctx = { layerCtx->fCtx->fResources,
- layerCtx->fCtx->fAssets,
- layerCtx->fCtx->fFrameRate,
- layer_animators};
-
- // Layer attachers may adjust these.
- float time_bias = 0,
- time_scale = 1;
-
- // Layer content.
- auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale);
-
- // Clip layers with explicit dimensions.
- float w = 0, h = 0;
- if (jlayer["w"].to(&w) && jlayer["h"].to(&h)) {
- layer = sksg::ClipEffect::Make(std::move(layer),
- sksg::Rect::Make(SkRect::MakeWH(w, h)),
- true);
- }
-
- // Optional layer mask.
- layer = AttachMask(jlayer["masksProperties"], &local_ctx, std::move(layer));
-
- // Optional layer transform.
- if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) {
- layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
- }
-
- // Optional layer opacity.
- layer = AttachOpacity(jlayer["ks"], &local_ctx, std::move(layer));
-
- class LayerController final : public sksg::GroupAnimator {
- public:
- LayerController(sksg::AnimatorList&& layer_animators,
- sk_sp<sksg::OpacityEffect> controlNode,
- float in, float out,
- float time_bias, float time_scale)
- : INHERITED(std::move(layer_animators))
- , fControlNode(std::move(controlNode))
- , fIn(in)
- , fOut(out)
- , fTimeBias(time_bias)
- , fTimeScale(time_scale) {}
-
- void onTick(float t) override {
- const auto active = (t >= fIn && t <= fOut);
-
- // Keep the layer fully transparent except for its [in..out] lifespan.
- // (note: opacity == 0 disables rendering, while opacity == 1 is a noop)
- fControlNode->setOpacity(active ? 1 : 0);
-
- // Dispatch ticks only while active.
- if (active)
- this->INHERITED::onTick((t + fTimeBias) * fTimeScale);
- }
-
- private:
- const sk_sp<sksg::OpacityEffect> fControlNode;
- const float fIn,
- fOut,
- fTimeBias,
- fTimeScale;
-
- using INHERITED = sksg::GroupAnimator;
- };
-
- auto controller_node = sksg::OpacityEffect::Make(std::move(layer));
- const auto in = jlayer["ip"].toDefault(0.0f),
- out = jlayer["op"].toDefault(in);
-
- if (!jlayer["tm"].isNull()) {
- LogFail(jlayer["tm"], "Unsupported time remapping");
- }
-
- if (in >= out || !controller_node)
- return nullptr;
-
- layerCtx->fCtx->fAnimators.push_back(
- skstd::make_unique<LayerController>(std::move(layer_animators),
- controller_node,
- in,
- out,
- time_bias,
- time_scale));
-
- if (jlayer["td"].toDefault(false)) {
- // This layer is a matte. We apply it as a mask to the next layer.
- layerCtx->fCurrentMatte = std::move(controller_node);
- return nullptr;
- }
-
- if (layerCtx->fCurrentMatte) {
- // There is a pending matte. Apply and reset.
- static constexpr sksg::MaskEffect::Mode gMaskModes[] = {
- sksg::MaskEffect::Mode::kNormal, // tt: 1
- sksg::MaskEffect::Mode::kInvert, // tt: 2
- };
- const auto matteType = jlayer["tt"].toDefault<int>(1) - 1;
-
- if (matteType >= 0 && matteType < SkTo<int>(SK_ARRAY_COUNT(gMaskModes))) {
- return sksg::MaskEffect::Make(std::move(controller_node),
- std::move(layerCtx->fCurrentMatte),
- gMaskModes[matteType]);
- }
- layerCtx->fCurrentMatte.reset();
- }
-
- return std::move(controller_node);
-}
-
-sk_sp<sksg::RenderNode> AttachComposition(const json::ValueRef& comp, AttachContext* ctx) {
- if (!comp.isObject())
- return nullptr;
-
- const auto jlayers = comp["layers"];
- if (!jlayers.isArray())
- return nullptr;
-
- SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
- AttachLayerContext layerCtx(jlayers, ctx);
-
- for (const json::ValueRef l : jlayers) {
- if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
- layers.push_back(std::move(layer_fragment));
- }
- }
-
- if (layers.empty()) {
- return nullptr;
- }
-
- // Layers are painted in bottom->top order.
- auto comp_group = sksg::Group::Make();
- for (int i = layers.count() - 1; i >= 0; --i) {
- comp_group->addChild(std::move(layers[i]));
- }
-
- return std::move(comp_group);
-}
-
-} // namespace
-
-sk_sp<Animation> Animation::Make(SkStream* stream, const ResourceProvider& res, Stats* stats) {
- Stats stats_storage;
- if (!stats)
- stats = &stats_storage;
- memset(stats, 0, sizeof(struct Stats));
-
- if (!stream->hasLength()) {
- // TODO: handle explicit buffering?
- LOG("!! cannot parse streaming content\n");
- return nullptr;
- }
-
- stats->fJsonSize = stream->getLength();
- const auto t0 = SkTime::GetMSecs();
-
- const json::Document doc(stream);
- const auto json = doc.root();
- if (!json.isObject())
- return nullptr;
-
- const auto t1 = SkTime::GetMSecs();
- stats->fJsonParseTimeMS = t1 - t0;
-
- const auto version = json["v"].toDefault(SkString());
- const auto size = SkSize::Make(json["w"].toDefault(0.0f),
- json["h"].toDefault(0.0f));
- const auto fps = json["fr"].toDefault(-1.0f);
-
- if (size.isEmpty() || version.isEmpty() || fps <= 0) {
- LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
- version.c_str(), size.width(), size.height(), fps);
- return nullptr;
- }
-
- const auto anim =
- sk_sp<Animation>(new Animation(res, std::move(version), size, fps, json, stats));
- const auto t2 = SkTime::GetMSecs();
- stats->fSceneParseTimeMS = t2 - t1;
- stats->fTotalLoadTimeMS = t2 - t0;
-
- return anim;
-}
-
-sk_sp<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res,
- Stats* stats) {
- class DirectoryResourceProvider final : public ResourceProvider {
- public:
- explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
-
- std::unique_ptr<SkStream> openStream(const char resource[]) const override {
- const auto resPath = SkOSPath::Join(fDir.c_str(), resource);
- return SkStream::MakeFromFile(resPath.c_str());
- }
-
- private:
- const SkString fDir;
- };
-
- const auto jsonStream = SkStream::MakeFromFile(path);
- if (!jsonStream)
- return nullptr;
-
- std::unique_ptr<ResourceProvider> defaultProvider;
- if (!res) {
- defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
- }
-
- return Make(jsonStream.get(), res ? *res : *defaultProvider, stats);
-}
-
-Animation::Animation(const ResourceProvider& resources,
- SkString version, const SkSize& size, SkScalar fps, const json::ValueRef& json,
- Stats* stats)
- : fVersion(std::move(version))
- , fSize(size)
- , fFrameRate(fps)
- , fInPoint(json["ip"].toDefault(0.0f))
- , fOutPoint(SkTMax(json["op"].toDefault(SK_ScalarMax), fInPoint)) {
-
- AssetMap assets;
- for (const json::ValueRef asset : json["assets"]) {
- if (asset.isObject()) {
- assets.set(asset["id"].toDefault(SkString()), { asset, false });
- }
- }
-
- sksg::AnimatorList animators;
- AttachContext ctx = { resources, assets, fFrameRate, animators };
- auto root = AttachComposition(json, &ctx);
-
- stats->fAnimatorCount = animators.size();
-
- fScene = sksg::Scene::Make(std::move(root), std::move(animators));
-
- // In case the client calls render before the first tick.
- this->animationTick(0);
-}
-
-Animation::~Animation() = default;
-
-void Animation::setShowInval(bool show) {
- if (fScene) {
- fScene->setShowInval(show);
- }
-}
-
-void Animation::render(SkCanvas* canvas, const SkRect* dstR) const {
- if (!fScene)
- return;
-
- SkAutoCanvasRestore restore(canvas, true);
- const SkRect srcR = SkRect::MakeSize(this->size());
- if (dstR) {
- canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
- }
- canvas->clipRect(srcR);
- fScene->render(canvas);
-}
-
-void Animation::animationTick(SkMSec ms) {
- if (!fScene)
- return;
-
- // 't' in the BM model really means 'frame #'
- auto t = static_cast<float>(ms) * fFrameRate / 1000;
-
- t = fInPoint + std::fmod(t, fOutPoint - fInPoint);
-
- fScene->animate(t);
-}
-
-} // namespace skottie
diff --git a/experimental/skottie/Skottie.h b/experimental/skottie/Skottie.h
deleted file mode 100644
index 0a89ca0438..0000000000
--- a/experimental/skottie/Skottie.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef Skottie_DEFINED
-#define Skottie_DEFINED
-
-#include "SkRefCnt.h"
-#include "SkSize.h"
-#include "SkString.h"
-#include "SkTypes.h"
-
-#include <memory>
-
-class SkCanvas;
-struct SkRect;
-class SkStream;
-
-namespace sksg { class Scene; }
-
-namespace skottie {
-
-namespace json { class ValueRef; }
-
-class SK_API ResourceProvider : public SkNoncopyable {
-public:
- virtual ~ResourceProvider() = default;
-
- virtual std::unique_ptr<SkStream> openStream(const char resource[]) const = 0;
-};
-
-class SK_API Animation : public SkRefCnt {
-public:
- struct Stats {
- float fTotalLoadTimeMS,
- fJsonParseTimeMS,
- fSceneParseTimeMS;
- size_t fJsonSize,
- fAnimatorCount;
- };
-
- static sk_sp<Animation> Make(SkStream*, const ResourceProvider&, Stats* = nullptr);
- static sk_sp<Animation> MakeFromFile(const char path[], const ResourceProvider* = nullptr,
- Stats* = nullptr);
-
- ~Animation() override;
-
- void render(SkCanvas*, const SkRect* dst = nullptr) const;
-
- void animationTick(SkMSec);
-
- const SkString& version() const { return fVersion; }
- const SkSize& size() const { return fSize; }
- SkScalar frameRate() const { return fFrameRate; }
- SkScalar inPoint() const { return fInPoint; }
- SkScalar outPoint() const { return fOutPoint; }
-
- void setShowInval(bool show);
-
-private:
- Animation(const ResourceProvider&, SkString ver, const SkSize& size, SkScalar fps,
- const json::ValueRef&, Stats*);
-
- SkString fVersion;
- SkSize fSize;
- SkScalar fFrameRate,
- fInPoint,
- fOutPoint;
-
- std::unique_ptr<sksg::Scene> fScene;
-
- typedef SkRefCnt INHERITED;
-};
-
-} // namespace skottie
-
-#endif // Skottie_DEFINED
diff --git a/experimental/skottie/SkottieAdapter.cpp b/experimental/skottie/SkottieAdapter.cpp
deleted file mode 100644
index a01599ccf6..0000000000
--- a/experimental/skottie/SkottieAdapter.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkottieAdapter.h"
-
-#include "SkMatrix.h"
-#include "SkottieValue.h"
-#include "SkPath.h"
-#include "SkRRect.h"
-#include "SkSGGradient.h"
-#include "SkSGPath.h"
-#include "SkSGRect.h"
-#include "SkSGTransform.h"
-#include "SkSGTrimEffect.h"
-
-#include <cmath>
-
-namespace skottie {
-
-RRectAdapter::RRectAdapter(sk_sp<sksg::RRect> wrapped_node)
- : fRRectNode(std::move(wrapped_node)) {}
-
-void RRectAdapter::apply() {
- // BM "position" == "center position"
- auto rr = SkRRect::MakeRectXY(SkRect::MakeXYWH(fPosition.x() - fSize.width() / 2,
- fPosition.y() - fSize.height() / 2,
- fSize.width(), fSize.height()),
- fRadius.width(),
- fRadius.height());
- fRRectNode->setRRect(rr);
-}
-
-TransformAdapter::TransformAdapter(sk_sp<sksg::Matrix> matrix)
- : fMatrixNode(std::move(matrix)) {}
-
-void TransformAdapter::apply() {
- SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
-
- t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
- t.postRotate(fRotation);
- t.postTranslate(fPosition.x(), fPosition.y());
- // TODO: skew
-
- fMatrixNode->setMatrix(t);
-}
-
-PolyStarAdapter::PolyStarAdapter(sk_sp<sksg::Path> wrapped_node, Type t)
- : fPathNode(std::move(wrapped_node))
- , fType(t) {}
-
-void PolyStarAdapter::apply() {
- static constexpr int kMaxPointCount = 100000;
- const auto count = SkToUInt(SkTPin(SkScalarRoundToInt(fPointCount), 0, kMaxPointCount));
- const auto arc = sk_ieee_float_divide(SK_ScalarPI * 2, count);
-
- const auto pt_on_circle = [](const SkPoint& c, SkScalar r, SkScalar a) {
- return SkPoint::Make(c.x() + r * std::cos(a),
- c.y() + r * std::sin(a));
- };
-
- // TODO: inner/outer "roundness"?
-
- SkPath poly;
-
- auto angle = SkDegreesToRadians(fRotation);
- poly.moveTo(pt_on_circle(fPosition, fOuterRadius, angle));
- poly.incReserve(fType == Type::kStar ? count * 2 : count);
-
- for (unsigned i = 0; i < count; ++i) {
- if (fType == Type::kStar) {
- poly.lineTo(pt_on_circle(fPosition, fInnerRadius, angle + arc * 0.5f));
- }
- angle += arc;
- poly.lineTo(pt_on_circle(fPosition, fOuterRadius, angle));
- }
-
- poly.close();
- fPathNode->setPath(poly);
-}
-
-GradientAdapter::GradientAdapter(sk_sp<sksg::Gradient> grad, size_t stopCount)
- : fGradient(std::move(grad))
- , fStopCount(stopCount) {}
-
-void GradientAdapter::apply() {
- this->onApply();
-
- // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ]
-
- if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) {
- SkDebugf("!! Invalid gradient stop array size: %zu", fColorStops.size());
- return;
- }
-
- std::vector<sksg::Gradient::ColorStop> stops;
-
- // TODO: merge/lerp opacity stops
- const auto csEnd = fColorStops.cbegin() + fStopCount * 4;
- for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) {
- const auto pos = cs[0];
- const VectorValue rgb({ cs[1], cs[2], cs[3] });
-
- stops.push_back({ pos, ValueTraits<VectorValue>::As<SkColor>(rgb) });
- }
-
- fGradient->setColorStops(std::move(stops));
-}
-
-LinearGradientAdapter::LinearGradientAdapter(sk_sp<sksg::LinearGradient> grad, size_t stopCount)
- : INHERITED(std::move(grad), stopCount) {}
-
-void LinearGradientAdapter::onApply() {
- auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
- grad->setStartPoint(this->startPoint());
- grad->setEndPoint(this->endPoint());
-}
-
-RadialGradientAdapter::RadialGradientAdapter(sk_sp<sksg::RadialGradient> grad, size_t stopCount)
- : INHERITED(std::move(grad), stopCount) {}
-
-void RadialGradientAdapter::onApply() {
- auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
- grad->setStartCenter(this->startPoint());
- grad->setEndCenter(this->startPoint());
- grad->setStartRadius(0);
- grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint()));
-}
-
-TrimEffectAdapter::TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)
- : fTrimEffect(std::move(trimEffect)) {
- SkASSERT(fTrimEffect);
-}
-
-void TrimEffectAdapter::apply() {
- // BM semantics: start/end are percentages, offset is "degrees" (?!).
- const auto start = fStart / 100,
- end = fEnd / 100,
- offset = fOffset / 360;
-
- auto startT = SkTMin(start, end) + offset,
- stopT = SkTMax(start, end) + offset;
- auto mode = SkTrimPathEffect::Mode::kNormal;
-
- if (stopT - startT < 1) {
- startT -= SkScalarFloorToScalar(startT);
- stopT -= SkScalarFloorToScalar(stopT);
-
- if (startT > stopT) {
- SkTSwap(startT, stopT);
- mode = SkTrimPathEffect::Mode::kInverted;
- }
- } else {
- startT = 0;
- stopT = 1;
- }
-
- fTrimEffect->setStart(startT);
- fTrimEffect->setStop(stopT);
- fTrimEffect->setMode(mode);
-}
-
-} // namespace skottie
diff --git a/experimental/skottie/SkottieAdapter.h b/experimental/skottie/SkottieAdapter.h
deleted file mode 100644
index e96c616fcf..0000000000
--- a/experimental/skottie/SkottieAdapter.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkottieAdapter_DEFINED
-#define SkottieAdapter_DEFINED
-
-#include "SkPoint.h"
-#include "SkRefCnt.h"
-#include "SkSize.h"
-
-#include <vector>
-
-namespace sksg {
-
-class Gradient;
-class LinearGradient;
-class Matrix;
-class Path;
-class RadialGradient;
-class RRect;
-class TrimEffect;
-
-};
-
-namespace skottie {
-
-#define ADAPTER_PROPERTY(p_name, p_type, p_default) \
- void set##p_name(const p_type& p) { \
- if (p == f##p_name) return; \
- f##p_name = p; \
- this->apply(); \
- } \
- private: \
- p_type f##p_name = p_default; \
- public:
-
-class RRectAdapter final : public SkRefCnt {
-public:
- explicit RRectAdapter(sk_sp<sksg::RRect>);
-
- ADAPTER_PROPERTY(Position, SkPoint , SkPoint::Make(0, 0))
- ADAPTER_PROPERTY(Size , SkSize , SkSize::Make(0, 0))
- ADAPTER_PROPERTY(Radius , SkSize , SkSize::Make(0, 0))
-
-private:
- void apply();
-
- sk_sp<sksg::RRect> fRRectNode;
-
- using INHERITED = SkRefCnt;
-};
-
-class PolyStarAdapter final : public SkRefCnt {
-public:
- enum class Type {
- kStar, kPoly,
- };
-
- PolyStarAdapter(sk_sp<sksg::Path>, Type);
-
- ADAPTER_PROPERTY(Position , SkPoint , SkPoint::Make(0, 0))
- ADAPTER_PROPERTY(PointCount , SkScalar, 0)
- ADAPTER_PROPERTY(InnerRadius , SkScalar, 0)
- ADAPTER_PROPERTY(OuterRadius , SkScalar, 0)
- ADAPTER_PROPERTY(InnerRoundness, SkScalar, 0)
- ADAPTER_PROPERTY(OuterRoundness, SkScalar, 0)
- ADAPTER_PROPERTY(Rotation , SkScalar, 0)
-
-private:
- void apply();
-
- sk_sp<sksg::Path> fPathNode;
- Type fType;
-
- using INHERITED = SkRefCnt;
-};
-
-class TransformAdapter final : public SkRefCnt {
-public:
- explicit TransformAdapter(sk_sp<sksg::Matrix>);
-
- ADAPTER_PROPERTY(AnchorPoint, SkPoint , SkPoint::Make(0, 0))
- ADAPTER_PROPERTY(Position , SkPoint , SkPoint::Make(0, 0))
- ADAPTER_PROPERTY(Scale , SkVector, SkPoint::Make(100, 100))
- ADAPTER_PROPERTY(Rotation , SkScalar, 0)
- ADAPTER_PROPERTY(Skew , SkScalar, 0)
- ADAPTER_PROPERTY(SkewAxis , SkScalar, 0)
-
-private:
- void apply();
-
- sk_sp<sksg::Matrix> fMatrixNode;
-
- using INHERITED = SkRefCnt;
-};
-
-class GradientAdapter : public SkRefCnt {
-public:
- ADAPTER_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0) )
- ADAPTER_PROPERTY(EndPoint , SkPoint , SkPoint::Make(0, 0) )
- ADAPTER_PROPERTY(ColorStops, std::vector<SkScalar>, std::vector<SkScalar>())
-
-protected:
- GradientAdapter(sk_sp<sksg::Gradient>, size_t stopCount);
-
- const SkPoint& startPoint() const { return fStartPoint; }
- const SkPoint& endPoint() const { return fEndPoint; }
-
- sk_sp<sksg::Gradient> fGradient;
- size_t fStopCount;
-
- virtual void onApply() = 0;
-
-private:
- void apply();
-
- using INHERITED = SkRefCnt;
-};
-
-class LinearGradientAdapter final : public GradientAdapter {
-public:
- LinearGradientAdapter(sk_sp<sksg::LinearGradient>, size_t stopCount);
-
-private:
- void onApply() override;
-
- using INHERITED = GradientAdapter;
-};
-
-class RadialGradientAdapter final : public GradientAdapter {
-public:
- RadialGradientAdapter(sk_sp<sksg::RadialGradient>, size_t stopCount);
-
-private:
- void onApply() override;
-
- using INHERITED = GradientAdapter;
-};
-
-class TrimEffectAdapter final : public SkRefCnt {
-public:
- explicit TrimEffectAdapter(sk_sp<sksg::TrimEffect>);
-
- ADAPTER_PROPERTY(Start , SkScalar, 0)
- ADAPTER_PROPERTY(End , SkScalar, 100)
- ADAPTER_PROPERTY(Offset, SkScalar, 0)
-
-private:
- void apply();
-
- sk_sp<sksg::TrimEffect> fTrimEffect;
-
- using INHERITED = SkRefCnt;
-};
-
-#undef ADAPTER_PROPERTY
-
-} // namespace skottie
-
-#endif // SkottieAdapter_DEFINED
diff --git a/experimental/skottie/SkottieAnimator.cpp b/experimental/skottie/SkottieAnimator.cpp
deleted file mode 100644
index 4554409761..0000000000
--- a/experimental/skottie/SkottieAnimator.cpp
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkottieAnimator.h"
-
-#include "SkCubicMap.h"
-#include "SkottieJson.h"
-#include "SkottieValue.h"
-#include "SkString.h"
-#include "SkTArray.h"
-
-#include <memory>
-
-namespace skottie {
-
-namespace {
-
-#define LOG SkDebugf
-
-bool LogFail(const json::ValueRef& json, const char* msg) {
- const auto dump = json.toString();
- LOG("!! %s: %s\n", msg, dump.c_str());
- return false;
-}
-
-class KeyframeAnimatorBase : public sksg::Animator {
-public:
- int count() const { return fRecs.count(); }
-
-protected:
- KeyframeAnimatorBase() = default;
-
- struct KeyframeRec {
- float t0, t1;
- int vidx0, vidx1, // v0/v1 indices
- cmidx; // cubic map index
-
- bool contains(float t) const { return t0 <= t && t <= t1; }
- bool isConstant() const { return vidx0 == vidx1; }
- bool isValid() const {
- SkASSERT(t0 <= t1);
- // Constant frames don't need/use t1 and vidx1.
- return t0 < t1 || this->isConstant();
- }
- };
-
- const KeyframeRec& frame(float t) {
- if (!fCachedRec || !fCachedRec->contains(t)) {
- fCachedRec = findFrame(t);
- }
- return *fCachedRec;
- }
-
- float localT(const KeyframeRec& rec, float t) const {
- SkASSERT(rec.isValid());
- SkASSERT(!rec.isConstant());
- SkASSERT(t > rec.t0 && t < rec.t1);
-
- auto lt = (t - rec.t0) / (rec.t1 - rec.t0);
-
- return rec.cmidx < 0
- ? lt
- : SkTPin(fCubicMaps[rec.cmidx].computeYFromX(lt), 0.0f, 1.0f);
- }
-
- virtual int parseValue(const json::ValueRef&) = 0;
-
- void parseKeyFrames(const json::ValueRef& jframes) {
- if (!jframes.isArray())
- return;
-
- for (const json::ValueRef jframe : jframes) {
- float t0;
- if (!jframe["t"].to(&t0))
- continue;
-
- if (!fRecs.empty()) {
- if (fRecs.back().t1 >= t0) {
- LOG("!! Ignoring out-of-order key frame (t:%f < t:%f)\n", t0, fRecs.back().t1);
- continue;
- }
- // Back-fill t1 in prev interval. Note: we do this even if we end up discarding
- // the current interval (to support "t"-only final frames).
- fRecs.back().t1 = t0;
- }
-
- const auto vidx0 = this->parseValue(jframe["s"]);
- if (vidx0 < 0)
- continue;
-
- // Defaults for constant frames.
- int vidx1 = vidx0, cmidx = -1;
-
- if (!jframe["h"].toDefault(false)) {
- // Regular frame, requires an end value.
- vidx1 = this->parseValue(jframe["e"]);
- if (vidx1 < 0)
- continue;
-
- // default is linear lerp
- static constexpr SkPoint kDefaultC0 = { 0, 0 },
- kDefaultC1 = { 1, 1 };
- const auto c0 = jframe["i"].toDefault(kDefaultC0),
- c1 = jframe["o"].toDefault(kDefaultC1);
-
- if (c0 != kDefaultC0 || c1 != kDefaultC1) {
- // TODO: is it worth de-duping these?
- cmidx = fCubicMaps.count();
- fCubicMaps.emplace_back();
- // TODO: why do we have to plug these inverted?
- fCubicMaps.back().setPts(c1, c0);
- }
- }
-
- fRecs.push_back({t0, t0, vidx0, vidx1, cmidx });
- }
-
- // If we couldn't determine a valid t1 for the last frame, discard it.
- if (!fRecs.empty() && !fRecs.back().isValid()) {
- fRecs.pop_back();
- }
-
- SkASSERT(fRecs.empty() || fRecs.back().isValid());
- }
-
-private:
- const KeyframeRec* findFrame(float t) const {
- SkASSERT(!fRecs.empty());
-
- auto f0 = &fRecs.front(),
- f1 = &fRecs.back();
-
- SkASSERT(f0->isValid());
- SkASSERT(f1->isValid());
-
- if (t < f0->t0) {
- return f0;
- }
-
- if (t > f1->t1) {
- return f1;
- }
-
- while (f0 != f1) {
- SkASSERT(f0 < f1);
- SkASSERT(t >= f0->t0 && t <= f1->t1);
-
- const auto f = f0 + (f1 - f0) / 2;
- SkASSERT(f->isValid());
-
- if (t > f->t1) {
- f0 = f + 1;
- } else {
- f1 = f;
- }
- }
-
- SkASSERT(f0 == f1);
- SkASSERT(f0->contains(t));
-
- return f0;
- }
-
- SkTArray<KeyframeRec> fRecs;
- SkTArray<SkCubicMap> fCubicMaps;
- const KeyframeRec* fCachedRec = nullptr;
-
- using INHERITED = sksg::Animator;
-};
-
-template <typename T>
-class KeyframeAnimator final : public KeyframeAnimatorBase {
-public:
- static std::unique_ptr<KeyframeAnimator> Make(const json::ValueRef& jframes,
- std::function<void(const T&)>&& apply) {
- std::unique_ptr<KeyframeAnimator> animator(new KeyframeAnimator(jframes, std::move(apply)));
- if (!animator->count())
- return nullptr;
-
- return animator;
- }
-
-protected:
- void onTick(float t) override {
- T val;
- this->eval(this->frame(t), t, &val);
-
- fApplyFunc(val);
- }
-
-private:
- KeyframeAnimator(const json::ValueRef& jframes,
- std::function<void(const T&)>&& apply)
- : fApplyFunc(std::move(apply)) {
- this->parseKeyFrames(jframes);
- }
-
- int parseValue(const json::ValueRef& jv) override {
- T val;
- if (!jv.to(&val) || (!fVs.empty() &&
- ValueTraits<T>::Cardinality(val) != ValueTraits<T>::Cardinality(fVs.back()))) {
- return -1;
- }
-
- // TODO: full deduping?
- if (fVs.empty() || val != fVs.back()) {
- fVs.push_back(std::move(val));
- }
- return fVs.count() - 1;
- }
-
- void eval(const KeyframeRec& rec, float t, T* v) const {
- SkASSERT(rec.isValid());
- if (rec.isConstant() || t <= rec.t0) {
- *v = fVs[rec.vidx0];
- } else if (t >= rec.t1) {
- *v = fVs[rec.vidx1];
- } else {
- const auto lt = this->localT(rec, t);
- const auto& v0 = fVs[rec.vidx0];
- const auto& v1 = fVs[rec.vidx1];
- *v = ValueTraits<T>::Lerp(v0, v1, lt);
- }
- }
-
- const std::function<void(const T&)> fApplyFunc;
- SkTArray<T> fVs;
-
-
- using INHERITED = KeyframeAnimatorBase;
-};
-
-template <typename T>
-static inline bool BindPropertyImpl(const json::ValueRef& jprop,
- sksg::AnimatorList* animators,
- std::function<void(const T&)>&& apply,
- const T* noop = nullptr) {
- if (!jprop.isObject())
- return false;
-
- const auto jpropA = jprop["a"];
- const auto jpropK = jprop["k"];
-
- // Older Json versions don't have an "a" animation marker.
- // For those, we attempt to parse both ways.
- if (!jpropA.toDefault(false)) {
- T val;
- if (jpropK.to<T>(&val)) {
- // Static property.
- if (noop && val == *noop)
- return false;
-
- apply(val);
- return true;
- }
-
- if (!jpropA.isNull()) {
- return LogFail(jprop, "Could not parse (explicit) static property");
- }
- }
-
- // Keyframe property.
- auto animator = KeyframeAnimator<T>::Make(jpropK, std::move(apply));
-
- if (!animator) {
- return LogFail(jprop, "Could not parse keyframed property");
- }
-
- animators->push_back(std::move(animator));
-
- return true;
-}
-
-class SplitPointAnimator final : public sksg::Animator {
-public:
- static std::unique_ptr<SplitPointAnimator> Make(const json::ValueRef& jprop,
- std::function<void(const VectorValue&)>&& apply,
- const VectorValue*) {
- if (!jprop.isObject())
- return nullptr;
-
- std::unique_ptr<SplitPointAnimator> split_animator(
- new SplitPointAnimator(std::move(apply)));
-
- // This raw pointer is captured in lambdas below. But the lambdas are owned by
- // the object itself, so the scope is bound to the life time of the object.
- auto* split_animator_ptr = split_animator.get();
-
- if (!BindPropertyImpl<ScalarValue>(jprop["x"], &split_animator->fAnimators,
- [split_animator_ptr](const ScalarValue& x) { split_animator_ptr->setX(x); }) ||
- !BindPropertyImpl<ScalarValue>(jprop["y"], &split_animator->fAnimators,
- [split_animator_ptr](const ScalarValue& y) { split_animator_ptr->setY(y); })) {
- LogFail(jprop, "Could not parse split property");
- return nullptr;
- }
-
- if (split_animator->fAnimators.empty()) {
- // Static split property, no need to hold on to the split animator.
- return nullptr;
- }
-
- return split_animator;
- }
-
- void onTick(float t) override {
- for (const auto& animator : fAnimators) {
- animator->tick(t);
- }
-
- const VectorValue vec = { fX, fY };
- fApplyFunc(vec);
- }
-
- void setX(const ScalarValue& x) { fX = x; }
- void setY(const ScalarValue& y) { fY = y; }
-
-private:
- explicit SplitPointAnimator(std::function<void(const VectorValue&)>&& apply)
- : fApplyFunc(std::move(apply)) {}
-
- const std::function<void(const VectorValue&)> fApplyFunc;
- sksg::AnimatorList fAnimators;
-
- ScalarValue fX = 0,
- fY = 0;
-
- using INHERITED = sksg::Animator;
-};
-
-bool BindSplitPositionProperty(const json::ValueRef& jprop,
- sksg::AnimatorList* animators,
- std::function<void(const VectorValue&)>&& apply,
- const VectorValue* noop) {
- if (auto split_animator = SplitPointAnimator::Make(jprop, std::move(apply), noop)) {
- animators->push_back(std::unique_ptr<sksg::Animator>(split_animator.release()));
- return true;
- }
-
- return false;
-}
-
-} // namespace
-
-template <>
-bool BindProperty(const json::ValueRef& jprop,
- sksg::AnimatorList* animators,
- std::function<void(const ScalarValue&)>&& apply,
- const ScalarValue* noop) {
- return BindPropertyImpl(jprop, animators, std::move(apply), noop);
-}
-
-template <>
-bool BindProperty(const json::ValueRef& jprop,
- sksg::AnimatorList* animators,
- std::function<void(const VectorValue&)>&& apply,
- const VectorValue* noop) {
- return jprop["s"].toDefault<bool>(false)
- ? BindSplitPositionProperty(jprop, animators, std::move(apply), noop)
- : BindPropertyImpl(jprop, animators, std::move(apply), noop);
-}
-
-template <>
-bool BindProperty(const json::ValueRef& jprop,
- sksg::AnimatorList* animators,
- std::function<void(const ShapeValue&)>&& apply,
- const ShapeValue* noop) {
- return BindPropertyImpl(jprop, animators, std::move(apply), noop);
-}
-
-} // namespace skottie
diff --git a/experimental/skottie/SkottieAnimator.h b/experimental/skottie/SkottieAnimator.h
deleted file mode 100644
index 6dc8f6c759..0000000000
--- a/experimental/skottie/SkottieAnimator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkottieAnimator_DEFINED
-#define SkottieAnimator_DEFINED
-
-#include "SkSGScene.h"
-
-#include <functional>
-
-namespace skottie {
-
-namespace json { class ValueRef; }
-
-// This is the workhorse for property binding: depending on whether the property is animated,
-// it will either apply immediately or instantiate and attach a keyframe animator.
-template <typename T>
-bool BindProperty(const json::ValueRef&,
- sksg::AnimatorList*,
- std::function<void(const T&)>&&,
- const T* noop = nullptr);
-
-} // namespace skottie
-
-#endif // SkottieAnimator_DEFINED
diff --git a/experimental/skottie/SkottieJson.cpp b/experimental/skottie/SkottieJson.cpp
deleted file mode 100644
index 23e616d1ea..0000000000
--- a/experimental/skottie/SkottieJson.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkottieJson.h"
-
-#include "SkData.h"
-#include "SkScalar.h"
-#include "SkPath.h"
-#include "SkPoint.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkottieValue.h"
-
-#include "rapidjson/error/en.h"
-#include "rapidjson/prettywriter.h"
-#include "rapidjson/stringbuffer.h"
-
-#include <vector>
-
-namespace skottie {
-
-namespace json {
-
-template <>
-bool ValueRef::to<SkScalar>(SkScalar* v) const {
- if (!fValue) return false;
-
- // Some versions wrap values as single-element arrays.
- if (fValue->IsArray() && fValue->Size() == 1) {
- return ValueRef(fValue->operator[](0)).to(v);
- }
-
- if (!fValue->IsNumber())
- return false;
-
- *v = static_cast<SkScalar>(fValue->GetDouble());
-
- return true;
-}
-
-template <>
-bool ValueRef::to<bool>(bool* v) const {
- if (!fValue) return false;
-
- switch(fValue->GetType()) {
- case rapidjson::kNumberType:
- *v = SkToBool(fValue->GetDouble());
- return true;
- case rapidjson::kFalseType:
- case rapidjson::kTrueType:
- *v = fValue->GetBool();
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-template <>
-bool ValueRef::to<int>(int* v) const {
- if (!fValue || !fValue->IsInt())
- return false;
-
- *v = fValue->GetInt();
-
- return true;
-}
-
-template <>
-bool ValueRef::to<SkString>(SkString* v) const {
- if (!fValue || !fValue->IsString())
- return false;
-
- v->set(fValue->GetString());
-
- return true;
-}
-
-template <>
-bool ValueRef::to<SkPoint>(SkPoint* v) const {
- if (!fValue || !fValue->IsObject())
- return false;
-
- const auto jvx = ValueRef(this->operator[]("x")),
- jvy = ValueRef(this->operator[]("y"));
-
- // Some BM versions seem to store x/y as single-element arrays.
- return ValueRef(jvx.isArray() ? jvx.operator[](size_t(0)) : jvx).to(&v->fX)
- && ValueRef(jvy.isArray() ? jvy.operator[](size_t(0)) : jvy).to(&v->fY);
-}
-
-template <>
-bool ValueRef::to<std::vector<float>>(std::vector<float>* v) const {
- if (!fValue || !fValue->IsArray())
- return false;
-
- v->resize(fValue->Size());
- for (size_t i = 0; i < fValue->Size(); ++i) {
- if (!ValueRef(fValue->operator[](i)).to(v->data() + i)) {
- return false;
- }
- }
-
- return true;
-}
-
-namespace {
-
-bool ParsePointVec(const ValueRef& jv, std::vector<SkPoint>* pts) {
- if (!jv.isArray())
- return false;
-
- pts->clear();
- pts->reserve(jv.size());
-
- std::vector<float> vec;
- for (size_t i = 0; i < jv.size(); ++i) {
- if (!jv[i].to(&vec) || vec.size() != 2)
- return false;
- pts->push_back(SkPoint::Make(vec[0], vec[1]));
- }
-
- return true;
-}
-
-} // namespace
-
-template <>
-bool ValueRef::to<ShapeValue>(ShapeValue* v) const {
- SkASSERT(v->fVertices.empty());
-
- if (!fValue)
- return false;
-
- // Some versions wrap values as single-element arrays.
- if (fValue->IsArray() && fValue->Size() == 1) {
- return ValueRef(fValue->operator[](0)).to(v);
- }
-
- std::vector<SkPoint> inPts, // Cubic Bezier "in" control points, relative to vertices.
- outPts, // Cubic Bezier "out" control points, relative to vertices.
- verts; // Cubic Bezier vertices.
-
- if (!fValue->IsObject() ||
- !ParsePointVec(this->operator[]("i"), &inPts) ||
- !ParsePointVec(this->operator[]("o"), &outPts) ||
- !ParsePointVec(this->operator[]("v"), &verts) ||
- inPts.size() != outPts.size() ||
- inPts.size() != verts.size()) {
-
- return false;
- }
-
- v->fVertices.reserve(inPts.size());
- for (size_t i = 0; i < inPts.size(); ++i) {
- v->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]}));
- }
- v->fClosed = this->operator[]("c").toDefault<bool>(false);
-
- return true;
-}
-
-size_t ValueRef::size() const {
- return this->isArray() ? fValue->Size() : 0;
-}
-
-ValueRef ValueRef::operator[](size_t i) const {
- return i < this->size() ? ValueRef(fValue->operator[](i)) : ValueRef();
-}
-
-ValueRef ValueRef::operator[](const char* key) const {
- if (!this->isObject())
- return ValueRef();
-
- const auto m = fValue->FindMember(key);
- return m == fValue->MemberEnd() ? ValueRef() : ValueRef(m->value);
-}
-
-const rapidjson::Value* ValueRef::begin() const {
- return this->isArray() ? fValue->Begin() : nullptr;
-}
-
-const rapidjson::Value* ValueRef::end() const {
- return this->isArray() ? fValue->End() : nullptr;
-}
-
-SkString ValueRef::toString() const {
-#ifdef SK_DEBUG
- rapidjson::StringBuffer buf;
- if (fValue) {
- rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buf);
- fValue->Accept(writer);
- }
-
- return SkString(buf.GetString());
-#else
- return SkString();
-#endif // SK_DEBUG
-}
-
-Document::Document(SkStream* stream) {
- if (!stream->hasLength()) {
- SkDebugf("!! unsupported unseekable json stream\n");
- return;
- }
-
- // RapidJSON provides three DOM-builder approaches:
- //
- // 1) in-place : all data buffered, constructs the DOM in-place -- this is the fastest
- // 2) from buffer: all data buffered, copies to DOM -- this is slightly slower
- // 3) from stream: streamed data, reads/copies to DOM -- this is *significantly* slower
- //
- // We like fast, so #1 it is.
-
- // The buffer needs to be C-string.
- const auto size = stream->getLength();
- fData = SkData::MakeUninitialized(size + 1);
- if (stream->read(fData->writable_data(), size) < size) {
- SkDebugf("!! could not read JSON stream\n");
- return;
- }
-
- auto data = static_cast<char*>(fData->writable_data());
- data[size] = '\0';
-
- fDocument.ParseInsitu(data);
-
-#ifdef SK_DEBUG
- if (fDocument.HasParseError()) {
- SkDebugf("!! failed to parse json: %s\n",
- rapidjson::GetParseError_En(fDocument.GetParseError()));
- }
-#endif
-}
-
-} // namespace json
-
-} // namespace skottie
diff --git a/experimental/skottie/SkottieJson.h b/experimental/skottie/SkottieJson.h
deleted file mode 100644
index 76e17c610e..0000000000
--- a/experimental/skottie/SkottieJson.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkottieJson_DEFINED
-#define SkottieJson_DEFINED
-
-#include "SkRefCnt.h"
-
-#include "rapidjson/document.h"
-
-class SkData;
-class SkStream;
-class SkString;
-
-namespace skottie {
-
-namespace json {
-
-class ValueRef {
-public:
- ValueRef() : fValue(nullptr) {}
- ValueRef(const rapidjson::Value& v) : fValue(v.IsNull() ? nullptr : &v) {}
-
- bool isNull() const { return !fValue; }
- bool isObject() const { return fValue && fValue->IsObject(); }
- bool isArray() const { return fValue && fValue->IsArray(); }
-
- template <typename T>
- bool to(T*) const;
-
- template <typename T>
- T toDefault(const T& defaultValue) const {
- T v;
- if (!this->to<T>(&v)) {
- v = defaultValue;
- }
- return v;
- }
-
- size_t size() const;
- ValueRef operator[](size_t i) const;
- ValueRef operator[](const char* key) const;
-
- bool operator==(const ValueRef& other) const { return fValue == other.fValue; }
- bool operator!=(const ValueRef& other) const { return !(*this == other); }
-
- const rapidjson::Value* begin() const;
- const rapidjson::Value* end() const;
-
- SkString toString() const;
-
-private:
- const rapidjson::Value* fValue;
-};
-
-// Container for the json DOM
-class Document {
-public:
- explicit Document(SkStream*);
-
- ValueRef root() const { return fDocument; }
-
-private:
- sk_sp<SkData> fData; // raw data
- rapidjson::Document fDocument; // in-place json DOM
-};
-
-} // namespace json
-
-} // namespace skottie
-
-#endif // SkottieJson_DEFINED
diff --git a/experimental/skottie/SkottieValue.cpp b/experimental/skottie/SkottieValue.cpp
deleted file mode 100644
index edfa891aa1..0000000000
--- a/experimental/skottie/SkottieValue.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkottieValue.h"
-
-#include "SkColor.h"
-#include "SkNx.h"
-#include "SkPoint.h"
-#include "SkSize.h"
-
-namespace skottie {
-
-template <>
-size_t ValueTraits<ScalarValue>::Cardinality(const ScalarValue&) {
- return 1;
-}
-
-template <>
-ScalarValue ValueTraits<ScalarValue>::Lerp(const ScalarValue& v0, const ScalarValue& v1, float t) {
- SkASSERT(t >= 0 && t <= 1);
- return v0 + (v1 - v0) * t;
-}
-
-template <>
-template <>
-SkScalar ValueTraits<ScalarValue>::As<SkScalar>(const ScalarValue& v) {
- return v;
-}
-
-template <>
-size_t ValueTraits<VectorValue>::Cardinality(const VectorValue& vec) {
- return vec.size();
-}
-
-template <>
-VectorValue ValueTraits<VectorValue>::Lerp(const VectorValue& v0, const VectorValue& v1, float t) {
- SkASSERT(v0.size() == v1.size());
-
- VectorValue v;
- v.reserve(v0.size());
-
- for (size_t i = 0; i < v0.size(); ++i) {
- v.push_back(ValueTraits<ScalarValue>::Lerp(v0[i], v1[i], t));
- }
-
- return v;
-}
-
-template <>
-template <>
-SkColor ValueTraits<VectorValue>::As<SkColor>(const VectorValue& v) {
- // best effort to turn this into a color
- const auto r = v.size() > 0 ? v[0] : 0,
- g = v.size() > 1 ? v[1] : 0,
- b = v.size() > 2 ? v[2] : 0,
- a = v.size() > 3 ? v[3] : 1;
-
- return SkColorSetARGB(SkTPin<SkScalar>(a, 0, 1) * 255,
- SkTPin<SkScalar>(r, 0, 1) * 255,
- SkTPin<SkScalar>(g, 0, 1) * 255,
- SkTPin<SkScalar>(b, 0, 1) * 255);
-}
-
-template <>
-template <>
-SkPoint ValueTraits<VectorValue>::As<SkPoint>(const VectorValue& vec) {
- // best effort to turn this into a point
- const auto x = vec.size() > 0 ? vec[0] : 0,
- y = vec.size() > 1 ? vec[1] : 0;
- return SkPoint::Make(x, y);
-}
-
-template <>
-template <>
-SkSize ValueTraits<VectorValue>::As<SkSize>(const VectorValue& vec) {
- const auto pt = ValueTraits::As<SkPoint>(vec);
- return SkSize::Make(pt.x(), pt.y());
-}
-
-template <>
-size_t ValueTraits<ShapeValue>::Cardinality(const ShapeValue& shape) {
- return shape.fVertices.size();
-}
-
-static SkPoint lerp_point(const SkPoint& v0, const SkPoint& v1, const Sk2f& t) {
- const auto v2f0 = Sk2f::Load(&v0),
- v2f1 = Sk2f::Load(&v1);
-
- SkPoint v;
- (v2f0 + (v2f1 - v2f0) * t).store(&v);
-
- return v;
-}
-
-template <>
-ShapeValue ValueTraits<ShapeValue>::Lerp(const ShapeValue& v0, const ShapeValue& v1, float t) {
- SkASSERT(t >= 0 && t <= 1);
- SkASSERT(v0.fVertices.size() == v1.fVertices.size());
- SkASSERT(v0.fClosed == v1.fClosed);
-
- ShapeValue v;
- v.fClosed = v0.fClosed;
- v.fVolatile = true; // interpolated values are volatile
-
- const auto t2f = Sk2f(t);
- v.fVertices.reserve(v0.fVertices.size());
-
- for (size_t i = 0; i < v0.fVertices.size(); ++i) {
- v.fVertices.emplace_back(BezierVertex({
- lerp_point(v0.fVertices[i].fInPoint , v1.fVertices[i].fInPoint , t2f),
- lerp_point(v0.fVertices[i].fOutPoint, v1.fVertices[i].fOutPoint, t2f),
- lerp_point(v0.fVertices[i].fVertex , v1.fVertices[i].fVertex , t2f)
- }));
- }
-
- return v;
-}
-
-template <>
-template <>
-SkPath ValueTraits<ShapeValue>::As<SkPath>(const ShapeValue& shape) {
- SkPath path;
-
- if (!shape.fVertices.empty()) {
- path.moveTo(shape.fVertices.front().fVertex);
- }
-
- const auto& addCubic = [&](size_t from, size_t to) {
- const auto c0 = shape.fVertices[from].fVertex + shape.fVertices[from].fOutPoint,
- c1 = shape.fVertices[to].fVertex + shape.fVertices[to].fInPoint;
-
- if (c0 == shape.fVertices[from].fVertex &&
- c1 == shape.fVertices[to].fVertex) {
- // If the control points are coincident, we can power-reduce to a straight line.
- // TODO: we could also do that when the controls are on the same line as the
- // vertices, but it's unclear how common that case is.
- path.lineTo(shape.fVertices[to].fVertex);
- } else {
- path.cubicTo(c0, c1, shape.fVertices[to].fVertex);
- }
- };
-
- for (size_t i = 1; i < shape.fVertices.size(); ++i) {
- addCubic(i - 1, i);
- }
-
- if (!shape.fVertices.empty() && shape.fClosed) {
- addCubic(shape.fVertices.size() - 1, 0);
- path.close();
- }
-
- path.setIsVolatile(shape.fVolatile);
-
- return path;
-}
-
-} // namespace skottie
diff --git a/experimental/skottie/SkottieValue.h b/experimental/skottie/SkottieValue.h
deleted file mode 100644
index cfdbd7aba7..0000000000
--- a/experimental/skottie/SkottieValue.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkottieValue_DEFINED
-#define SkottieValue_DEFINED
-
-#include "SkPath.h"
-#include "SkScalar.h"
-
-#include <vector>
-
-namespace skottie {
-
-template <typename T>
-struct ValueTraits {
- static size_t Cardinality(const T&);
-
- template <typename U>
- static U As(const T&);
-
- static T Lerp(const T&, const T&, float);
-};
-
-using ScalarValue = SkScalar;
-using VectorValue = std::vector<ScalarValue>;
-
-struct BezierVertex {
- SkPoint fInPoint, // "in" control point, relative to the vertex
- fOutPoint, // "out" control point, relative to the vertex
- fVertex;
-
- bool operator==(const BezierVertex& other) const {
- return fInPoint == other.fInPoint
- && fOutPoint == other.fOutPoint
- && fVertex == other.fVertex;
- }
-
- bool operator!=(const BezierVertex& other) const { return !(*this == other); }
-};
-
-struct ShapeValue {
- std::vector<BezierVertex> fVertices;
- bool fClosed : 1,
- fVolatile : 1;
-
- ShapeValue() : fClosed(false), fVolatile(false) {}
- ShapeValue(const ShapeValue&) = default;
- ShapeValue(ShapeValue&&) = default;
- ShapeValue& operator=(const ShapeValue&) = default;
-
- bool operator==(const ShapeValue& other) const {
- return fVertices == other.fVertices && fClosed == other.fClosed;
- }
-
- bool operator!=(const ShapeValue& other) const { return !(*this == other); }
-};
-
-} // namespace skottie
-
-#endif // SkottieValue_DEFINED