[skottie] Make AnimationBuilder available for value parsing

Plumb AnimationBuilder throught a bazillion layers to make it reachable
when parsing animatable values.

This is in preparation of keyframed text, which will require access to
the font set when parsing.

Refactor only, no functional changes.

TBR=
Change-Id: Ide2ef2ba66fbcc75fdcc785f987b364d45dff5b6
Reviewed-on: https://skia-review.googlesource.com/149264
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Florin Malita 2018-08-25 20:22:34 -04:00 committed by Skia Commit-Bot
parent 3054989426
commit 471a946cf1
13 changed files with 301 additions and 264 deletions

View File

@ -14,7 +14,6 @@ skia_skottie_sources = [
"$_src/SkottieAdapter.cpp",
"$_src/SkottieAdapter.h",
"$_src/SkottieAnimator.cpp",
"$_src/SkottieAnimator.h",
"$_src/SkottieJson.cpp",
"$_src/SkottieJson.h",
"$_src/SkottieLayer.cpp",

View File

@ -25,7 +25,6 @@
#include "SkTime.h"
#include "SkTo.h"
#include "SkottieAdapter.h"
#include "SkottieAnimator.h"
#include "SkottieJson.h"
#include "SkottiePriv.h"
#include "SkottieValue.h"
@ -43,23 +42,24 @@ void LogJSON(const skjson::Value& json, const char msg[]) {
LOG("%s: %s\n", msg, dump.c_str());
}
sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AnimatorScope* ascope,
sk_sp<sksg::Matrix> parentMatrix) {
sk_sp<sksg::Matrix> AnimationBuilder::attachMatrix(const skjson::ObjectValue& t,
AnimatorScope* ascope,
sk_sp<sksg::Matrix> parentMatrix) const {
static const VectorValue g_default_vec_0 = { 0, 0},
g_default_vec_100 = {100, 100};
auto matrix = sksg::Matrix::Make(SkMatrix::I(), parentMatrix);
auto adapter = sk_make_sp<TransformAdapter>(matrix);
auto bound = BindProperty<VectorValue>(t["a"], ascope,
auto bound = this->bindProperty<VectorValue>(t["a"], ascope,
[adapter](const VectorValue& a) {
adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
}, g_default_vec_0);
bound |= BindProperty<VectorValue>(t["p"], ascope,
bound |= this->bindProperty<VectorValue>(t["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
}, g_default_vec_0);
bound |= BindProperty<VectorValue>(t["s"], ascope,
bound |= this->bindProperty<VectorValue>(t["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
}, g_default_vec_100);
@ -70,15 +70,15 @@ sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AnimatorScope* as
// we can still make use of rz.
jrotation = &t["rz"];
}
bound |= BindProperty<ScalarValue>(*jrotation, ascope,
bound |= this->bindProperty<ScalarValue>(*jrotation, ascope,
[adapter](const ScalarValue& r) {
adapter->setRotation(r);
}, 0.0f);
bound |= BindProperty<ScalarValue>(t["sk"], ascope,
bound |= this->bindProperty<ScalarValue>(t["sk"], ascope,
[adapter](const ScalarValue& sk) {
adapter->setSkew(sk);
}, 0.0f);
bound |= BindProperty<ScalarValue>(t["sa"], ascope,
bound |= this->bindProperty<ScalarValue>(t["sa"], ascope,
[adapter](const ScalarValue& sa) {
adapter->setSkewAxis(sa);
}, 0.0f);
@ -86,14 +86,15 @@ sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AnimatorScope* as
return bound ? matrix : parentMatrix;
}
sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue& jtransform, AnimatorScope* ascope,
sk_sp<sksg::RenderNode> childNode) {
sk_sp<sksg::RenderNode> AnimationBuilder::attachOpacity(const skjson::ObjectValue& jtransform,
AnimatorScope* ascope,
sk_sp<sksg::RenderNode> childNode) const {
if (!childNode)
return nullptr;
auto opacityNode = sksg::OpacityEffect::Make(childNode);
if (!BindProperty<ScalarValue>(jtransform["o"], ascope,
if (!this->bindProperty<ScalarValue>(jtransform["o"], ascope,
[opacityNode](const ScalarValue& o) {
// BM opacity is [0..100]
opacityNode->setOpacity(o * 0.01f);
@ -105,9 +106,10 @@ sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue& jtransform, Ani
return std::move(opacityNode);
}
sk_sp<sksg::Path> AttachPath(const skjson::Value& jpath, AnimatorScope* ascope) {
sk_sp<sksg::Path> AnimationBuilder::attachPath(const skjson::Value& jpath,
AnimatorScope* ascope) const {
auto path_node = sksg::Path::Make();
return BindProperty<ShapeValue>(jpath, ascope,
return this->bindProperty<ShapeValue>(jpath, ascope,
[path_node](const ShapeValue& p) {
// FillType is tracked in the SG node, not in keyframes -- make sure we preserve it.
auto path = ValueTraits<ShapeValue>::As<SkPath>(p);
@ -118,10 +120,11 @@ sk_sp<sksg::Path> AttachPath(const skjson::Value& jpath, AnimatorScope* ascope)
: nullptr;
}
sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AnimatorScope* ascope,
const char prop_name[]) {
sk_sp<sksg::Color> AnimationBuilder::attachColor(const skjson::ObjectValue& jcolor,
AnimatorScope* ascope,
const char prop_name[]) const {
auto color_node = sksg::Color::Make(SK_ColorBLACK);
BindProperty<VectorValue>(jcolor[prop_name], ascope,
this->bindProperty<VectorValue>(jcolor[prop_name], ascope,
[color_node](const VectorValue& c) {
color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
});

View File

@ -5,11 +5,11 @@
* found in the LICENSE file.
*/
#include "SkottieAnimator.h"
#include "SkCubicMap.h"
#include "SkottieJson.h"
#include "SkottiePriv.h"
#include "SkottieValue.h"
#include "SkSGScene.h"
#include "SkString.h"
#include "SkTArray.h"
@ -68,9 +68,9 @@ protected:
: SkTPin(fCubicMaps[rec.cmidx].computeYFromX(lt), 0.0f, 1.0f);
}
virtual int parseValue(const skjson::Value&) = 0;
virtual int parseValue(const skjson::Value&, const AnimationBuilder* abuilder) = 0;
void parseKeyFrames(const skjson::ArrayValue& jframes) {
void parseKeyFrames(const skjson::ArrayValue& jframes, const AnimationBuilder* abuilder) {
for (const skjson::ObjectValue* jframe : jframes) {
if (!jframe) continue;
@ -88,7 +88,7 @@ protected:
fRecs.back().t1 = t0;
}
const auto vidx0 = this->parseValue((*jframe)["s"]);
const auto vidx0 = this->parseValue((*jframe)["s"], abuilder);
if (vidx0 < 0)
continue;
@ -97,7 +97,7 @@ protected:
if (!ParseDefault<bool>((*jframe)["h"], false)) {
// Regular frame, requires an end value.
vidx1 = this->parseValue((*jframe)["e"]);
vidx1 = this->parseValue((*jframe)["e"], abuilder);
if (vidx1 < 0)
continue;
@ -176,10 +176,12 @@ template <typename T>
class KeyframeAnimator final : public KeyframeAnimatorBase {
public:
static std::unique_ptr<KeyframeAnimator> Make(const skjson::ArrayValue* jv,
const AnimationBuilder* abuilder,
std::function<void(const T&)>&& apply) {
if (!jv) return nullptr;
std::unique_ptr<KeyframeAnimator> animator(new KeyframeAnimator(*jv, std::move(apply)));
std::unique_ptr<KeyframeAnimator> animator(
new KeyframeAnimator(*jv, abuilder, std::move(apply)));
if (!animator->count())
return nullptr;
@ -193,14 +195,16 @@ protected:
private:
KeyframeAnimator(const skjson::ArrayValue& jframes,
const AnimationBuilder* abuilder,
std::function<void(const T&)>&& apply)
: fApplyFunc(std::move(apply)) {
this->parseKeyFrames(jframes);
this->parseKeyFrames(jframes, abuilder);
}
int parseValue(const skjson::Value& jv) override {
int parseValue(const skjson::Value& jv, const AnimationBuilder* abuilder) override {
T val;
if (!Parse<T>(jv, &val) || (!fVs.empty() && !ValueTraits<T>::CanLerp(val, fVs.back()))) {
if (!ValueTraits<T>::FromJSON(jv, abuilder, &val) ||
(!fVs.empty() && !ValueTraits<T>::CanLerp(val, fVs.back()))) {
return -1;
}
@ -241,7 +245,8 @@ private:
template <typename T>
static inline bool BindPropertyImpl(const skjson::ObjectValue* jprop,
sksg::AnimatorList* animators,
const AnimationBuilder* abuilder,
AnimatorScope* ascope,
std::function<void(const T&)>&& apply,
const T* noop = nullptr) {
if (!jprop) return false;
@ -257,7 +262,7 @@ static inline bool BindPropertyImpl(const skjson::ObjectValue* jprop,
// For those, we attempt to parse both ways.
if (!ParseDefault<bool>(jpropA, false)) {
T val;
if (Parse<T>(jpropK, &val)) {
if (ValueTraits<T>::FromJSON(jpropK, abuilder, &val)) {
// Static property.
if (noop && val == *noop)
return false;
@ -272,13 +277,13 @@ static inline bool BindPropertyImpl(const skjson::ObjectValue* jprop,
}
// Keyframe property.
auto animator = KeyframeAnimator<T>::Make(jpropK, std::move(apply));
auto animator = KeyframeAnimator<T>::Make(jpropK, abuilder, std::move(apply));
if (!animator) {
return LogFail(*jprop, "Could not parse keyframed property");
}
animators->push_back(std::move(animator));
ascope->push_back(std::move(animator));
return true;
}
@ -286,6 +291,7 @@ static inline bool BindPropertyImpl(const skjson::ObjectValue* jprop,
class SplitPointAnimator final : public sksg::Animator {
public:
static std::unique_ptr<SplitPointAnimator> Make(const skjson::ObjectValue* jprop,
const AnimationBuilder* abuilder,
std::function<void(const VectorValue&)>&& apply,
const VectorValue*) {
if (!jprop) return nullptr;
@ -297,9 +303,9 @@ public:
// 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,
if (!BindPropertyImpl<ScalarValue>((*jprop)["x"], abuilder, &split_animator->fAnimators,
[split_animator_ptr](const ScalarValue& x) { split_animator_ptr->setX(x); }) ||
!BindPropertyImpl<ScalarValue>((*jprop)["y"], &split_animator->fAnimators,
!BindPropertyImpl<ScalarValue>((*jprop)["y"], abuilder, &split_animator->fAnimators,
[split_animator_ptr](const ScalarValue& y) { split_animator_ptr->setY(y); })) {
LogFail(*jprop, "Could not parse split property");
return nullptr;
@ -340,11 +346,12 @@ private:
};
bool BindSplitPositionProperty(const skjson::Value& jv,
sksg::AnimatorList* animators,
const AnimationBuilder* abuilder,
AnimatorScope* ascope,
std::function<void(const VectorValue&)>&& apply,
const VectorValue* noop) {
if (auto split_animator = SplitPointAnimator::Make(jv, std::move(apply), noop)) {
animators->push_back(std::unique_ptr<sksg::Animator>(split_animator.release()));
if (auto split_animator = SplitPointAnimator::Make(jv, abuilder, std::move(apply), noop)) {
ascope->push_back(std::unique_ptr<sksg::Animator>(split_animator.release()));
return true;
}
@ -354,32 +361,32 @@ bool BindSplitPositionProperty(const skjson::Value& jv,
} // namespace
template <>
bool BindProperty(const skjson::Value& jv,
bool AnimationBuilder::bindProperty(const skjson::Value& jv,
AnimatorScope* ascope,
std::function<void(const ScalarValue&)>&& apply,
const ScalarValue* noop) {
return BindPropertyImpl(jv, ascope, std::move(apply), noop);
const ScalarValue* noop) const {
return BindPropertyImpl(jv, this, ascope, std::move(apply), noop);
}
template <>
bool BindProperty(const skjson::Value& jv,
bool AnimationBuilder::bindProperty(const skjson::Value& jv,
AnimatorScope* ascope,
std::function<void(const VectorValue&)>&& apply,
const VectorValue* noop) {
const VectorValue* noop) const {
if (!jv.is<skjson::ObjectValue>())
return false;
return ParseDefault<bool>(jv.as<skjson::ObjectValue>()["s"], false)
? BindSplitPositionProperty(jv, ascope, std::move(apply), noop)
: BindPropertyImpl(jv, ascope, std::move(apply), noop);
? BindSplitPositionProperty(jv, this, ascope, std::move(apply), noop)
: BindPropertyImpl(jv, this, ascope, std::move(apply), noop);
}
template <>
bool BindProperty(const skjson::Value& jv,
bool AnimationBuilder::bindProperty(const skjson::Value& jv,
AnimatorScope* ascope,
std::function<void(const ShapeValue&)>&& apply,
const ShapeValue* noop) {
return BindPropertyImpl(jv, ascope, std::move(apply), noop);
const ShapeValue* noop) const {
return BindPropertyImpl(jv, this, ascope, std::move(apply), noop);
}
} // namespace internal

View File

@ -1,39 +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 "SkottiePriv.h"
#include <functional>
namespace skjson { class Value; }
namespace skottie {
namespace internal {
// 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 skjson::Value&,
AnimatorScope*,
std::function<void(const T&)>&&,
const T* default_igore = nullptr);
template <typename T>
bool BindProperty(const skjson::Value& jv,
AnimatorScope* animators,
std::function<void(const T&)>&& apply,
const T& default_ignore) {
return BindProperty(jv, animators, std::move(apply), &default_ignore);
}
} // namespace internal
} // namespace skottie
#endif // SkottieAnimator_DEFINED

View File

@ -110,63 +110,4 @@ bool Parse<std::vector<float>>(const Value& v, std::vector<float>* vec) {
return true;
}
namespace {
bool ParsePointVec(const Value& v, std::vector<SkPoint>* pts) {
if (!v.is<ArrayValue>())
return false;
const auto& av = v.as<ArrayValue>();
pts->clear();
pts->reserve(av.size());
std::vector<float> vec;
for (size_t i = 0; i < av.size(); ++i) {
if (!Parse(av[i], &vec) || vec.size() != 2)
return false;
pts->push_back(SkPoint::Make(vec[0], vec[1]));
}
return true;
}
} // namespace
template <>
bool Parse<ShapeValue>(const Value& v, ShapeValue* sh) {
SkASSERT(sh->fVertices.empty());
// Some versions wrap values as single-element arrays.
if (const skjson::ArrayValue* av = v) {
if (av->size() == 1) {
return Parse((*av)[0], sh);
}
}
if (!v.is<skjson::ObjectValue>())
return false;
const auto& ov = v.as<ObjectValue>();
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 (!ParsePointVec(ov["i"], &inPts) ||
!ParsePointVec(ov["o"], &outPts) ||
!ParsePointVec(ov["v"], &verts) ||
inPts.size() != outPts.size() ||
inPts.size() != verts.size()) {
return false;
}
sh->fVertices.reserve(inPts.size());
for (size_t i = 0; i < inPts.size(); ++i) {
sh->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]}));
}
sh->fClosed = ParseDefault<bool>(ov["c"], false);
return true;
}
} // namespace skottie

View File

@ -12,7 +12,6 @@
#include "SkImage.h"
#include "SkJSON.h"
#include "SkMakeUnique.h"
#include "SkottieAnimator.h"
#include "SkottieJson.h"
#include "SkottieValue.h"
#include "SkParse.h"
@ -65,6 +64,7 @@ const MaskInfo* GetMaskInfo(char mode) {
}
sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
const AnimationBuilder* abuilder,
AnimatorScope* ascope,
sk_sp<sksg::RenderNode> childNode) {
if (!jmask) return childNode;
@ -100,7 +100,7 @@ sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
continue;
}
auto mask_path = AttachPath((*m)["pt"], ascope);
auto mask_path = abuilder->attachPath((*m)["pt"], ascope);
if (!mask_path) {
LogJSON(*m, "!! Could not parse mask path");
continue;
@ -118,7 +118,7 @@ sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
mask_paint->setBlendMode(mask_stack.empty() ? SkBlendMode::kSrc
: mask_info->fBlendMode);
has_opacity |= BindProperty<ScalarValue>((*m)["o"], ascope,
has_opacity |= abuilder->bindProperty<ScalarValue>((*m)["o"], ascope,
[mask_paint](const ScalarValue& o) {
mask_paint->setOpacity(o * 0.01f);
}, 100.0f);
@ -164,7 +164,7 @@ sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
} // namespace
sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name,
AnimatorScope* ascope) {
AnimatorScope* ascope) const {
class SkottieSGAdapter final : public sksg::RenderNode {
public:
explicit SkottieSGAdapter(sk_sp<Animation> animation)
@ -230,7 +230,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name
sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
const skjson::ObjectValue& jlayer, AnimatorScope* ascope,
sk_sp<sksg::RenderNode>(AnimationBuilder::* attach_proc)(const skjson::ObjectValue& comp,
AnimatorScope* ascope)) {
AnimatorScope* ascope) const) const {
const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
if (refId.isEmpty()) {
@ -261,7 +261,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachSolidLayer(const skjson::ObjectValue& jlayer,
AnimatorScope*) {
AnimatorScope*) const {
const auto size = SkSize::Make(ParseDefault<float>(jlayer["sw"], 0.0f),
ParseDefault<float>(jlayer["sh"], 0.0f));
const skjson::StringValue* hex_str = jlayer["sc"];
@ -281,7 +281,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachSolidLayer(const skjson::ObjectV
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage,
AnimatorScope*) {
AnimatorScope*) const {
const skjson::StringValue* name = jimage["p"];
const skjson::StringValue* path = jimage["u"];
if (!name) {
@ -306,12 +306,12 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectV
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer,
AnimatorScope* ascope) {
AnimatorScope* ascope) const {
return this->attachAssetRef(jlayer, ascope, &AnimationBuilder::attachImageAsset);
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachNullLayer(const skjson::ObjectValue& layer,
AnimatorScope*) {
AnimatorScope*) const {
// Null layers are used solely to drive dependent transforms,
// but we use free-floating sksg::Matrices for that purpose.
return nullptr;
@ -326,7 +326,8 @@ struct AnimationBuilder::AttachLayerContext {
SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
sk_sp<sksg::RenderNode> fCurrentMatte;
sk_sp<sksg::Matrix> AttachLayerMatrix(const skjson::ObjectValue& jlayer) {
sk_sp<sksg::Matrix> AttachLayerMatrix(const skjson::ObjectValue& jlayer,
const AnimationBuilder* abuilder) {
const auto layer_index = ParseDefault<int>(jlayer["ind"], -1);
if (layer_index < 0)
return nullptr;
@ -334,11 +335,12 @@ struct AnimationBuilder::AttachLayerContext {
if (auto* m = fLayerMatrixMap.find(layer_index))
return *m;
return this->AttachLayerMatrixImpl(jlayer, layer_index);
return this->AttachLayerMatrixImpl(jlayer, abuilder, layer_index);
}
private:
sk_sp<sksg::Matrix> AttachParentLayerMatrix(const skjson::ObjectValue& jlayer,
const AnimationBuilder* abuilder,
int layer_index) {
const auto parent_index = ParseDefault<int>(jlayer["parent"], -1);
if (parent_index < 0 || parent_index == layer_index)
@ -351,24 +353,27 @@ private:
if (!l) continue;
if (ParseDefault<int>((*l)["ind"], -1) == parent_index) {
return this->AttachLayerMatrixImpl(*l, parent_index);
return this->AttachLayerMatrixImpl(*l, abuilder, parent_index);
}
}
return nullptr;
}
sk_sp<sksg::Matrix> AttachLayerMatrixImpl(const skjson::ObjectValue& jlayer, int layer_index) {
sk_sp<sksg::Matrix> AttachLayerMatrixImpl(const skjson::ObjectValue& jlayer,
const AnimationBuilder* abuilder,
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);
auto parent_matrix = this->AttachParentLayerMatrix(jlayer, abuilder, layer_index);
if (const skjson::ObjectValue* jtransform = jlayer["ks"]) {
return *fLayerMatrixMap.set(layer_index, AttachMatrix(*jtransform, fScope,
std::move(parent_matrix)));
return *fLayerMatrixMap.set(layer_index,
abuilder->attachMatrix(*jtransform, fScope,
std::move(parent_matrix)));
}
return nullptr;
@ -376,11 +381,11 @@ private:
};
sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue* jlayer,
AttachLayerContext* layerCtx) {
AttachLayerContext* layerCtx) const {
if (!jlayer) return nullptr;
using LayerAttacher = sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
AnimatorScope*);
AnimatorScope*) const;
static constexpr LayerAttacher gLayerAttachers[] = {
&AnimationBuilder::attachPrecompLayer, // 'ty': 0
&AnimationBuilder::attachSolidLayer, // 'ty': 1
@ -409,17 +414,17 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
}
// Optional layer mask.
layer = AttachMask((*jlayer)["masksProperties"], &layer_animators, std::move(layer));
layer = AttachMask((*jlayer)["masksProperties"], this, &layer_animators, std::move(layer));
// Optional layer transform.
if (auto layerMatrix = layerCtx->AttachLayerMatrix(*jlayer)) {
if (auto layerMatrix = layerCtx->AttachLayerMatrix(*jlayer, this)) {
layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix));
}
// Optional layer opacity.
// TODO: de-dupe this "ks" lookup with matrix above.
if (const skjson::ObjectValue* jtransform = (*jlayer)["ks"]) {
layer = AttachOpacity(*jtransform, &layer_animators, std::move(layer));
layer = this->attachOpacity(*jtransform, &layer_animators, std::move(layer));
}
// Optional layer effects.
@ -492,7 +497,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(const skjson::ObjectValue& comp,
AnimatorScope* scope) {
AnimatorScope* scope) const {
const skjson::ArrayValue* jlayers = comp["layers"];
if (!jlayers) return nullptr;

View File

@ -18,6 +18,7 @@ namespace internal {
namespace {
sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue* jeffect_props,
const AnimationBuilder* abuilder,
AnimatorScope* ascope,
sk_sp<sksg::RenderNode> layer) {
if (!jeffect_props) return layer;
@ -29,7 +30,7 @@ sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue* jeffect_
switch (const auto ty = ParseDefault<int>((*jprop)["ty"], -1)) {
case 2: // color
color_node = AttachColor(*jprop, ascope, "v");
color_node = abuilder->attachColor(*jprop, ascope, "v");
break;
default:
LOG("?? Ignoring unsupported fill effect poperty type: %d\n", ty);
@ -46,13 +47,13 @@ sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue* jeffect_
sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::ArrayValue& jeffects,
AnimatorScope* ascope,
sk_sp<sksg::RenderNode> layer) {
sk_sp<sksg::RenderNode> layer) const {
for (const skjson::ObjectValue* jeffect : jeffects) {
if (!jeffect) continue;
switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) {
case 21: // Fill
layer = AttachFillLayerEffect((*jeffect)["ef"], ascope, std::move(layer));
layer = AttachFillLayerEffect((*jeffect)["ef"], this, ascope, std::move(layer));
break;
default:
LOG("?? Unsupported layer effect type: %d\n", ty);

View File

@ -8,7 +8,6 @@
#include "SkottiePriv.h"
#include "SkJSON.h"
#include "SkottieAnimator.h"
#include "SkottieJson.h"
#include "SkottieValue.h"
#include "SkMakeUnique.h"
@ -20,7 +19,7 @@ namespace skottie {
namespace internal {
sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer,
AnimatorScope* ascope) {
AnimatorScope* ascope) const {
const skjson::ObjectValue* time_remap = jlayer["tm"];
const auto start_time = ParseDefault<float>(jlayer["st"], 0.0f),
stretch_time = ParseDefault<float>(jlayer["sr"], 1.0f);
@ -70,7 +69,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::Objec
// because both the lambda and the mapper are scoped/owned by ctx->fAnimators.
auto* raw_mapper = time_mapper.get();
auto frame_rate = fFrameRate;
BindProperty<ScalarValue>(*time_remap, ascope,
this->bindProperty<ScalarValue>(*time_remap, ascope,
[raw_mapper, frame_rate](const ScalarValue& t) {
raw_mapper->remapTime(t * frame_rate);
});

View File

@ -49,6 +49,30 @@ public:
std::unique_ptr<sksg::Scene> parse(const skjson::ObjectValue&);
// 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 skjson::Value&,
AnimatorScope*,
std::function<void(const T&)>&&,
const T* default_igore = nullptr) const;
template <typename T>
bool bindProperty(const skjson::Value& jv,
AnimatorScope* ascope,
std::function<void(const T&)>&& apply,
const T& default_ignore) const {
return this->bindProperty(jv, ascope, std::move(apply), &default_ignore);
}
sk_sp<sksg::Color> attachColor(const skjson::ObjectValue&, AnimatorScope*,
const char prop_name[]) const;
sk_sp<sksg::Matrix> attachMatrix(const skjson::ObjectValue&, AnimatorScope*,
sk_sp<sksg::Matrix>) const;
sk_sp<sksg::RenderNode> attachOpacity(const skjson::ObjectValue&, AnimatorScope*,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::Path> attachPath(const skjson::Value&, AnimatorScope*) const;
private:
struct AttachLayerContext;
@ -56,24 +80,24 @@ private:
void parseFonts (const skjson::ObjectValue* jfonts,
const skjson::ArrayValue* jchars);
sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*);
sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*) const;
sk_sp<sksg::RenderNode> attachLayerEffects(const skjson::ArrayValue& jeffects, AnimatorScope*,
sk_sp<sksg::RenderNode>);
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachAssetRef(const skjson::ObjectValue&, AnimatorScope*,
sk_sp<sksg::RenderNode>(AnimationBuilder::*)(const skjson::ObjectValue&,
AnimatorScope* ctx));
sk_sp<sksg::RenderNode> attachImageAsset(const skjson::ObjectValue&, AnimatorScope*);
AnimatorScope* ctx) const) const;
sk_sp<sksg::RenderNode> attachImageAsset(const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachNestedAnimation(const char* name, AnimatorScope* ascope);
sk_sp<sksg::RenderNode> attachNestedAnimation(const char* name, AnimatorScope* ascope) const;
sk_sp<sksg::RenderNode> attachImageLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachImageLayer (const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<ResourceProvider> fResourceProvider;
sk_sp<SkFontMgr> fFontMgr;
@ -100,20 +124,13 @@ private:
using AssetCache = SkTHashMap<SkString, sk_sp<sksg::RenderNode>>;
using FontMap = SkTHashMap<SkString, FontInfo>;
AssetMap fAssets;
AssetCache fAssetCache;
FontMap fFonts;
AssetMap fAssets;
FontMap fFonts;
mutable AssetCache fAssetCache;
using INHERITED = SkNoncopyable;
};
// Shared helpers
sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue&, AnimatorScope*, const char prop_name[]);
sk_sp<sksg::Path> AttachPath(const skjson::Value&, AnimatorScope*);
sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue&, AnimatorScope*, sk_sp<sksg::Matrix>);
sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue&, AnimatorScope*,
sk_sp<sksg::RenderNode>);
} // namespace internal
} // namespace skottie

View File

@ -9,7 +9,6 @@
#include "SkJSON.h"
#include "SkottieAdapter.h"
#include "SkottieAnimator.h"
#include "SkottieJson.h"
#include "SkottieValue.h"
#include "SkPath.h"
@ -31,24 +30,26 @@ namespace internal {
namespace {
sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
return AttachPath(jpath["ks"], ascope);
return abuilder->attachPath(jpath["ks"], ascope);
}
sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
auto rect_node = sksg::RRect::Make();
auto adapter = sk_make_sp<RRectAdapter>(rect_node);
auto p_attached = BindProperty<VectorValue>(jrect["p"], ascope,
auto p_attached = abuilder->bindProperty<VectorValue>(jrect["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
auto s_attached = BindProperty<VectorValue>(jrect["s"], ascope,
auto s_attached = abuilder->bindProperty<VectorValue>(jrect["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
});
auto r_attached = BindProperty<ScalarValue>(jrect["r"], ascope,
auto r_attached = abuilder->bindProperty<ScalarValue>(jrect["r"], ascope,
[adapter](const ScalarValue& r) {
adapter->setRadius(SkSize::Make(r, r));
});
@ -61,15 +62,16 @@ sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
}
sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
auto rect_node = sksg::RRect::Make();
auto adapter = sk_make_sp<RRectAdapter>(rect_node);
auto p_attached = BindProperty<VectorValue>(jellipse["p"], ascope,
auto p_attached = abuilder->bindProperty<VectorValue>(jellipse["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
auto s_attached = BindProperty<VectorValue>(jellipse["s"], ascope,
auto s_attached = abuilder->bindProperty<VectorValue>(jellipse["s"], ascope,
[adapter](const VectorValue& s) {
const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
adapter->setSize(sz);
@ -84,6 +86,7 @@ sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jelli
}
sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
static constexpr PolyStarAdapter::Type gTypes[] = {
PolyStarAdapter::Type::kStar, // "sy": 1
@ -99,31 +102,31 @@ sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jsta
auto path_node = sksg::Path::Make();
auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
BindProperty<VectorValue>(jstar["p"], ascope,
abuilder->bindProperty<VectorValue>(jstar["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
BindProperty<ScalarValue>(jstar["pt"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["pt"], ascope,
[adapter](const ScalarValue& pt) {
adapter->setPointCount(pt);
});
BindProperty<ScalarValue>(jstar["ir"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["ir"], ascope,
[adapter](const ScalarValue& ir) {
adapter->setInnerRadius(ir);
});
BindProperty<ScalarValue>(jstar["or"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["or"], ascope,
[adapter](const ScalarValue& otr) {
adapter->setOuterRadius(otr);
});
BindProperty<ScalarValue>(jstar["is"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["is"], ascope,
[adapter](const ScalarValue& is) {
adapter->setInnerRoundness(is);
});
BindProperty<ScalarValue>(jstar["os"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["os"], ascope,
[adapter](const ScalarValue& os) {
adapter->setOuterRoundness(os);
});
BindProperty<ScalarValue>(jstar["r"], ascope,
abuilder->bindProperty<ScalarValue>(jstar["r"], ascope,
[adapter](const ScalarValue& r) {
adapter->setRotation(r);
});
@ -131,7 +134,8 @@ sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jsta
return std::move(path_node);
}
sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AnimatorScope* ascope) {
sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad,
const AnimationBuilder* abuilder, AnimatorScope* ascope) {
const skjson::ObjectValue* stops = jgrad["g"];
if (!stops)
return nullptr;
@ -155,15 +159,15 @@ sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AnimatorS
gradient_node = std::move(radial_node);
}
BindProperty<VectorValue>((*stops)["k"], ascope,
abuilder->bindProperty<VectorValue>((*stops)["k"], ascope,
[adapter](const VectorValue& stops) {
adapter->setColorStops(stops);
});
BindProperty<VectorValue>(jgrad["s"], ascope,
abuilder->bindProperty<VectorValue>(jgrad["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
});
BindProperty<VectorValue>(jgrad["e"], ascope,
abuilder->bindProperty<VectorValue>(jgrad["e"], ascope,
[adapter](const VectorValue& e) {
adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
});
@ -171,12 +175,13 @@ sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AnimatorS
return gradient_node;
}
sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint,
const AnimationBuilder* abuilder, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> paint_node) {
if (paint_node) {
paint_node->setAntiAlias(true);
BindProperty<ScalarValue>(jpaint["o"], ascope,
abuilder->bindProperty<ScalarValue>(jpaint["o"], ascope,
[paint_node](const ScalarValue& o) {
// BM opacity is [0..100]
paint_node->setOpacity(o * 0.01f);
@ -186,14 +191,15 @@ sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint, AnimatorSc
return paint_node;
}
sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke,
const AnimationBuilder* abuilder, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> stroke_node) {
if (!stroke_node)
return nullptr;
stroke_node->setStyle(SkPaint::kStroke_Style);
BindProperty<ScalarValue>(jstroke["w"], ascope,
abuilder->bindProperty<ScalarValue>(jstroke["w"], ascope,
[stroke_node](const ScalarValue& w) {
stroke_node->setStrokeWidth(w);
});
@ -219,24 +225,30 @@ sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke, Animator
return stroke_node;
}
sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill, AnimatorScope* ascope) {
return AttachPaint(jfill, ascope, AttachColor(jfill, ascope, "c"));
sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill,
const AnimationBuilder* abuilder, AnimatorScope* ascope) {
return AttachPaint(jfill, abuilder, ascope, abuilder->attachColor(jfill, ascope, "c"));
}
sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill, AnimatorScope* ascope) {
return AttachPaint(jfill, ascope, AttachGradient(jfill, ascope));
sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill,
const AnimationBuilder* abuilder, AnimatorScope* ascope) {
return AttachPaint(jfill, abuilder, ascope, AttachGradient(jfill, abuilder, ascope));
}
sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
return AttachStroke(jstroke, ascope, AttachPaint(jstroke, ascope,
AttachColor(jstroke, ascope, "c")));
return AttachStroke(jstroke, abuilder, ascope,
AttachPaint(jstroke, abuilder, ascope,
abuilder->attachColor(jstroke, ascope, "c")));
}
sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
const AnimationBuilder* abuilder,
AnimatorScope* ascope) {
return AttachStroke(jstroke, ascope, AttachPaint(jstroke, ascope,
AttachGradient(jstroke, ascope)));
return AttachStroke(jstroke, abuilder, ascope,
AttachPaint(jstroke, abuilder, ascope,
AttachGradient(jstroke, abuilder, ascope)));
}
sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
@ -252,7 +264,7 @@ sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Me
}
std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
const skjson::ObjectValue& jmerge, AnimatorScope*,
const skjson::ObjectValue& jmerge, const AnimationBuilder*, AnimatorScope*,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
static constexpr sksg::Merge::Mode gModes[] = {
sksg::Merge::Mode::kMerge, // "mm": 1
@ -272,7 +284,7 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
}
std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
const skjson::ObjectValue& jtrim, AnimatorScope* ascope,
const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
enum class Mode {
@ -297,15 +309,15 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
trimmed.push_back(trimEffect);
const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
BindProperty<ScalarValue>(jtrim["s"], ascope,
abuilder->bindProperty<ScalarValue>(jtrim["s"], ascope,
[adapter](const ScalarValue& s) {
adapter->setStart(s);
});
BindProperty<ScalarValue>(jtrim["e"], ascope,
abuilder->bindProperty<ScalarValue>(jtrim["e"], ascope,
[adapter](const ScalarValue& e) {
adapter->setEnd(e);
});
BindProperty<ScalarValue>(jtrim["o"], ascope,
abuilder->bindProperty<ScalarValue>(jtrim["o"], ascope,
[adapter](const ScalarValue& o) {
adapter->setOffset(o);
});
@ -315,7 +327,7 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
}
std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
const skjson::ObjectValue& jtrim, AnimatorScope* ascope,
const skjson::ObjectValue& jtrim, const AnimationBuilder* abuilder, AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
std::vector<sk_sp<sksg::GeometryNode>> rounded;
@ -325,7 +337,7 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
rounded.push_back(roundEffect);
BindProperty<ScalarValue>(jtrim["r"], ascope,
abuilder->bindProperty<ScalarValue>(jtrim["r"], ascope,
[roundEffect](const ScalarValue& r) {
roundEffect->setRadius(r);
});
@ -334,7 +346,8 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
return rounded;
}
using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&, AnimatorScope*);
using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&,
const AnimationBuilder*, AnimatorScope*);
static constexpr GeometryAttacherT gGeometryAttachers[] = {
AttachPathGeometry,
AttachRRectGeometry,
@ -342,7 +355,8 @@ static constexpr GeometryAttacherT gGeometryAttachers[] = {
AttachPolystarGeometry,
};
using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&, AnimatorScope*);
using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&,
const AnimationBuilder*, AnimatorScope*);
static constexpr PaintAttacherT gPaintAttachers[] = {
AttachColorFill,
AttachColorStroke,
@ -352,7 +366,7 @@ static constexpr PaintAttacherT gPaintAttachers[] = {
using GeometryEffectAttacherT =
std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
AnimatorScope*,
const AnimationBuilder*, AnimatorScope*,
std::vector<sk_sp<sksg::GeometryNode>>&&);
static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
AttachMergeGeometryEffect,
@ -414,27 +428,29 @@ struct GeometryEffectRec {
};
struct AttachShapeContext {
AttachShapeContext(AnimatorScope* ascope,
AttachShapeContext(const AnimationBuilder* abuilder,
AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>* geos,
std::vector<GeometryEffectRec>* effects,
size_t committedAnimators)
: fScope(ascope)
: fBuilder(abuilder)
, fScope(ascope)
, fGeometryStack(geos)
, fGeometryEffectStack(effects)
, fCommittedAnimators(committedAnimators) {}
const AnimationBuilder* fBuilder;
AnimatorScope* fScope;
std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
std::vector<GeometryEffectRec>* fGeometryEffectStack;
size_t fCommittedAnimators;
};
sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
AttachShapeContext* shapeCtx) {
sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape, AttachShapeContext* ctx) {
if (!jshape)
return nullptr;
SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();)
SkDEBUGCODE(const auto initialGeometryEffects = ctx->fGeometryEffectStack->size();)
sk_sp<sksg::Group> shape_group = sksg::Group::Make();
sk_sp<sksg::RenderNode> shape_wrapper = shape_group;
@ -466,14 +482,15 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
switch (info->fShapeType) {
case ShapeType::kTransform:
if ((shape_matrix = AttachMatrix(*shape, shapeCtx->fScope, nullptr))) {
if ((shape_matrix = ctx->fBuilder->attachMatrix(*shape, ctx->fScope, nullptr))) {
shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
}
shape_wrapper = AttachOpacity(*shape, shapeCtx->fScope, std::move(shape_wrapper));
shape_wrapper = ctx->fBuilder->attachOpacity(*shape, ctx->fScope,
std::move(shape_wrapper));
break;
case ShapeType::kGeometryEffect:
SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
shapeCtx->fGeometryEffectStack->push_back(
ctx->fGeometryEffectStack->push_back(
{ *shape, gGeometryEffectAttachers[info->fAttacherIndex] });
break;
default:
@ -493,7 +510,8 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
case ShapeType::kGeometry: {
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
shapeCtx->fScope)) {
ctx->fBuilder,
ctx->fScope)) {
geos.push_back(std::move(geo));
}
} break;
@ -502,38 +520,42 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
if (!geos.empty()) {
geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
shapeCtx->fScope,
ctx->fBuilder,
ctx->fScope,
std::move(geos));
}
SkASSERT(&shapeCtx->fGeometryEffectStack->back().fJson == &rec->fJson);
SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach ==
SkASSERT(&ctx->fGeometryEffectStack->back().fJson == &rec->fJson);
SkASSERT(ctx->fGeometryEffectStack->back().fAttach ==
gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]);
shapeCtx->fGeometryEffectStack->pop_back();
ctx->fGeometryEffectStack->pop_back();
} break;
case ShapeType::kGroup: {
AttachShapeContext groupShapeCtx(shapeCtx->fScope,
AttachShapeContext groupShapeCtx(ctx->fBuilder,
ctx->fScope,
&geos,
shapeCtx->fGeometryEffectStack,
shapeCtx->fCommittedAnimators);
ctx->fGeometryEffectStack,
ctx->fCommittedAnimators);
if (auto subgroup = AttachShape(rec->fJson["it"], &groupShapeCtx)) {
draws.push_back(std::move(subgroup));
SkASSERT(groupShapeCtx.fCommittedAnimators >= shapeCtx->fCommittedAnimators);
shapeCtx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
SkASSERT(groupShapeCtx.fCommittedAnimators >= ctx->fCommittedAnimators);
ctx->fCommittedAnimators = groupShapeCtx.fCommittedAnimators;
}
} break;
case ShapeType::kPaint: {
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fScope);
auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
ctx->fBuilder,
ctx->fScope);
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->fScope, std::move(drawGeos));
for (auto it = ctx->fGeometryEffectStack->rbegin();
it != ctx->fGeometryEffectStack->rend(); ++it) {
drawGeos = it->fAttach(it->fJson, ctx->fBuilder, ctx->fScope, std::move(drawGeos));
}
// If we still have multiple geos, reduce using 'merge'.
@ -543,7 +565,7 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
SkASSERT(geo);
draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
shapeCtx->fCommittedAnimators = shapeCtx->fScope->size();
ctx->fCommittedAnimators = ctx->fScope->size();
} break;
default:
break;
@ -551,11 +573,11 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
}
// By now we should have popped all local geometry effects.
SkASSERT(shapeCtx->fGeometryEffectStack->size() == initialGeometryEffects);
SkASSERT(ctx->fGeometryEffectStack->size() == initialGeometryEffects);
// Push transformed local geometries to parent list, for subsequent paints.
for (const auto& geo : geos) {
shapeCtx->fGeometryStack->push_back(shape_matrix
ctx->fGeometryStack->push_back(shape_matrix
? sksg::GeometryTransform::Make(std::move(geo), shape_matrix)
: std::move(geo));
}
@ -571,10 +593,10 @@ sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
} // namespace
sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
AnimatorScope* ascope) {
AnimatorScope* ascope) const {
std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
std::vector<GeometryEffectRec> geometryEffectStack;
AttachShapeContext shapeCtx(ascope, &geometryStack, &geometryEffectStack, ascope->size());
AttachShapeContext shapeCtx(this, ascope, &geometryStack, &geometryEffectStack, ascope->size());
auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
// Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches

View File

@ -206,7 +206,7 @@ void AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts,
}
sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& layer,
AnimatorScope* ascope) {
AnimatorScope* ascope) const {
// General text node format:
// "t": {
// "a": [], // animators (TODO)

View File

@ -8,12 +8,20 @@
#include "SkottieValue.h"
#include "SkColor.h"
#include "SkottieJson.h"
#include "SkottiePriv.h"
#include "SkNx.h"
#include "SkPoint.h"
#include "SkSize.h"
namespace skottie {
template <>
bool ValueTraits<ScalarValue>::FromJSON(const skjson::Value& jv, const internal::AnimationBuilder*,
ScalarValue* v) {
return Parse(jv, v);
}
template <>
bool ValueTraits<ScalarValue>::CanLerp(const ScalarValue&, const ScalarValue&) {
return true;
@ -32,6 +40,12 @@ SkScalar ValueTraits<ScalarValue>::As<SkScalar>(const ScalarValue& v) {
return v;
}
template <>
bool ValueTraits<VectorValue>::FromJSON(const skjson::Value& jv, const internal::AnimationBuilder*,
VectorValue* v) {
return Parse(jv, v);
}
template <>
bool ValueTraits<VectorValue>::CanLerp(const VectorValue& v1, const VectorValue& v2) {
return v1.size() == v2.size();
@ -80,6 +94,67 @@ SkSize ValueTraits<VectorValue>::As<SkSize>(const VectorValue& vec) {
return SkSize::Make(pt.x(), pt.y());
}
namespace {
bool ParsePointVec(const skjson::Value& jv, std::vector<SkPoint>* pts) {
if (!jv.is<skjson::ArrayValue>())
return false;
const auto& av = jv.as<skjson::ArrayValue>();
pts->clear();
pts->reserve(av.size());
std::vector<float> vec;
for (size_t i = 0; i < av.size(); ++i) {
if (!Parse(av[i], &vec) || vec.size() != 2)
return false;
pts->push_back(SkPoint::Make(vec[0], vec[1]));
}
return true;
}
} // namespace
template <>
bool ValueTraits<ShapeValue>::FromJSON(const skjson::Value& jv,
const internal::AnimationBuilder* abuilder,
ShapeValue* v) {
SkASSERT(v->fVertices.empty());
// Some versions wrap values as single-element arrays.
if (const skjson::ArrayValue* av = jv) {
if (av->size() == 1) {
return FromJSON((*av)[0], abuilder, v);
}
}
if (!jv.is<skjson::ObjectValue>())
return false;
const auto& ov = jv.as<skjson::ObjectValue>();
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 (!ParsePointVec(ov["i"], &inPts) ||
!ParsePointVec(ov["o"], &outPts) ||
!ParsePointVec(ov["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 = ParseDefault<bool>(ov["c"], false);
return true;
}
template <>
bool ValueTraits<ShapeValue>::CanLerp(const ShapeValue& v1, const ShapeValue& v2) {
return v1.fVertices.size() == v2.fVertices.size()

View File

@ -13,10 +13,17 @@
#include <vector>
namespace skottie {
namespace skjson { class Value; }
namespace skottie {
namespace internal {
class AnimationBuilder;
} // namespace internal
template <typename T>
struct ValueTraits {
static bool FromJSON(const skjson::Value&, const internal::AnimationBuilder*, T*);
template <typename U>
static U As(const T&);