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 + (v1 - v0) * 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