From d6c4f8f608d431fcc11dab1f7c9858b6cdfb3d8a Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Thu, 4 Jan 2018 13:11:14 -0500 Subject: [skotty] Add cubic Bezier lerp stubs ... and refactor some of the keyframe parsing. TBR= Change-Id: If45922eab36412908036401cee693202f5c3e281 Reviewed-on: https://skia-review.googlesource.com/91100 Reviewed-by: Florin Malita Commit-Queue: Florin Malita --- experimental/skotty/SkottyAnimator.cpp | 12 +++++ experimental/skotty/SkottyAnimator.h | 82 ++++++++++++++++++++++------------ experimental/skotty/SkottyPriv.h | 8 ++++ 3 files changed, 74 insertions(+), 28 deletions(-) (limited to 'experimental') diff --git a/experimental/skotty/SkottyAnimator.cpp b/experimental/skotty/SkottyAnimator.cpp index 451f146888..d4c5c96c1a 100644 --- a/experimental/skotty/SkottyAnimator.cpp +++ b/experimental/skotty/SkottyAnimator.cpp @@ -42,4 +42,16 @@ void KeyframeInterval::lerp(float t, ShapeValue* v) const { SkAssertResult(fV1.fPath.interpolate(fV0.fPath, t, &v->fPath)); } +float AnimatorBase::ComputeLocalT(float t, float t0, float t1, + const SkPoint& c0, const SkPoint& c1) { + SkASSERT(t1 > t0); + auto lt = (t - t0) / (t1 - t0); + + if (c0 != SkPoint({0, 0}) || c1 != SkPoint({1, 1})) { + // TODO: lt = CubicBezier(lt, c0, c1); + } + + return SkTPin(lt, 0, 1); +} + } // namespace skotty diff --git a/experimental/skotty/SkottyAnimator.h b/experimental/skotty/SkottyAnimator.h index 833390e744..f8d1c3abb6 100644 --- a/experimental/skotty/SkottyAnimator.h +++ b/experimental/skotty/SkottyAnimator.h @@ -25,17 +25,61 @@ public: virtual void tick(SkMSec) = 0; protected: - AnimatorBase() = default; + AnimatorBase() = default; + + // Compute a cubic-Bezier-interpolated t relative to [t0..t1]. + static float ComputeLocalT(float t, float t0, float t1, + const SkPoint& c0, const SkPoint& c1); }; // Describes a keyframe interpolation interval (v0@t0) -> (v1@t1). // TODO: add interpolation params. template struct KeyframeInterval { - T fV0, - fV1; - float fT0 = 0, - fT1 = 0; + // Start/end values. + T fV0, + fV1; + + // Start/end times. + float fT0 = 0, + fT1 = 0; + + // Cubic Bezier interpolation control pts. + SkPoint fC0, + fC1; + + // Parse the current interval AND back-fill prev interval t1. + bool parse(const Json::Value& k, KeyframeInterval* prev) { + SkASSERT(k.isObject()); + + fT0 = fT1 = ParseScalar(k["t"], SK_ScalarMin); + if (fT0 == SK_ScalarMin) { + return false; + } + + if (prev) { + if (prev->fT1 >= fT0) { + LOG("!! Dropping out-of-order key frame (t: %f < t: %f)\n", fT0, prev->fT1); + return false; + } + // 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). + prev->fT1 = fT0; + } + + if (!T::Parse(k["s"], &fV0) || + !T::Parse(k["e"], &fV1) || + fV0.cardinality() != fV1.cardinality() || + (prev && fV0.cardinality() != prev->fV0.cardinality())) { + return false; + } + + // default is linear lerp + fC0 = ParsePoint(k["i"], SkPoint::Make(0, 0)); + fC1 = ParsePoint(k["o"], SkPoint::Make(1, 1)); + + return true; + } void lerp(float t, T*) const; }; @@ -49,10 +93,9 @@ public: void tick(SkMSec t) override { const auto& frame = this->findInterval(t); - const auto rel_t = (t - frame.fT0) / (frame.fT1 - frame.fT0); ValT val; - frame.lerp(SkTPin(rel_t, 0, 1), &val); + frame.lerp(ComputeLocalT(t, frame.fT0, frame.fT1, frame.fC0, frame.fC1), &val); fFunc(fTarget, val.template as()); } @@ -80,36 +123,19 @@ Animator::Make(const Json::Value& frames, return nullptr; SkTArray> intervals; + intervals.reserve(frames.size()); + for (const auto& frame : frames) { if (!frame.isObject()) return nullptr; - const auto t = ParseScalar(frame["t"], SK_ScalarMin); - if (t == SK_ScalarMin) - break; - - auto* prev_interval = intervals.empty() ? nullptr : &intervals.back(); - if (prev_interval) { - if (prev_interval->fT0 >= t) { - LOG("!! Ignoring out-of-order key frame (t: %f < t: %f)\n", t, prev_interval->fT0); - continue; - } - // Back-fill the prev interval t1. - prev_interval->fT1 = t; - } - auto& curr_interval = intervals.push_back(); - if (!ValT::Parse(frame["s"], &curr_interval.fV0) || - !ValT::Parse(frame["e"], &curr_interval.fV1) || - curr_interval.fV0.cardinality() != curr_interval.fV1.cardinality() || - (prev_interval && - curr_interval.fV0.cardinality() != prev_interval->fV0.cardinality())) { + auto* prev_interval = intervals.count() > 1 ? &intervals.fromBack(1) : nullptr; + if (!curr_interval.parse(frame, prev_interval)) { // Invalid frame, or "t"-only frame. intervals.pop_back(); continue; } - - curr_interval.fT0 = curr_interval.fT1 = t; } // If we couldn't determine a t1 for the last interval, discard it. diff --git a/experimental/skotty/SkottyPriv.h b/experimental/skotty/SkottyPriv.h index 35b1e847a3..e39994100c 100644 --- a/experimental/skotty/SkottyPriv.h +++ b/experimental/skotty/SkottyPriv.h @@ -9,6 +9,7 @@ #define SkottyPriv_DEFINED #include "SkJSONCPP.h" +#include "SkPoint.h" #include "SkScalar.h" #include "SkString.h" @@ -36,6 +37,13 @@ static inline bool ParseBool(const Json::Value& v, bool defaultValue) { ? v.asBool() : defaultValue; } +static inline SkPoint ParsePoint(const Json::Value& v, const SkPoint& defaultValue) { + return v.isObject() + ? SkPoint::Make(ParseScalar(v["x"], defaultValue.x()), + ParseScalar(v["y"], defaultValue.y())) + : defaultValue; +} + } // namespace #endif // SkottyPriv_DEFINED -- cgit v1.2.3