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