Home | History | Annotate | Download | only in skottie
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkottieAnimator.h"
      9 
     10 #include "SkCubicMap.h"
     11 #include "SkJSONCPP.h"
     12 #include "SkottieProperties.h"
     13 #include "SkottieParser.h"
     14 #include "SkTArray.h"
     15 
     16 #include <memory>
     17 
     18 namespace skottie {
     19 
     20 namespace {
     21 
     22 #define LOG SkDebugf
     23 
     24 bool LogFail(const Json::Value& json, const char* msg) {
     25     const auto dump = json.toStyledString();
     26     LOG("!! %s: %s", msg, dump.c_str());
     27     return false;
     28 }
     29 
     30 template <typename T>
     31 static inline T lerp(const T&, const T&, float);
     32 
     33 template <>
     34 ScalarValue lerp(const ScalarValue& v0, const ScalarValue& v1, float t) {
     35     SkASSERT(t >= 0 && t <= 1);
     36     return v0 * (1 - t) + v1 * t;
     37 }
     38 
     39 template <>
     40 VectorValue lerp(const VectorValue& v0, const VectorValue& v1, float t) {
     41     SkASSERT(v0.size() == v1.size());
     42 
     43     VectorValue v;
     44     v.reserve(v0.size());
     45 
     46     for (size_t i = 0; i < v0.size(); ++i) {
     47         v.push_back(lerp(v0[i], v1[i], t));
     48     }
     49 
     50     return v;
     51 }
     52 
     53 template <>
     54 ShapeValue lerp(const ShapeValue& v0, const ShapeValue& v1, float t) {
     55     SkASSERT(t >= 0 && t <= 1);
     56     SkASSERT(v1.isInterpolatable(v0));
     57 
     58     ShapeValue v;
     59     SkAssertResult(v1.interpolate(v0, t, &v));
     60     v.setIsVolatile(true);
     61 
     62     return v;
     63 }
     64 
     65 class KeyframeAnimatorBase : public sksg::Animator {
     66 public:
     67     int count() const { return fRecs.count(); }
     68 
     69 protected:
     70     KeyframeAnimatorBase() = default;
     71 
     72     struct KeyframeRec {
     73         float t0, t1;
     74         int   vidx0, vidx1, // v0/v1 indices
     75               cmidx;        // cubic map index
     76 
     77         bool contains(float t) const { return t0 <= t && t <= t1; }
     78         bool isConstant() const { return vidx0 == vidx1; }
     79         bool isValid() const {
     80             SkASSERT(t0 <= t1);
     81             // Constant frames don't need/use t1 and vidx1.
     82             return t0 < t1 || this->isConstant();
     83         }
     84     };
     85 
     86     const KeyframeRec& frame(float t) {
     87         if (!fCachedRec || !fCachedRec->contains(t)) {
     88             fCachedRec = findFrame(t);
     89         }
     90         return *fCachedRec;
     91     }
     92 
     93     float localT(const KeyframeRec& rec, float t) const {
     94         SkASSERT(rec.isValid());
     95         SkASSERT(!rec.isConstant());
     96         SkASSERT(t > rec.t0 && t < rec.t1);
     97 
     98         auto lt = (t - rec.t0) / (rec.t1 - rec.t0);
     99 
    100         return rec.cmidx < 0
    101             ? lt
    102             : SkTPin(fCubicMaps[rec.cmidx].computeYFromX(lt), 0.0f, 1.0f);
    103     }
    104 
    105     virtual int parseValue(const Json::Value&) = 0;
    106 
    107     void parseKeyFrames(const Json::Value& jframes) {
    108         if (!jframes.isArray())
    109             return;
    110 
    111         for (const auto& jframe : jframes) {
    112             if (!jframe.isObject())
    113                 continue;
    114 
    115             float t0;
    116             if (!Parse(jframe["t"], &t0)) {
    117                 continue;
    118             }
    119 
    120             if (!fRecs.empty()) {
    121                 if (fRecs.back().t1 >= t0) {
    122                     LOG("!! Ignoring out-of-order key frame (t:%f < t:%f)\n", t0, fRecs.back().t1);
    123                     continue;
    124                 }
    125                 // Back-fill t1 in prev interval.  Note: we do this even if we end up discarding
    126                 // the current interval (to support "t"-only final frames).
    127                 fRecs.back().t1 = t0;
    128             }
    129 
    130             const auto vidx0 = this->parseValue(jframe["s"]);
    131             if (vidx0 < 0) {
    132                 continue;
    133             }
    134 
    135             // Defaults for constant frames.
    136             int vidx1 = vidx0, cmidx = -1;
    137 
    138             if (!ParseDefault(jframe["h"], false)) {
    139                 // Regular frame, requires an end value.
    140                 vidx1 = this->parseValue(jframe["e"]);
    141                 if (vidx1 < 0) {
    142                     continue;
    143                 }
    144 
    145                 // default is linear lerp
    146                 static constexpr SkPoint kDefaultC0 = { 0, 0 },
    147                                          kDefaultC1 = { 1, 1 };
    148                 const auto c0 = ParseDefault(jframe["i"], kDefaultC0),
    149                            c1 = ParseDefault(jframe["o"], kDefaultC1);
    150 
    151                 if (c0 != kDefaultC0 || c1 != kDefaultC1) {
    152                     // TODO: is it worth de-duping these?
    153                     cmidx = fCubicMaps.count();
    154                     fCubicMaps.emplace_back();
    155                     // TODO: why do we have to plug these inverted?
    156                     fCubicMaps.back().setPts(c1, c0);
    157                 }
    158             }
    159 
    160             fRecs.push_back({t0, t0, vidx0, vidx1, cmidx });
    161         }
    162 
    163         // If we couldn't determine a valid t1 for the last frame, discard it.
    164         if (!fRecs.empty() && !fRecs.back().isValid()) {
    165             fRecs.pop_back();
    166         }
    167 
    168         SkASSERT(fRecs.empty() || fRecs.back().isValid());
    169     }
    170 
    171 private:
    172     const KeyframeRec* findFrame(float t) const {
    173         SkASSERT(!fRecs.empty());
    174 
    175         auto f0 = &fRecs.front(),
    176              f1 = &fRecs.back();
    177 
    178         SkASSERT(f0->isValid());
    179         SkASSERT(f1->isValid());
    180 
    181         if (t < f0->t0) {
    182             return f0;
    183         }
    184 
    185         if (t > f1->t1) {
    186             return f1;
    187         }
    188 
    189         while (f0 != f1) {
    190             SkASSERT(f0 < f1);
    191             SkASSERT(t >= f0->t0 && t <= f1->t1);
    192 
    193             const auto f = f0 + (f1 - f0) / 2;
    194             SkASSERT(f->isValid());
    195 
    196             if (t > f->t1) {
    197                 f0 = f + 1;
    198             } else {
    199                 f1 = f;
    200             }
    201         }
    202 
    203         SkASSERT(f0 == f1);
    204         SkASSERT(f0->contains(t));
    205 
    206         return f0;
    207     }
    208 
    209     SkTArray<KeyframeRec> fRecs;
    210     SkTArray<SkCubicMap>  fCubicMaps;
    211     const KeyframeRec*    fCachedRec = nullptr;
    212 
    213     using INHERITED = sksg::Animator;
    214 };
    215 
    216 template <typename T>
    217 class KeyframeAnimator final : public KeyframeAnimatorBase {
    218 public:
    219     static std::unique_ptr<KeyframeAnimator> Make(const Json::Value& jframes,
    220                                                   std::function<void(const T&)>&& apply) {
    221         std::unique_ptr<KeyframeAnimator> animator(new KeyframeAnimator(jframes, std::move(apply)));
    222         if (!animator->count())
    223             return nullptr;
    224 
    225         return animator;
    226     }
    227 
    228 protected:
    229     void onTick(float t) override {
    230         T val;
    231         this->eval(this->frame(t), t, &val);
    232 
    233         fApplyFunc(val);
    234     }
    235 
    236 private:
    237     KeyframeAnimator(const Json::Value& jframes,
    238                      std::function<void(const T&)>&& apply)
    239         : fApplyFunc(std::move(apply)) {
    240         this->parseKeyFrames(jframes);
    241     }
    242 
    243     int parseValue(const Json::Value& jv) override {
    244         T val;
    245         if (!Parse(jv, &val) || (!fVs.empty() &&
    246                 ValueTraits<T>::Cardinality(val) != ValueTraits<T>::Cardinality(fVs.back()))) {
    247             return -1;
    248         }
    249 
    250         // TODO: full deduping?
    251         if (fVs.empty() || val != fVs.back()) {
    252             fVs.push_back(std::move(val));
    253         }
    254         return fVs.count() - 1;
    255     }
    256 
    257     void eval(const KeyframeRec& rec, float t, T* v) const {
    258         SkASSERT(rec.isValid());
    259         if (rec.isConstant() || t <= rec.t0) {
    260             *v = fVs[rec.vidx0];
    261         } else if (t >= rec.t1) {
    262             *v = fVs[rec.vidx1];
    263         } else {
    264             const auto lt = this->localT(rec, t);
    265             const auto& v0 = fVs[rec.vidx0];
    266             const auto& v1 = fVs[rec.vidx1];
    267             *v = lerp(v0, v1, lt);
    268         }
    269     }
    270 
    271     const std::function<void(const T&)> fApplyFunc;
    272     SkTArray<T>                         fVs;
    273 
    274 
    275     using INHERITED = KeyframeAnimatorBase;
    276 };
    277 
    278 template <typename T>
    279 static inline bool BindPropertyImpl(const Json::Value& jprop,
    280                                     sksg::AnimatorList* animators,
    281                                     std::function<void(const T&)>&& apply,
    282                                     const T* noop = nullptr) {
    283     if (!jprop.isObject())
    284         return false;
    285 
    286     const auto& jpropA = jprop["a"];
    287     const auto& jpropK = jprop["k"];
    288 
    289     // Older Json versions don't have an "a" animation marker.
    290     // For those, we attempt to parse both ways.
    291     if (!ParseDefault(jpropA, false)) {
    292         T val;
    293         if (Parse<T>(jpropK, &val)) {
    294             // Static property.
    295             if (noop && val == *noop)
    296                 return false;
    297 
    298             apply(val);
    299             return true;
    300         }
    301 
    302         if (!jpropA.isNull()) {
    303             return LogFail(jprop, "Could not parse (explicit) static property");
    304         }
    305     }
    306 
    307     // Keyframe property.
    308     auto animator = KeyframeAnimator<T>::Make(jpropK, std::move(apply));
    309 
    310     if (!animator) {
    311         return LogFail(jprop, "Could not parse keyframed property");
    312     }
    313 
    314     animators->push_back(std::move(animator));
    315 
    316     return true;
    317 }
    318 
    319 class SplitPointAnimator final : public sksg::Animator {
    320 public:
    321     static std::unique_ptr<SplitPointAnimator> Make(const Json::Value& jprop,
    322                                                     std::function<void(const VectorValue&)>&& apply,
    323                                                     const VectorValue*) {
    324         if (!jprop.isObject())
    325             return nullptr;
    326 
    327         std::unique_ptr<SplitPointAnimator> split_animator(
    328             new SplitPointAnimator(std::move(apply)));
    329 
    330         // This raw pointer is captured in lambdas below. But the lambdas are owned by
    331         // the object itself, so the scope is bound to the life time of the object.
    332         auto* split_animator_ptr = split_animator.get();
    333 
    334         if (!BindPropertyImpl<ScalarValue>(jprop["x"], &split_animator->fAnimators,
    335                 [split_animator_ptr](const ScalarValue& x) { split_animator_ptr->setX(x); }) ||
    336             !BindPropertyImpl<ScalarValue>(jprop["y"], &split_animator->fAnimators,
    337                 [split_animator_ptr](const ScalarValue& y) { split_animator_ptr->setY(y); })) {
    338             LogFail(jprop, "Could not parse split property");
    339             return nullptr;
    340         }
    341 
    342         if (split_animator->fAnimators.empty()) {
    343             // Static split property, no need to hold on to the split animator.
    344             return nullptr;
    345         }
    346 
    347         return split_animator;
    348     }
    349 
    350     void onTick(float t) override {
    351         for (const auto& animator : fAnimators) {
    352             animator->tick(t);
    353         }
    354 
    355         const VectorValue vec = { fX, fY };
    356         fApplyFunc(vec);
    357     }
    358 
    359     void setX(const ScalarValue& x) { fX = x; }
    360     void setY(const ScalarValue& y) { fY = y; }
    361 
    362 private:
    363     explicit SplitPointAnimator(std::function<void(const VectorValue&)>&& apply)
    364         : fApplyFunc(std::move(apply)) {}
    365 
    366     const std::function<void(const VectorValue&)> fApplyFunc;
    367     sksg::AnimatorList                            fAnimators;
    368 
    369     ScalarValue                                   fX = 0,
    370                                                   fY = 0;
    371 
    372     using INHERITED = sksg::Animator;
    373 };
    374 
    375 bool BindSplitPositionProperty(const Json::Value& jprop,
    376                                sksg::AnimatorList* animators,
    377                                std::function<void(const VectorValue&)>&& apply,
    378                                const VectorValue* noop) {
    379     if (auto split_animator = SplitPointAnimator::Make(jprop, std::move(apply), noop)) {
    380         animators->push_back(std::unique_ptr<sksg::Animator>(split_animator.release()));
    381         return true;
    382     }
    383 
    384     return false;
    385 }
    386 
    387 } // namespace
    388 
    389 template <>
    390 bool BindProperty(const Json::Value& jprop,
    391                   sksg::AnimatorList* animators,
    392                   std::function<void(const ScalarValue&)>&& apply,
    393                   const ScalarValue* noop) {
    394     return BindPropertyImpl(jprop, animators, std::move(apply), noop);
    395 }
    396 
    397 template <>
    398 bool BindProperty(const Json::Value& jprop,
    399                   sksg::AnimatorList* animators,
    400                   std::function<void(const VectorValue&)>&& apply,
    401                   const VectorValue* noop) {
    402     return ParseDefault(jprop["s"], false)
    403         ? BindSplitPositionProperty(jprop, animators, std::move(apply), noop)
    404         : BindPropertyImpl(jprop, animators, std::move(apply), noop);
    405 }
    406 
    407 template <>
    408 bool BindProperty(const Json::Value& jprop,
    409                   sksg::AnimatorList* animators,
    410                   std::function<void(const ShapeValue&)>&& apply,
    411                   const ShapeValue* noop) {
    412     return BindPropertyImpl(jprop, animators, std::move(apply), noop);
    413 }
    414 
    415 } // namespace skottie
    416