1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkContourMeasure.h" 9cb93a386Sopenharmony_ci#include "include/core/SkPathBuilder.h" 10cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottieJson.h" 11cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottieValue.h" 12cb93a386Sopenharmony_ci#include "modules/skottie/src/animator/Animator.h" 13cb93a386Sopenharmony_ci#include "modules/skottie/src/animator/KeyframeAnimator.h" 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ci#include <cmath> 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_cinamespace skottie::internal { 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_cinamespace { 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci// Spatial 2D specialization: stores SkV2s and optional contour interpolators externally. 22cb93a386Sopenharmony_ciclass Vec2KeyframeAnimator final : public KeyframeAnimator { 23cb93a386Sopenharmony_cipublic: 24cb93a386Sopenharmony_ci struct SpatialValue { 25cb93a386Sopenharmony_ci Vec2Value v2; 26cb93a386Sopenharmony_ci sk_sp<SkContourMeasure> cmeasure; 27cb93a386Sopenharmony_ci }; 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci Vec2KeyframeAnimator(std::vector<Keyframe> kfs, std::vector<SkCubicMap> cms, 30cb93a386Sopenharmony_ci std::vector<SpatialValue> vs, Vec2Value* vec_target, float* rot_target) 31cb93a386Sopenharmony_ci : INHERITED(std::move(kfs), std::move(cms)) 32cb93a386Sopenharmony_ci , fValues(std::move(vs)) 33cb93a386Sopenharmony_ci , fVecTarget(vec_target) 34cb93a386Sopenharmony_ci , fRotTarget(rot_target) {} 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ciprivate: 37cb93a386Sopenharmony_ci StateChanged update(const Vec2Value& new_vec_value, const Vec2Value& new_tan_value) { 38cb93a386Sopenharmony_ci auto changed = (new_vec_value != *fVecTarget); 39cb93a386Sopenharmony_ci *fVecTarget = new_vec_value; 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci if (fRotTarget) { 42cb93a386Sopenharmony_ci const auto new_rot_value = SkRadiansToDegrees(std::atan2(new_tan_value.y, 43cb93a386Sopenharmony_ci new_tan_value.x)); 44cb93a386Sopenharmony_ci changed |= new_rot_value != *fRotTarget; 45cb93a386Sopenharmony_ci *fRotTarget = new_rot_value; 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci return changed; 49cb93a386Sopenharmony_ci } 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ci StateChanged onSeek(float t) override { 52cb93a386Sopenharmony_ci auto get_lerp_info = [this](float t) { 53cb93a386Sopenharmony_ci auto lerp_info = this->getLERPInfo(t); 54cb93a386Sopenharmony_ci 55cb93a386Sopenharmony_ci // When tracking rotation/orientation, the last keyframe requires special handling: 56cb93a386Sopenharmony_ci // it doesn't store any spatial information but it is expected to maintain the 57cb93a386Sopenharmony_ci // previous orientation (per AE semantics). 58cb93a386Sopenharmony_ci // 59cb93a386Sopenharmony_ci // The easiest way to achieve this is to actually swap with the previous keyframe, 60cb93a386Sopenharmony_ci // with an adjusted weight of 1. 61cb93a386Sopenharmony_ci const auto vidx = lerp_info.vrec0.idx; 62cb93a386Sopenharmony_ci if (fRotTarget && vidx == fValues.size() - 1 && vidx > 0) { 63cb93a386Sopenharmony_ci SkASSERT(!fValues[vidx].cmeasure); 64cb93a386Sopenharmony_ci SkASSERT(lerp_info.vrec1.idx == vidx); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ci // Change LERPInfo{0, SIZE - 1, SIZE - 1} 67cb93a386Sopenharmony_ci // to LERPInfo{1, SIZE - 2, SIZE - 1} 68cb93a386Sopenharmony_ci lerp_info.weight = 1; 69cb93a386Sopenharmony_ci lerp_info.vrec0 = {vidx - 1}; 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci // This yields equivalent lerp results because keyframed values are contiguous 72cb93a386Sopenharmony_ci // i.e frame[n-1].end_val == frame[n].start_val. 73cb93a386Sopenharmony_ci } 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_ci return lerp_info; 76cb93a386Sopenharmony_ci }; 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci const auto lerp_info = get_lerp_info(t); 79cb93a386Sopenharmony_ci 80cb93a386Sopenharmony_ci const auto& v0 = fValues[lerp_info.vrec0.idx]; 81cb93a386Sopenharmony_ci if (v0.cmeasure) { 82cb93a386Sopenharmony_ci // Spatial keyframe: the computed weight is relative to the interpolation path 83cb93a386Sopenharmony_ci // arc length. 84cb93a386Sopenharmony_ci SkPoint pos; 85cb93a386Sopenharmony_ci SkVector tan; 86cb93a386Sopenharmony_ci if (v0.cmeasure->getPosTan(lerp_info.weight * v0.cmeasure->length(), &pos, &tan)) { 87cb93a386Sopenharmony_ci return this->update({ pos.fX, pos.fY }, {tan.fX, tan.fY}); 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci } 90cb93a386Sopenharmony_ci 91cb93a386Sopenharmony_ci const auto& v1 = fValues[lerp_info.vrec1.idx]; 92cb93a386Sopenharmony_ci const auto tan = v1.v2 - v0.v2; 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci return this->update(Lerp(v0.v2, v1.v2, lerp_info.weight), tan); 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_ci const std::vector<Vec2KeyframeAnimator::SpatialValue> fValues; 98cb93a386Sopenharmony_ci Vec2Value* fVecTarget; 99cb93a386Sopenharmony_ci float* fRotTarget; 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci using INHERITED = KeyframeAnimator; 102cb93a386Sopenharmony_ci}; 103cb93a386Sopenharmony_ci 104cb93a386Sopenharmony_ciclass Vec2ExpressionAnimator final : public Animator { 105cb93a386Sopenharmony_cipublic: 106cb93a386Sopenharmony_ci Vec2ExpressionAnimator(sk_sp<ExpressionEvaluator<std::vector<float>>> expression_evaluator, 107cb93a386Sopenharmony_ci Vec2Value* target_value) 108cb93a386Sopenharmony_ci : fExpressionEvaluator(std::move(expression_evaluator)) 109cb93a386Sopenharmony_ci , fTarget(target_value) {} 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ciprivate: 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci StateChanged onSeek(float t) override { 114cb93a386Sopenharmony_ci auto old_value = *fTarget; 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci std::vector<float> result = fExpressionEvaluator->evaluate(t); 117cb93a386Sopenharmony_ci fTarget->x = result.size() > 0 ? result[0] : 0; 118cb93a386Sopenharmony_ci fTarget->y = result.size() > 1 ? result[1] : 0; 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci return *fTarget != old_value; 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci sk_sp<ExpressionEvaluator<std::vector<float>>> fExpressionEvaluator; 124cb93a386Sopenharmony_ci Vec2Value* fTarget; 125cb93a386Sopenharmony_ci}; 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ciclass Vec2AnimatorBuilder final : public AnimatorBuilder { 128cb93a386Sopenharmony_ci public: 129cb93a386Sopenharmony_ci Vec2AnimatorBuilder(Vec2Value* vec_target, float* rot_target) 130cb93a386Sopenharmony_ci : INHERITED(Keyframe::Value::Type::kIndex) 131cb93a386Sopenharmony_ci , fVecTarget(vec_target) 132cb93a386Sopenharmony_ci , fRotTarget(rot_target) {} 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci sk_sp<KeyframeAnimator> makeFromKeyframes(const AnimationBuilder& abuilder, 135cb93a386Sopenharmony_ci const skjson::ArrayValue& jkfs) override { 136cb93a386Sopenharmony_ci SkASSERT(jkfs.size() > 0); 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci fValues.reserve(jkfs.size()); 139cb93a386Sopenharmony_ci if (!this->parseKeyframes(abuilder, jkfs)) { 140cb93a386Sopenharmony_ci return nullptr; 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci fValues.shrink_to_fit(); 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci return sk_sp<Vec2KeyframeAnimator>( 145cb93a386Sopenharmony_ci new Vec2KeyframeAnimator(std::move(fKFs), 146cb93a386Sopenharmony_ci std::move(fCMs), 147cb93a386Sopenharmony_ci std::move(fValues), 148cb93a386Sopenharmony_ci fVecTarget, 149cb93a386Sopenharmony_ci fRotTarget)); 150cb93a386Sopenharmony_ci } 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci sk_sp<Animator> makeFromExpression(ExpressionManager& em, const char* expr) override { 153cb93a386Sopenharmony_ci sk_sp<ExpressionEvaluator<std::vector<SkScalar>>> expression_evaluator = 154cb93a386Sopenharmony_ci em.createArrayExpressionEvaluator(expr); 155cb93a386Sopenharmony_ci return sk_make_sp<Vec2ExpressionAnimator>(expression_evaluator, fVecTarget); 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci bool parseValue(const AnimationBuilder&, const skjson::Value& jv) const override { 159cb93a386Sopenharmony_ci return Parse(jv, fVecTarget); 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci private: 163cb93a386Sopenharmony_ci void backfill_spatial(const Vec2KeyframeAnimator::SpatialValue& val) { 164cb93a386Sopenharmony_ci SkASSERT(!fValues.empty()); 165cb93a386Sopenharmony_ci auto& prev_val = fValues.back(); 166cb93a386Sopenharmony_ci SkASSERT(!prev_val.cmeasure); 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_ci if (val.v2 == prev_val.v2) { 169cb93a386Sopenharmony_ci // spatial interpolation only make sense for noncoincident values 170cb93a386Sopenharmony_ci return; 171cb93a386Sopenharmony_ci } 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ci // Check whether v0 and v1 have the same direction AND ||v0||>=||v1|| 174cb93a386Sopenharmony_ci auto check_vecs = [](const SkV2& v0, const SkV2& v1) { 175cb93a386Sopenharmony_ci const auto v0_len2 = v0.lengthSquared(), 176cb93a386Sopenharmony_ci v1_len2 = v1.lengthSquared(); 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_ci // check magnitude 179cb93a386Sopenharmony_ci if (v0_len2 < v1_len2) { 180cb93a386Sopenharmony_ci return false; 181cb93a386Sopenharmony_ci } 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci // v0, v1 have the same direction iff dot(v0,v1) = ||v0||*||v1|| 184cb93a386Sopenharmony_ci // <=> dot(v0,v1)^2 = ||v0||^2 * ||v1||^2 185cb93a386Sopenharmony_ci const auto dot = v0.dot(v1); 186cb93a386Sopenharmony_ci return SkScalarNearlyEqual(dot * dot, v0_len2 * v1_len2); 187cb93a386Sopenharmony_ci }; 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci if (check_vecs(val.v2 - prev_val.v2, fTo) && 190cb93a386Sopenharmony_ci check_vecs(prev_val.v2 - val.v2, fTi)) { 191cb93a386Sopenharmony_ci // Both control points lie on the [prev_val..val] segment 192cb93a386Sopenharmony_ci // => we can power-reduce the Bezier "curve" to a straight line. 193cb93a386Sopenharmony_ci return; 194cb93a386Sopenharmony_ci } 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ci // Finally, this looks like a legitimate spatial keyframe. 197cb93a386Sopenharmony_ci SkPathBuilder p; 198cb93a386Sopenharmony_ci p.moveTo (prev_val.v2.x , prev_val.v2.y); 199cb93a386Sopenharmony_ci p.cubicTo(prev_val.v2.x + fTo.x, prev_val.v2.y + fTo.y, 200cb93a386Sopenharmony_ci val.v2.x + fTi.x, val.v2.y + fTi.y, 201cb93a386Sopenharmony_ci val.v2.x, val.v2.y); 202cb93a386Sopenharmony_ci prev_val.cmeasure = SkContourMeasureIter(p.detach(), false).next(); 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci bool parseKFValue(const AnimationBuilder&, 206cb93a386Sopenharmony_ci const skjson::ObjectValue& jkf, 207cb93a386Sopenharmony_ci const skjson::Value& jv, 208cb93a386Sopenharmony_ci Keyframe::Value* v) override { 209cb93a386Sopenharmony_ci Vec2KeyframeAnimator::SpatialValue val; 210cb93a386Sopenharmony_ci if (!Parse(jv, &val.v2)) { 211cb93a386Sopenharmony_ci return false; 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci 214cb93a386Sopenharmony_ci if (fPendingSpatial) { 215cb93a386Sopenharmony_ci this->backfill_spatial(val); 216cb93a386Sopenharmony_ci } 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci // Track the last keyframe spatial tangents (checked on next parseValue). 219cb93a386Sopenharmony_ci fTi = ParseDefault<SkV2>(jkf["ti"], {0,0}); 220cb93a386Sopenharmony_ci fTo = ParseDefault<SkV2>(jkf["to"], {0,0}); 221cb93a386Sopenharmony_ci fPendingSpatial = fTi != SkV2{0,0} || fTo != SkV2{0,0}; 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci if (fValues.empty() || val.v2 != fValues.back().v2 || fPendingSpatial) { 224cb93a386Sopenharmony_ci fValues.push_back(std::move(val)); 225cb93a386Sopenharmony_ci } 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci v->idx = SkToU32(fValues.size() - 1); 228cb93a386Sopenharmony_ci 229cb93a386Sopenharmony_ci return true; 230cb93a386Sopenharmony_ci } 231cb93a386Sopenharmony_ci 232cb93a386Sopenharmony_ci std::vector<Vec2KeyframeAnimator::SpatialValue> fValues; 233cb93a386Sopenharmony_ci Vec2Value* fVecTarget; // required 234cb93a386Sopenharmony_ci float* fRotTarget; // optional 235cb93a386Sopenharmony_ci SkV2 fTi{0,0}, 236cb93a386Sopenharmony_ci fTo{0,0}; 237cb93a386Sopenharmony_ci bool fPendingSpatial = false; 238cb93a386Sopenharmony_ci 239cb93a386Sopenharmony_ci using INHERITED = AnimatorBuilder; 240cb93a386Sopenharmony_ci }; 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci} // namespace 243cb93a386Sopenharmony_ci 244cb93a386Sopenharmony_cibool AnimatablePropertyContainer::bindAutoOrientable(const AnimationBuilder& abuilder, 245cb93a386Sopenharmony_ci const skjson::ObjectValue* jprop, 246cb93a386Sopenharmony_ci Vec2Value* v, float* orientation) { 247cb93a386Sopenharmony_ci if (!jprop) { 248cb93a386Sopenharmony_ci return false; 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci 251cb93a386Sopenharmony_ci if (!ParseDefault<bool>((*jprop)["s"], false)) { 252cb93a386Sopenharmony_ci // Regular (static or keyframed) 2D value. 253cb93a386Sopenharmony_ci Vec2AnimatorBuilder builder(v, orientation); 254cb93a386Sopenharmony_ci return this->bindImpl(abuilder, jprop, builder); 255cb93a386Sopenharmony_ci } 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci // Separate-dimensions vector value: each component is animated independently. 258cb93a386Sopenharmony_ci bool boundX = this->bind(abuilder, (*jprop)["x"], &v->x); 259cb93a386Sopenharmony_ci bool boundY = this->bind(abuilder, (*jprop)["y"], &v->y); 260cb93a386Sopenharmony_ci return boundX || boundY; 261cb93a386Sopenharmony_ci} 262cb93a386Sopenharmony_ci 263cb93a386Sopenharmony_citemplate <> 264cb93a386Sopenharmony_cibool AnimatablePropertyContainer::bind<Vec2Value>(const AnimationBuilder& abuilder, 265cb93a386Sopenharmony_ci const skjson::ObjectValue* jprop, 266cb93a386Sopenharmony_ci Vec2Value* v) { 267cb93a386Sopenharmony_ci return this->bindAutoOrientable(abuilder, jprop, v, nullptr); 268cb93a386Sopenharmony_ci} 269cb93a386Sopenharmony_ci 270cb93a386Sopenharmony_ci} // namespace skottie::internal 271