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