aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/skottie/SkottieAnimator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'experimental/skottie/SkottieAnimator.cpp')
-rw-r--r--experimental/skottie/SkottieAnimator.cpp374
1 files changed, 0 insertions, 374 deletions
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