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, nullptr,
27 {100, 100}, 10, 1, 0);
28 skjson::DOM json_dom(jprop, strlen(jprop));
29
30 fDidBind = this->bind(abuilder, json_dom.root(), &fValue);
31 }
32
operator bool() const33 operator bool() const { return fDidBind; }
34
operator ()(float t)35 const T& operator()(float t) { this->seek(t); return fValue; }
36
37 private:
onSync()38 void onSync() override {}
39
40 T fValue = T();
41 bool fDidBind;
42 };
43
44 } // namespace
45
DEF_TEST(Skottie_Keyframe,reporter)46 DEF_TEST(Skottie_Keyframe, reporter) {
47 {
48 MockProperty<ScalarValue> prop(R"({})");
49 REPORTER_ASSERT(reporter, !prop);
50 }
51 {
52 MockProperty<ScalarValue> prop(R"({ "a": 1, "k": [] })");
53 REPORTER_ASSERT(reporter, !prop);
54 }
55 {
56 // New style
57 MockProperty<ScalarValue> prop(R"({
58 "a": 1,
59 "k": [
60 { "t": 1, "s": 1 },
61 { "t": 2, "s": 2 },
62 { "t": 3, "s": 4 }
63 ]
64 })");
65 REPORTER_ASSERT(reporter, prop);
66 REPORTER_ASSERT(reporter, !prop.isStatic());
67 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( -1), 1));
68 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 0), 1));
69 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 1), 1));
70 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1.5f));
71 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 2), 2));
72 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 3));
73 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 3), 4));
74 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 4), 4));
75 }
76 {
77 // New style hold (hard stops)
78 MockProperty<ScalarValue> prop(R"({
79 "a": 1,
80 "k": [
81 { "t": 1, "s": 1, "h": true },
82 { "t": 2, "s": 2, "h": true },
83 { "t": 3, "s": 4, "h": true }
84 ]
85 })");
86 REPORTER_ASSERT(reporter, prop);
87 REPORTER_ASSERT(reporter, !prop.isStatic());
88 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0 ), 1));
89 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1 ), 1));
90 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1));
91 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(2.f, 0.f)), 1));
92 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2 ), 2));
93 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 2));
94 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(3.f, 0.f)), 2));
95 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(3 ), 4));
96 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(4 ), 4));
97 }
98 {
99 // Legacy style
100 MockProperty<ScalarValue> prop(R"({
101 "a": 1,
102 "k": [
103 { "t": 1, "s": 1, "e": 2 },
104 { "t": 2, "s": 2, "e": 4 },
105 { "t": 3 }
106 ]
107 })");
108 REPORTER_ASSERT(reporter, prop);
109 REPORTER_ASSERT(reporter, !prop.isStatic());
110 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(-1), 1));
111 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 0), 1));
112 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 1 ), 1));
113 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 1.5), 1.5f));
114 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 2 ), 2));
115 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 2.5), 3));
116 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 3 ), 4));
117 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop( 4 ), 4));
118 }
119 {
120 // Legacy style hold (hard stops)
121 MockProperty<ScalarValue> prop(R"({
122 "a": 1,
123 "k": [
124 { "t": 1, "s": 1, "e": 2, "h": true },
125 { "t": 2, "s": 2, "e": 4, "h": true },
126 { "t": 3 }
127 ]
128 })");
129 REPORTER_ASSERT(reporter, prop);
130 REPORTER_ASSERT(reporter, !prop.isStatic());
131 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0 ), 1));
132 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1 ), 1));
133 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(1.5), 1));
134 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(2.f, 0.f)), 1));
135 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2 ), 2));
136 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(2.5), 2));
137 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(std::nextafter(3.f, 0.f)), 2));
138 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(3 ), 4));
139 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(4 ), 4));
140 }
141 {
142 // Static scalar prop (all equal keyframes, using float kf Value)
143 MockProperty<ScalarValue> prop(R"({
144 "a": 1,
145 "k": [
146 { "t": 1, "s": 42, "e": 42 },
147 { "t": 2, "s": 42, "e": 42 },
148 { "t": 3 }
149 ]
150 })");
151 REPORTER_ASSERT(reporter, prop);
152 REPORTER_ASSERT(reporter, prop.isStatic());
153 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0), 42));
154 }
155 {
156 // Static vector prop (all equal keyframes, using uint32 kf Value)
157 MockProperty<Vec2Value> prop(R"({
158 "a": 1,
159 "k": [
160 { "t": 1, "s": [4,2], "e": [4,2] },
161 { "t": 2, "s": [4,2], "e": [4,2] },
162 { "t": 3 }
163 ]
164 })");
165 REPORTER_ASSERT(reporter, prop);
166 REPORTER_ASSERT(reporter, prop.isStatic());
167 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0).x, 4));
168 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(prop(0).y, 2));
169 }
170 }
171