From 6bbd7df9fb83dd8fc05b73ba2f6c525825d90795 Mon Sep 17 00:00:00 2001 From: Kyle Carlstrom Date: Mon, 26 Jul 2021 10:12:51 -0700 Subject: [PATCH] Generalize KeyframeAnimatorBuilder to AnimatorBuilder Change-Id: I116489a1261a93d9b2513ca53f04e1305e6fd85b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/432957 Reviewed-by: Florin Malita Reviewed-by: Avinash Parchuri Commit-Queue: Avinash Parchuri --- modules/skottie/src/animator/Animator.cpp | 4 +- modules/skottie/src/animator/Animator.h | 4 +- .../skottie/src/animator/KeyframeAnimator.cpp | 6 +- .../skottie/src/animator/KeyframeAnimator.h | 6 +- .../src/animator/ScalarKeyframeAnimator.cpp | 55 ++--- .../src/animator/ShapeKeyframeAnimator.cpp | 2 +- .../src/animator/TextKeyframeAnimator.cpp | 103 ++++---- .../src/animator/Vec2KeyframeAnimator.cpp | 220 +++++++++--------- .../src/animator/VectorKeyframeAnimator.cpp | 10 +- .../src/animator/VectorKeyframeAnimator.h | 6 +- 10 files changed, 208 insertions(+), 208 deletions(-) diff --git a/modules/skottie/src/animator/Animator.cpp b/modules/skottie/src/animator/Animator.cpp index 3e891476f8..dc6ff08707 100644 --- a/modules/skottie/src/animator/Animator.cpp +++ b/modules/skottie/src/animator/Animator.cpp @@ -49,7 +49,7 @@ void AnimatablePropertyContainer::shrink_to_fit() { bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder, const skjson::ObjectValue* jprop, - KeyframeAnimatorBuilder& builder) { + AnimatorBuilder& builder) { if (!jprop) { return false; } @@ -80,7 +80,7 @@ bool AnimatablePropertyContainer::bindImpl(const AnimationBuilder& abuilder, sk_sp animator; const skjson::ArrayValue* jkfs = jpropK; if (jkfs && jkfs->size() > 0) { - animator = builder.make(abuilder, *jkfs); + animator = builder.makeFromKeyframes(abuilder, *jkfs); } if (!animator) { diff --git a/modules/skottie/src/animator/Animator.h b/modules/skottie/src/animator/Animator.h index 0ad4d2a465..f145cc05c1 100644 --- a/modules/skottie/src/animator/Animator.h +++ b/modules/skottie/src/animator/Animator.h @@ -24,7 +24,7 @@ namespace skottie { namespace internal { class AnimationBuilder; -class KeyframeAnimatorBuilder; +class AnimatorBuilder; class Animator : public SkRefCnt { public: @@ -72,7 +72,7 @@ protected: private: StateChanged onSeek(float) final; - bool bindImpl(const AnimationBuilder&, const skjson::ObjectValue*, KeyframeAnimatorBuilder&); + bool bindImpl(const AnimationBuilder&, const skjson::ObjectValue*, AnimatorBuilder&); std::vector> fAnimators; bool fHasSynced = false; diff --git a/modules/skottie/src/animator/KeyframeAnimator.cpp b/modules/skottie/src/animator/KeyframeAnimator.cpp index 8672c60368..a85b80574b 100644 --- a/modules/skottie/src/animator/KeyframeAnimator.cpp +++ b/modules/skottie/src/animator/KeyframeAnimator.cpp @@ -86,9 +86,9 @@ float KeyframeAnimator::compute_weight(const KFSegment &seg, float t) const { return w; } -KeyframeAnimatorBuilder::~KeyframeAnimatorBuilder() = default; +AnimatorBuilder::~AnimatorBuilder() = default; -bool KeyframeAnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder, +bool AnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder, const skjson::ArrayValue& jkfs) { // Keyframe format: // @@ -196,7 +196,7 @@ bool KeyframeAnimatorBuilder::parseKeyframes(const AnimationBuilder& abuilder, return true; } -uint32_t KeyframeAnimatorBuilder::parseMapping(const skjson::ObjectValue& jkf) { +uint32_t AnimatorBuilder::parseMapping(const skjson::ObjectValue& jkf) { if (ParseDefault(jkf["h"], false)) { return Keyframe::kConstantMapping; } diff --git a/modules/skottie/src/animator/KeyframeAnimator.h b/modules/skottie/src/animator/KeyframeAnimator.h index cbadabfc68..6196f7c1d0 100644 --- a/modules/skottie/src/animator/KeyframeAnimator.h +++ b/modules/skottie/src/animator/KeyframeAnimator.h @@ -104,11 +104,11 @@ private: mutable KFSegment fCurrentSegment = { nullptr, nullptr }; // Cached segment. }; -class KeyframeAnimatorBuilder : public SkNoncopyable { +class AnimatorBuilder : public SkNoncopyable { public: - virtual ~KeyframeAnimatorBuilder(); + virtual ~AnimatorBuilder(); - virtual sk_sp make(const AnimationBuilder&, const skjson::ArrayValue&) = 0; + virtual sk_sp makeFromKeyframes(const AnimationBuilder&, const skjson::ArrayValue&) = 0; virtual bool parseValue(const AnimationBuilder&, const skjson::Value&) const = 0; diff --git a/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp b/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp index d0babc5077..5e4dbe7070 100644 --- a/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp +++ b/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp @@ -14,14 +14,36 @@ namespace skottie::internal { namespace { -// Scalar specialization: stores scalar values (floats) inline in keyframes. + // Scalar specialization: stores scalar values (floats) inline in keyframes. class ScalarKeyframeAnimator final : public KeyframeAnimator { public: - class Builder final : public KeyframeAnimatorBuilder { - public: - explicit Builder(ScalarValue* target) : fTarget(target) {} + ScalarKeyframeAnimator(std::vector kfs, + std::vector cms, + ScalarValue* target_value) + : INHERITED(std::move(kfs), std::move(cms)) + , fTarget(target_value) {} - sk_sp make(const AnimationBuilder& abuilder, +private: + + StateChanged onSeek(float t) override { + const auto& lerp_info = this->getLERPInfo(t); + const auto old_value = *fTarget; + + *fTarget = Lerp(lerp_info.vrec0.flt, lerp_info.vrec1.flt, lerp_info.weight); + + return *fTarget != old_value; + } + + ScalarValue* fTarget; + + using INHERITED = KeyframeAnimator; +}; + +class ScalarAnimatorBuilder final : public AnimatorBuilder { + public: + explicit ScalarAnimatorBuilder(ScalarValue* target) : fTarget(target) {} + + sk_sp makeFromKeyframes(const AnimationBuilder& abuilder, const skjson::ArrayValue& jkfs) override { SkASSERT(jkfs.size() > 0); if (!this->parseKeyframes(abuilder, jkfs)) { @@ -47,34 +69,13 @@ public: ScalarValue* fTarget; }; -private: - ScalarKeyframeAnimator(std::vector kfs, - std::vector cms, - ScalarValue* target_value) - : INHERITED(std::move(kfs), std::move(cms)) - , fTarget(target_value) {} - - StateChanged onSeek(float t) override { - const auto& lerp_info = this->getLERPInfo(t); - const auto old_value = *fTarget; - - *fTarget = Lerp(lerp_info.vrec0.flt, lerp_info.vrec1.flt, lerp_info.weight); - - return *fTarget != old_value; - } - - ScalarValue* fTarget; - - using INHERITED = KeyframeAnimator; -}; - } // namespace template <> bool AnimatablePropertyContainer::bind(const AnimationBuilder& abuilder, const skjson::ObjectValue* jprop, ScalarValue* v) { - ScalarKeyframeAnimator::Builder builder(v); + ScalarAnimatorBuilder builder(v); return this->bindImpl(abuilder, jprop, builder); } diff --git a/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp b/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp index ea6901d42d..79d59fc406 100644 --- a/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp +++ b/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp @@ -168,7 +168,7 @@ template <> bool AnimatablePropertyContainer::bind(const AnimationBuilder& abuilder, const skjson::ObjectValue* jprop, ShapeValue* v) { - VectorKeyframeAnimatorBuilder builder(v, parse_encoding_len, parse_encoding_data); + VectorAnimatorBuilder builder(v, parse_encoding_len, parse_encoding_data); return this->bindImpl(abuilder, jprop, builder); } diff --git a/modules/skottie/src/animator/TextKeyframeAnimator.cpp b/modules/skottie/src/animator/TextKeyframeAnimator.cpp index 8fb1ad1fe8..6c031dadc9 100644 --- a/modules/skottie/src/animator/TextKeyframeAnimator.cpp +++ b/modules/skottie/src/animator/TextKeyframeAnimator.cpp @@ -13,65 +13,15 @@ namespace skottie::internal { namespace { - class TextKeyframeAnimator final : public KeyframeAnimator { public: - class Builder final : public KeyframeAnimatorBuilder { - public: - explicit Builder(TextValue* target) : fTarget(target) {} - - sk_sp make(const AnimationBuilder& abuilder, - const skjson::ArrayValue& jkfs) override { - SkASSERT(jkfs.size() > 0); - - fValues.reserve(jkfs.size()); - if (!this->parseKeyframes(abuilder, jkfs)) { - return nullptr; - } - fValues.shrink_to_fit(); - - return sk_sp( - new TextKeyframeAnimator(std::move(fKFs), - std::move(fCMs), - std::move(fValues), - fTarget)); - } - - bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override { - return Parse(jv, abuilder, fTarget); - } - - private: - bool parseKFValue(const AnimationBuilder& abuilder, - const skjson::ObjectValue&, - const skjson::Value& jv, - Keyframe::Value* v) override { - TextValue val; - if (!Parse(jv, abuilder, &val)) { - return false; - } - - // TODO: full deduping? - if (fValues.empty() || val != fValues.back()) { - fValues.push_back(std::move(val)); - } - - v->idx = SkToU32(fValues.size() - 1); - - return true; - } - - std::vector fValues; - TextValue* fTarget; - }; - -private: TextKeyframeAnimator(std::vector kfs, std::vector cms, std::vector vs, TextValue* target_value) : INHERITED(std::move(kfs), std::move(cms)) , fValues(std::move(vs)) , fTarget(target_value) {} +private: StateChanged onSeek(float t) override { const auto& lerp_info = this->getLERPInfo(t); @@ -90,13 +40,62 @@ private: using INHERITED = KeyframeAnimator; }; +class TextAnimatorBuilder final : public AnimatorBuilder { +public: + explicit TextAnimatorBuilder(TextValue* target) : fTarget(target) {} + + sk_sp makeFromKeyframes(const AnimationBuilder& abuilder, + const skjson::ArrayValue& jkfs) override { + SkASSERT(jkfs.size() > 0); + + fValues.reserve(jkfs.size()); + if (!this->parseKeyframes(abuilder, jkfs)) { + return nullptr; + } + fValues.shrink_to_fit(); + + return sk_sp( + new TextKeyframeAnimator(std::move(fKFs), + std::move(fCMs), + std::move(fValues), + fTarget)); + } + + bool parseValue(const AnimationBuilder& abuilder, const skjson::Value& jv) const override { + return Parse(jv, abuilder, fTarget); + } + +private: + bool parseKFValue(const AnimationBuilder& abuilder, + const skjson::ObjectValue&, + const skjson::Value& jv, + Keyframe::Value* v) override { + TextValue val; + if (!Parse(jv, abuilder, &val)) { + return false; + } + + // TODO: full deduping? + if (fValues.empty() || val != fValues.back()) { + fValues.push_back(std::move(val)); + } + + v->idx = SkToU32(fValues.size() - 1); + + return true; + } + + std::vector fValues; + TextValue* fTarget; +}; + } // namespace template <> bool AnimatablePropertyContainer::bind(const AnimationBuilder& abuilder, const skjson::ObjectValue* jprop, TextValue* v) { - TextKeyframeAnimator::Builder builder(v); + TextAnimatorBuilder builder(v); return this->bindImpl(abuilder, jprop, builder); } diff --git a/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp b/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp index 1eee3d580f..f328dda945 100644 --- a/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp +++ b/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp @@ -20,119 +20,12 @@ namespace { // Spatial 2D specialization: stores SkV2s and optional contour interpolators externally. class Vec2KeyframeAnimator final : public KeyframeAnimator { +public: struct SpatialValue { Vec2Value v2; sk_sp cmeasure; }; -public: - class Builder final : public KeyframeAnimatorBuilder { - public: - Builder(Vec2Value* vec_target, float* rot_target) - : fVecTarget(vec_target) - , fRotTarget(rot_target) {} - - sk_sp make(const AnimationBuilder& abuilder, - const skjson::ArrayValue& jkfs) override { - SkASSERT(jkfs.size() > 0); - - fValues.reserve(jkfs.size()); - if (!this->parseKeyframes(abuilder, jkfs)) { - return nullptr; - } - fValues.shrink_to_fit(); - - return sk_sp( - new Vec2KeyframeAnimator(std::move(fKFs), - std::move(fCMs), - std::move(fValues), - fVecTarget, - fRotTarget)); - } - - bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override { - return Parse(jv, fVecTarget); - } - - private: - void backfill_spatial(const SpatialValue& val) { - SkASSERT(!fValues.empty()); - auto& prev_val = fValues.back(); - SkASSERT(!prev_val.cmeasure); - - if (val.v2 == prev_val.v2) { - // spatial interpolation only make sense for noncoincident values - return; - } - - // Check whether v0 and v1 have the same direction AND ||v0||>=||v1|| - auto check_vecs = [](const SkV2& v0, const SkV2& v1) { - const auto v0_len2 = v0.lengthSquared(), - v1_len2 = v1.lengthSquared(); - - // check magnitude - if (v0_len2 < v1_len2) { - return false; - } - - // v0, v1 have the same direction iff dot(v0,v1) = ||v0||*||v1|| - // <=> dot(v0,v1)^2 = ||v0||^2 * ||v1||^2 - const auto dot = v0.dot(v1); - return SkScalarNearlyEqual(dot * dot, v0_len2 * v1_len2); - }; - - if (check_vecs(val.v2 - prev_val.v2, fTo) && - check_vecs(prev_val.v2 - val.v2, fTi)) { - // Both control points lie on the [prev_val..val] segment - // => we can power-reduce the Bezier "curve" to a straight line. - return; - } - - // Finally, this looks like a legitimate spatial keyframe. - SkPathBuilder p; - p.moveTo (prev_val.v2.x , prev_val.v2.y); - p.cubicTo(prev_val.v2.x + fTo.x, prev_val.v2.y + fTo.y, - val.v2.x + fTi.x, val.v2.y + fTi.y, - val.v2.x, val.v2.y); - prev_val.cmeasure = SkContourMeasureIter(p.detach(), false).next(); - } - - bool parseKFValue(const AnimationBuilder&, - const skjson::ObjectValue& jkf, - const skjson::Value& jv, - Keyframe::Value* v) override { - SpatialValue val; - if (!Parse(jv, &val.v2)) { - return false; - } - - if (fPendingSpatial) { - this->backfill_spatial(val); - } - - // Track the last keyframe spatial tangents (checked on next parseValue). - fTi = ParseDefault(jkf["ti"], {0,0}); - fTo = ParseDefault(jkf["to"], {0,0}); - fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0}; - - if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) { - fValues.push_back(std::move(val)); - } - - v->idx = SkToU32(fValues.size() - 1); - - return true; - } - - std::vector fValues; - Vec2Value* fVecTarget; // required - float* fRotTarget; // optional - SkV2 fTi{0,0}, - fTo{0,0}; - bool fPendingSpatial = false; - }; - -private: Vec2KeyframeAnimator(std::vector kfs, std::vector cms, std::vector vs, Vec2Value* vec_target, float* rot_target) : INHERITED(std::move(kfs), std::move(cms)) @@ -140,6 +33,7 @@ private: , fVecTarget(vec_target) , fRotTarget(rot_target) {} +private: StateChanged update(const Vec2Value& new_vec_value, const Vec2Value& new_tan_value) { auto changed = (new_vec_value != *fVecTarget); *fVecTarget = new_vec_value; @@ -200,13 +94,119 @@ private: return this->update(Lerp(v0.v2, v1.v2, lerp_info.weight), tan); } - const std::vector fValues; + const std::vector fValues; Vec2Value* fVecTarget; float* fRotTarget; using INHERITED = KeyframeAnimator; }; +class Vec2AnimatorBuilder final : public AnimatorBuilder { + public: + Vec2AnimatorBuilder(Vec2Value* vec_target, float* rot_target) + : fVecTarget(vec_target) + , fRotTarget(rot_target) {} + + sk_sp makeFromKeyframes(const AnimationBuilder& abuilder, + const skjson::ArrayValue& jkfs) override { + SkASSERT(jkfs.size() > 0); + + fValues.reserve(jkfs.size()); + if (!this->parseKeyframes(abuilder, jkfs)) { + return nullptr; + } + fValues.shrink_to_fit(); + + return sk_sp( + new Vec2KeyframeAnimator(std::move(fKFs), + std::move(fCMs), + std::move(fValues), + fVecTarget, + fRotTarget)); + } + + bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override { + return Parse(jv, fVecTarget); + } + + private: + void backfill_spatial(const Vec2KeyframeAnimator::SpatialValue& val) { + SkASSERT(!fValues.empty()); + auto& prev_val = fValues.back(); + SkASSERT(!prev_val.cmeasure); + + if (val.v2 == prev_val.v2) { + // spatial interpolation only make sense for noncoincident values + return; + } + + // Check whether v0 and v1 have the same direction AND ||v0||>=||v1|| + auto check_vecs = [](const SkV2& v0, const SkV2& v1) { + const auto v0_len2 = v0.lengthSquared(), + v1_len2 = v1.lengthSquared(); + + // check magnitude + if (v0_len2 < v1_len2) { + return false; + } + + // v0, v1 have the same direction iff dot(v0,v1) = ||v0||*||v1|| + // <=> dot(v0,v1)^2 = ||v0||^2 * ||v1||^2 + const auto dot = v0.dot(v1); + return SkScalarNearlyEqual(dot * dot, v0_len2 * v1_len2); + }; + + if (check_vecs(val.v2 - prev_val.v2, fTo) && + check_vecs(prev_val.v2 - val.v2, fTi)) { + // Both control points lie on the [prev_val..val] segment + // => we can power-reduce the Bezier "curve" to a straight line. + return; + } + + // Finally, this looks like a legitimate spatial keyframe. + SkPathBuilder p; + p.moveTo (prev_val.v2.x , prev_val.v2.y); + p.cubicTo(prev_val.v2.x + fTo.x, prev_val.v2.y + fTo.y, + val.v2.x + fTi.x, val.v2.y + fTi.y, + val.v2.x, val.v2.y); + prev_val.cmeasure = SkContourMeasureIter(p.detach(), false).next(); + } + + bool parseKFValue(const AnimationBuilder&, + const skjson::ObjectValue& jkf, + const skjson::Value& jv, + Keyframe::Value* v) override { + Vec2KeyframeAnimator::SpatialValue val; + if (!Parse(jv, &val.v2)) { + return false; + } + + if (fPendingSpatial) { + this->backfill_spatial(val); + } + + // Track the last keyframe spatial tangents (checked on next parseValue). + fTi = ParseDefault(jkf["ti"], {0,0}); + fTo = ParseDefault(jkf["to"], {0,0}); + fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0}; + + if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) { + fValues.push_back(std::move(val)); + } + + v->idx = SkToU32(fValues.size() - 1); + + return true; + } + + std::vector fValues; + Vec2Value* fVecTarget; // required + float* fRotTarget; // optional + SkV2 fTi{0,0}, + fTo{0,0}; + bool fPendingSpatial = false; + }; + } // namespace bool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abuilder, @@ -218,7 +218,7 @@ bool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abu if (!ParseDefault((*jprop)["s"], false)) { // Regular (static or keyframed) 2D value. - Vec2KeyframeAnimator::Builder builder(v, orientation); + Vec2AnimatorBuilder builder(v, orientation); return this->bindImpl(abuilder, jprop, builder); } diff --git a/modules/skottie/src/animator/VectorKeyframeAnimator.cpp b/modules/skottie/src/animator/VectorKeyframeAnimator.cpp index a591c36d60..56f7d79229 100644 --- a/modules/skottie/src/animator/VectorKeyframeAnimator.cpp +++ b/modules/skottie/src/animator/VectorKeyframeAnimator.cpp @@ -142,14 +142,14 @@ private: } // namespace -VectorKeyframeAnimatorBuilder::VectorKeyframeAnimatorBuilder(std::vector* target, +VectorAnimatorBuilder::VectorAnimatorBuilder(std::vector* target, VectorLenParser parse_len, VectorDataParser parse_data) : fParseLen(parse_len) , fParseData(parse_data) , fTarget(target) {} -sk_sp VectorKeyframeAnimatorBuilder::make(const AnimationBuilder& abuilder, +sk_sp VectorAnimatorBuilder::makeFromKeyframes(const AnimationBuilder& abuilder, const skjson::ArrayValue& jkfs) { SkASSERT(jkfs.size() > 0); @@ -186,7 +186,7 @@ sk_sp VectorKeyframeAnimatorBuilder::make(const AnimationBuild fTarget)); } -bool VectorKeyframeAnimatorBuilder::parseValue(const AnimationBuilder&, +bool VectorAnimatorBuilder::parseValue(const AnimationBuilder&, const skjson::Value& jv) const { size_t vec_len; if (!this->fParseLen(jv, &vec_len)) { @@ -197,7 +197,7 @@ bool VectorKeyframeAnimatorBuilder::parseValue(const AnimationBuilder&, return fParseData(jv, vec_len, fTarget->data()); } -bool VectorKeyframeAnimatorBuilder::parseKFValue(const AnimationBuilder&, +bool VectorAnimatorBuilder::parseKFValue(const AnimationBuilder&, const skjson::ObjectValue&, const skjson::Value& jv, Keyframe::Value* kfv) { @@ -236,7 +236,7 @@ bool AnimatablePropertyContainer::bind(const AnimationBuilder& abui if (!ParseDefault((*jprop)["s"], false)) { // Regular (static or keyframed) vector value. - VectorKeyframeAnimatorBuilder builder( + VectorAnimatorBuilder builder( v, // Len parser. [](const skjson::Value& jv, size_t* len) -> bool { diff --git a/modules/skottie/src/animator/VectorKeyframeAnimator.h b/modules/skottie/src/animator/VectorKeyframeAnimator.h index 5b8c209e6f..d2531345e1 100644 --- a/modules/skottie/src/animator/VectorKeyframeAnimator.h +++ b/modules/skottie/src/animator/VectorKeyframeAnimator.h @@ -14,14 +14,14 @@ namespace skottie::internal { -class VectorKeyframeAnimatorBuilder final : public KeyframeAnimatorBuilder { +class VectorAnimatorBuilder final : public AnimatorBuilder { public: using VectorLenParser = bool(*)(const skjson::Value&, size_t*); using VectorDataParser = bool(*)(const skjson::Value&, size_t, float*); - VectorKeyframeAnimatorBuilder(std::vector*, VectorLenParser, VectorDataParser); + VectorAnimatorBuilder(std::vector*, VectorLenParser, VectorDataParser); - sk_sp make(const AnimationBuilder&, const skjson::ArrayValue&) override; + sk_sp makeFromKeyframes(const AnimationBuilder&, const skjson::ArrayValue&) override; private: bool parseValue(const AnimationBuilder&, const skjson::Value&) const override;