xref: /aosp_15_r20/external/skia/modules/skottie/tests/Keyframe.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "modules/skottie/include/ExternalLayer.h"
9 #include "modules/skottie/src/SkottiePriv.h"
10 #include "modules/skottie/src/SkottieValue.h"
11 #include "modules/skottie/src/animator/Animator.h"
12 #include "src/utils/SkJSON.h"
13 #include "tests/Test.h"
14 
15 #include <cmath>
16 
17 using namespace skottie;
18 using namespace skottie::internal;
19 
20 namespace  {
21 
22 template <typename T>
23 class MockProperty final : public AnimatablePropertyContainer {
24 public:
MockProperty(const char * jprop)25     explicit MockProperty(const char* jprop) {
26         AnimationBuilder abuilder(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
27                                   nullptr, nullptr,
28                                   {100, 100}, 10, 1, 0);
29         skjson::DOM json_dom(jprop, strlen(jprop));
30 
31         fDidBind = this->bind(abuilder, json_dom.root(), &fValue);
32     }
33 
operator bool() const34     explicit operator bool() const { return fDidBind; }
35 
operator ()(float t)36     const T& operator()(float t) { this->seek(t); return fValue; }
37 
38 private:
onSync()39     void onSync() override {}
40 
41     T     fValue = T();
42     bool  fDidBind;
43 };
44 
45 }  // namespace
46 
DEF_TEST(Skottie_Keyframe,reporter)47 DEF_TEST(Skottie_Keyframe, reporter) {
48     {
49         MockProperty<ScalarValue> prop(R"({})");
50         REPORTER_ASSERT(reporter, !prop);
51     }
52     {
53         MockProperty<ScalarValue> prop(R"({ "a": 1, "k": [] })");
54         REPORTER_ASSERT(reporter, !prop);
55     }
56     {
57         // New style
58         MockProperty<ScalarValue> prop(R"({
59                                          "a": 1,
60                                          "k": [
61                                            { "t":  1, "s": 1 },
62                                            { "t":  2, "s": 2 },
63                                            { "t":  3, "s": 4 }
64                                          ]
65                                        })");
66         REPORTER_ASSERT(reporter, prop);
67         REPORTER_ASSERT(reporter, !prop.isStatic());
68         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( -1), 1));
69         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(  0), 1));
70         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(  1), 1));
71         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1.5f));
72         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(  2), 2));
73         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 3));
74         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(  3), 4));
75         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(  4), 4));
76     }
77     {
78         // New style hold (hard stops)
79         MockProperty<ScalarValue> prop(R"({
80                                          "a": 1,
81                                          "k": [
82                                            { "t":  1, "s": 1, "h": true },
83                                            { "t":  2, "s": 2, "h": true },
84                                            { "t":  3, "s": 4, "h": true }
85                                          ]
86                                        })");
87         REPORTER_ASSERT(reporter, prop);
88         REPORTER_ASSERT(reporter, !prop.isStatic());
89         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0  ), 1));
90         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1  ), 1));
91         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1));
92         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(2.f, 0.f)), 1));
93         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2  ), 2));
94         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 2));
95         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(3.f, 0.f)), 2));
96         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(3  ), 4));
97         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(4  ), 4));
98     }
99     {
100         // Legacy style
101         MockProperty<ScalarValue> prop(R"({
102                                          "a": 1,
103                                          "k": [
104                                            { "t":  1, "s": 1, "e": 2 },
105                                            { "t":  2, "s": 2, "e": 4 },
106                                            { "t":  3 }
107                                          ]
108                                        })");
109         REPORTER_ASSERT(reporter, prop);
110         REPORTER_ASSERT(reporter, !prop.isStatic());
111         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(-1), 1));
112         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 0), 1));
113         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 1  ), 1));
114         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 1.5), 1.5f));
115         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 2  ), 2));
116         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 2.5), 3));
117         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 3  ), 4));
118         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 4  ), 4));
119     }
120     {
121         // Legacy style hold (hard stops)
122         MockProperty<ScalarValue> prop(R"({
123                                          "a": 1,
124                                          "k": [
125                                            { "t":  1, "s": 1, "e": 2, "h": true },
126                                            { "t":  2, "s": 2, "e": 4, "h": true },
127                                            { "t":  3 }
128                                          ]
129                                        })");
130         REPORTER_ASSERT(reporter, prop);
131         REPORTER_ASSERT(reporter, !prop.isStatic());
132         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0  ), 1));
133         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1  ), 1));
134         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1));
135         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(2.f, 0.f)), 1));
136         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2  ), 2));
137         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 2));
138         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(3.f, 0.f)), 2));
139         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(3  ), 4));
140         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(4  ), 4));
141     }
142     {
143         // Static scalar prop (all equal keyframes, using float kf Value)
144         MockProperty<ScalarValue> prop(R"({
145                                          "a": 1,
146                                          "k": [
147                                            { "t":  1, "s": 42, "e": 42 },
148                                            { "t":  2, "s": 42, "e": 42 },
149                                            { "t":  3 }
150                                          ]
151                                        })");
152         REPORTER_ASSERT(reporter, prop);
153         REPORTER_ASSERT(reporter, prop.isStatic());
154         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0), 42));
155     }
156     {
157         // Static vector prop (all equal keyframes, using uint32 kf Value)
158         MockProperty<Vec2Value> prop(R"({
159                                        "a": 1,
160                                        "k": [
161                                          { "t":  1, "s": [4,2], "e": [4,2] },
162                                          { "t":  2, "s": [4,2], "e": [4,2] },
163                                          { "t":  3 }
164                                        ]
165                                      })");
166         REPORTER_ASSERT(reporter, prop);
167         REPORTER_ASSERT(reporter, prop.isStatic());
168         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0).x, 4));
169         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0).y, 2));
170     }
171     {
172         // Spatial interpolation [100,100]->[200,200], with supernormal easing:
173         // https://cubic-bezier.com/#.5,-0.5,.5,1.5
174         MockProperty<Vec2Value> prop(R"({
175                                        "a": 1,
176                                        "k": [
177                                          { "t": 0, "s": [100,100],
178                                            "o":{"x":[0.5], "y":[-0.5]}, "i":{"x":[0.5], "y":[1.5]},
179                                           "to": [10,15], "ti": [-10,-5]
180                                          },
181                                          { "t": 1, "s": [200,200]
182                                          }
183                                        ]
184                                      })");
185         REPORTER_ASSERT(reporter, prop);
186         REPORTER_ASSERT(reporter, !prop.isStatic());
187 
188         // Not linear.
189         REPORTER_ASSERT(reporter, !SkScalarNearlyEqual(prop(0.5f).x, 150.f));
190         REPORTER_ASSERT(reporter, !SkScalarNearlyEqual(prop(0.5f).y, 150.f));
191 
192         // Subnormal region triggers extrapolation.
193         REPORTER_ASSERT(reporter, prop(0.15f).x < 100);
194         REPORTER_ASSERT(reporter, prop(0.15f).y < 100);
195 
196         // Supernormal region triggers extrapolation.
197         REPORTER_ASSERT(reporter, prop(0.85f).x > 200);
198         REPORTER_ASSERT(reporter, prop(0.85f).y > 200);
199     }
200     {
201         // Coincident keyframes (t == 1)
202         //
203         // Effective interpolation intervals:
204         //   [0 .. 1) -> [100 .. 200)
205         //   [1 .. 2) -> [300 .. 400)
206         //
207         // When more than 2 concident keyframes are present, only the first and last one count.
208         MockProperty<ScalarValue> prop(R"({
209                                             "a": 1,
210                                             "k": [
211                                               { "t": 0, "s": [100]  },
212                                               { "t": 1, "s": [200]  },
213                                               { "t": 1, "s": [1000] },
214                                               { "t": 1, "s": [300]  },
215                                               { "t": 2, "s": [400]  }
216                                             ]
217                                           })");
218         REPORTER_ASSERT(reporter, prop);
219         REPORTER_ASSERT(reporter, !prop.isStatic());
220 
221         REPORTER_ASSERT(reporter, prop(0.9999f) > 100);
222         REPORTER_ASSERT(reporter, prop(0.9999f) < 200);
223         REPORTER_ASSERT(reporter, prop(1) == 300);
224         REPORTER_ASSERT(reporter, prop(1.0001f) > 300);
225         REPORTER_ASSERT(reporter, prop(1.0001f) < 400);
226     }
227 }
228