blob: f2e368dfc1980ebc712d065e64194fc2ef8305e8 [file] [log] [blame]
Florin Malita94d4d3e2018-06-18 13:10:51 -04001/*
2 * Copyright 2018 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
Florin Malitaa85f3a12018-09-24 17:24:59 -04008#include "SkMatrix.h"
Florin Malita94d4d3e2018-06-18 13:10:51 -04009#include "Skottie.h"
Florin Malitaa85f3a12018-09-24 17:24:59 -040010#include "SkottieProperty.h"
Florin Malita0d33eac2019-03-28 13:41:53 -040011#include "SkottieShaper.h"
Florin Malita94d4d3e2018-06-18 13:10:51 -040012#include "SkStream.h"
Florin Malita0d33eac2019-03-28 13:41:53 -040013#include "SkTextBlob.h"
14#include "SkTypeface.h"
Florin Malita94d4d3e2018-06-18 13:10:51 -040015
16#include "Test.h"
17
Florin Malita0d33eac2019-03-28 13:41:53 -040018#include <cmath>
Florin Malitad9c56b42018-10-09 14:33:08 -040019#include <tuple>
Florin Malitaa85f3a12018-09-24 17:24:59 -040020#include <vector>
21
22using namespace skottie;
23
Florin Malita94d4d3e2018-06-18 13:10:51 -040024DEF_TEST(Skottie_OssFuzz8956, reporter) {
Florin Malitaa85f3a12018-09-24 17:24:59 -040025 static constexpr char json[] =
Florin Malita94d4d3e2018-06-18 13:10:51 -040026 "{\"v\":\" \",\"fr\":3,\"w\":4,\"h\":3,\"layers\":[{\"ty\": 1, \"sw\": 10, \"sh\": 10,"
27 " \"sc\":\"#ffffff\", \"ks\":{\"o\":{\"a\": true, \"k\":"
28 " [{\"t\": 0, \"s\": 0, \"e\": 1, \"i\": {\"x\":[]}}]}}}]}";
29
30 SkMemoryStream stream(json, strlen(json));
31
32 // Passes if parsing doesn't crash.
Florin Malitaa85f3a12018-09-24 17:24:59 -040033 auto animation = Animation::Make(&stream);
34}
35
36DEF_TEST(Skottie_Properties, reporter) {
37 static constexpr char json[] = R"({
38 "v": "5.2.1",
39 "w": 100,
40 "h": 100,
41 "fr": 1,
42 "ip": 0,
43 "op": 1,
44 "layers": [
45 {
46 "ty": 4,
47 "nm": "layer_0",
48 "ind": 0,
49 "ip": 0,
50 "op": 1,
51 "ks": {
52 "o": { "a": 0, "k": 50 }
53 },
54 "shapes": [
55 {
56 "ty": "el",
57 "nm": "geometry_0",
58 "p": { "a": 0, "k": [ 50, 50 ] },
59 "s": { "a": 0, "k": [ 50, 50 ] }
60 },
61 {
62 "ty": "fl",
63 "nm": "fill_0",
64 "c": { "a": 0, "k": [ 1, 0, 0] }
65 },
66 {
67 "ty": "tr",
68 "nm": "shape_transform_0",
69 "o": { "a": 0, "k": 100 },
70 "s": { "a": 0, "k": [ 50, 50 ] }
71 }
72 ]
73 }
74 ]
75 })";
76
77 class TestPropertyObserver final : public PropertyObserver {
78 public:
79 struct ColorInfo {
80 SkString node_name;
81 SkColor color;
82 };
83
84 struct OpacityInfo {
85 SkString node_name;
86 float opacity;
87 };
88
89 struct TransformInfo {
Florin Malita8ac81b72018-11-28 11:39:39 -050090 SkString node_name;
91 skottie::TransformPropertyValue transform;
Florin Malitaa85f3a12018-09-24 17:24:59 -040092 };
93
94 void onColorProperty(const char node_name[],
95 const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override {
Florin Malita8ac81b72018-11-28 11:39:39 -050096 fColors.push_back({SkString(node_name), lh()->get()});
Florin Malitaa85f3a12018-09-24 17:24:59 -040097 }
98
99 void onOpacityProperty(const char node_name[],
100 const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override {
Florin Malita8ac81b72018-11-28 11:39:39 -0500101 fOpacities.push_back({SkString(node_name), lh()->get()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400102 }
103
104 void onTransformProperty(const char node_name[],
105 const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override {
Florin Malita8ac81b72018-11-28 11:39:39 -0500106 fTransforms.push_back({SkString(node_name), lh()->get()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400107 }
108
109 const std::vector<ColorInfo>& colors() const { return fColors; }
110 const std::vector<OpacityInfo>& opacities() const { return fOpacities; }
111 const std::vector<TransformInfo>& transforms() const { return fTransforms; }
112
113 private:
114 std::vector<ColorInfo> fColors;
115 std::vector<OpacityInfo> fOpacities;
116 std::vector<TransformInfo> fTransforms;
117 };
118
119 SkMemoryStream stream(json, strlen(json));
120 auto observer = sk_make_sp<TestPropertyObserver>();
121
122 auto animation = skottie::Animation::Builder()
123 .setPropertyObserver(observer)
124 .make(&stream);
125
126 REPORTER_ASSERT(reporter, animation);
127
128 const auto& colors = observer->colors();
129 REPORTER_ASSERT(reporter, colors.size() == 1);
130 REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0"));
131 REPORTER_ASSERT(reporter, colors[0].color == 0xffff0000);
132
133 const auto& opacities = observer->opacities();
134 REPORTER_ASSERT(reporter, opacities.size() == 2);
135 REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0"));
136 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].opacity, 100));
137 REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0"));
138 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].opacity, 50));
139
140 const auto& transforms = observer->transforms();
141 REPORTER_ASSERT(reporter, transforms.size() == 2);
142 REPORTER_ASSERT(reporter, transforms[0].node_name.equals("shape_transform_0"));
Florin Malita8ac81b72018-11-28 11:39:39 -0500143 REPORTER_ASSERT(reporter, transforms[0].transform == skottie::TransformPropertyValue({
144 SkPoint::Make(0, 0),
145 SkPoint::Make(0, 0),
146 SkVector::Make(50, 50),
147 0,
148 0,
149 0
150 }));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400151 REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_0"));
Florin Malita8ac81b72018-11-28 11:39:39 -0500152 REPORTER_ASSERT(reporter, transforms[1].transform == skottie::TransformPropertyValue({
153 SkPoint::Make(0, 0),
154 SkPoint::Make(0, 0),
155 SkVector::Make(100, 100),
156 0,
157 0,
158 0
159 }));
Florin Malita94d4d3e2018-06-18 13:10:51 -0400160}
Florin Malitad9c56b42018-10-09 14:33:08 -0400161
162DEF_TEST(Skottie_Annotations, reporter) {
163 static constexpr char json[] = R"({
164 "v": "5.2.1",
165 "w": 100,
166 "h": 100,
Florin Malita91af8d82018-11-30 16:46:45 -0500167 "fr": 10,
Florin Malitad9c56b42018-10-09 14:33:08 -0400168 "ip": 0,
Florin Malita91af8d82018-11-30 16:46:45 -0500169 "op": 100,
Florin Malitad9c56b42018-10-09 14:33:08 -0400170 "layers": [
171 {
172 "ty": 1,
173 "ind": 0,
174 "ip": 0,
175 "op": 1,
176 "ks": {
177 "o": { "a": 0, "k": 50 }
178 },
179 "sw": 100,
180 "sh": 100,
181 "sc": "#ffffff"
182 }
183 ],
Florin Malita91af8d82018-11-30 16:46:45 -0500184 "markers": [
185 {
186 "cm": "marker_1",
187 "dr": 25,
188 "tm": 25
189 },
190 {
191 "cm": "marker_2",
192 "dr": 0,
193 "tm": 75
194 }
195 ]
Florin Malitad9c56b42018-10-09 14:33:08 -0400196 })";
197
Florin Malita91af8d82018-11-30 16:46:45 -0500198 class TestMarkerObserver final : public MarkerObserver {
Florin Malitad9c56b42018-10-09 14:33:08 -0400199 public:
Florin Malita91af8d82018-11-30 16:46:45 -0500200 void onMarker(const char name[], float t0, float t1) override {
201 fMarkers.push_back(std::make_tuple(name, t0, t1));
Florin Malitad9c56b42018-10-09 14:33:08 -0400202 }
203
Florin Malita91af8d82018-11-30 16:46:45 -0500204 std::vector<std::tuple<std::string, float, float>> fMarkers;
Florin Malitad9c56b42018-10-09 14:33:08 -0400205 };
206
207 SkMemoryStream stream(json, strlen(json));
Florin Malita91af8d82018-11-30 16:46:45 -0500208 auto observer = sk_make_sp<TestMarkerObserver>();
Florin Malitad9c56b42018-10-09 14:33:08 -0400209
210 auto animation = skottie::Animation::Builder()
Florin Malita91af8d82018-11-30 16:46:45 -0500211 .setMarkerObserver(observer)
Florin Malitad9c56b42018-10-09 14:33:08 -0400212 .make(&stream);
213
214 REPORTER_ASSERT(reporter, animation);
215
Florin Malita91af8d82018-11-30 16:46:45 -0500216 REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul);
217 REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1");
218 REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f);
219 REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f);
220 REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2");
221 REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f);
222 REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f);
Florin Malitad9c56b42018-10-09 14:33:08 -0400223}
Florin Malita0d33eac2019-03-28 13:41:53 -0400224
225DEF_TEST(Skottie_Shaper, reporter) {
226 auto typeface = SkTypeface::MakeDefault();
227 REPORTER_ASSERT(reporter, typeface);
228
229 static constexpr struct {
230 SkScalar text_size,
231 tolerance;
232 } kTestSizes[] = {
Florin Malita20678f82019-03-28 18:35:44 -0400233 // These gross tolerances are required for the test to pass on NativeFonts bots.
234 // Might be worth investigating why we need so much slack.
235 { 5, 2.0f },
236 { 10, 2.0f },
237 { 15, 2.4f },
238 { 25, 4.4f },
Florin Malita0d33eac2019-03-28 13:41:53 -0400239 };
240
241 static constexpr struct {
242 SkTextUtils::Align align;
243 SkScalar l_selector,
244 r_selector;
245 } kTestAligns[] = {
246 { SkTextUtils:: kLeft_Align, 0.0f, 1.0f },
247 { SkTextUtils::kCenter_Align, 0.5f, 0.5f },
248 { SkTextUtils:: kRight_Align, 1.0f, 0.0f },
249 };
250
251 const SkString text("Foo, bar.\rBaz.");
252 const SkPoint text_point = SkPoint::Make(100, 100);
253
254 for (const auto& tsize : kTestSizes) {
255 for (const auto& talign : kTestAligns) {
256 const skottie::Shaper::TextDesc desc = {
257 typeface,
258 tsize.text_size,
259 talign.align,
260 };
261
262 const auto shape_result = skottie::Shaper::Shape(text, desc, text_point);
263 REPORTER_ASSERT(reporter, shape_result.fBlob);
264
265 const auto shape_bounds = shape_result.computeBounds();
266 REPORTER_ASSERT(reporter, !shape_bounds.isEmpty());
267
268 const auto expected_l = text_point.x() - shape_bounds.width() * talign.l_selector;
269 REPORTER_ASSERT(reporter,
270 std::fabs(shape_bounds.left() - expected_l) < tsize.tolerance);
271
272 const auto expected_r = text_point.x() + shape_bounds.width() * talign.r_selector;
273 REPORTER_ASSERT(reporter,
274 std::fabs(shape_bounds.right() - expected_r) < tsize.tolerance);
275
276 }
277 }
278}