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