1 /*
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
8 #include "include/core/SkFontMgr.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkStream.h"
11 #include "include/core/SkTypeface.h"
12 #include "modules/skottie/include/Skottie.h"
13 #include "modules/skottie/include/SkottieProperty.h"
14 #include "src/core/SkFontDescriptor.h"
15 #include "tests/Test.h"
16 #include "tools/ToolUtils.h"
17
18 #include <cmath>
19 #include <string>
20 #include <tuple>
21 #include <vector>
22
23 using namespace skottie;
24
DEF_TEST(Skottie_OssFuzz8956,reporter)25 DEF_TEST(Skottie_OssFuzz8956, reporter) {
26 static constexpr char json[] =
27 "{\"v\":\" \",\"fr\":3,\"w\":4,\"h\":3,\"layers\":[{\"ty\": 1, \"sw\": 10, \"sh\": 10,"
28 " \"sc\":\"#ffffff\", \"ks\":{\"o\":{\"a\": true, \"k\":"
29 " [{\"t\": 0, \"s\": 0, \"e\": 1, \"i\": {\"x\":[]}}]}}}]}";
30
31 SkMemoryStream stream(json, strlen(json));
32
33 // Passes if parsing doesn't crash.
34 auto animation = Animation::Make(&stream);
35 }
36
DEF_TEST(Skottie_Properties,reporter)37 DEF_TEST(Skottie_Properties, reporter) {
38 auto test_typeface = ToolUtils::create_portable_typeface();
39 REPORTER_ASSERT(reporter, test_typeface);
40
41 static const char json[] = R"({
42 "v": "5.2.1",
43 "w": 100,
44 "h": 100,
45 "fr": 1,
46 "ip": 0,
47 "op": 1,
48 "fonts": {
49 "list": [
50 {
51 "fName": "test_font",
52 "fFamily": "test-family",
53 "fStyle": "TestFontStyle"
54 }
55 ]
56 },
57 "layers": [
58 {
59 "ty": 4,
60 "nm": "layer_0",
61 "ind": 0,
62 "ip": 0,
63 "op": 1,
64 "ks": {
65 "o": { "a": 0, "k": 50 }
66 },
67 "ef": [{
68 "ef": [
69 {},
70 {},
71 { "v": { "a": 0, "k": [ 0, 1, 0 ] }},
72 {},
73 {},
74 {},
75 { "v": { "a": 0, "k": 1 }}
76 ],
77 "nm": "fill_effect_0",
78 "mn": "ADBE Fill",
79 "ty": 21
80 }],
81 "shapes": [
82 {
83 "ty": "el",
84 "nm": "geometry_0",
85 "p": { "a": 0, "k": [ 50, 50 ] },
86 "s": { "a": 0, "k": [ 50, 50 ] }
87 },
88 {
89 "ty": "fl",
90 "nm": "fill_0",
91 "c": { "a": 0, "k": [ 1, 0, 0] }
92 },
93 {
94 "ty": "tr",
95 "nm": "shape_transform_0",
96 "o": { "a": 0, "k": 100 },
97 "s": { "a": 0, "k": [ 50, 50 ] }
98 }
99 ]
100 },
101 {
102 "ty": 5,
103 "nm": "layer_1",
104 "ip": 0,
105 "op": 1,
106 "ks": {
107 "p": { "a": 0, "k": [25, 25] }
108 },
109 "t": {
110 "d": {
111 "k": [
112 {
113 "t": 0,
114 "s": {
115 "f": "test_font",
116 "s": 100,
117 "t": "inline_text",
118 "lh": 120,
119 "ls": 12
120 }
121 }
122 ]
123 }
124 }
125 }
126 ]
127 })";
128
129
130 class TestPropertyObserver final : public PropertyObserver {
131 public:
132 struct ColorInfo {
133 SkString node_name;
134 std::unique_ptr<skottie::ColorPropertyHandle> handle;
135 };
136
137 struct OpacityInfo {
138 SkString node_name;
139 std::unique_ptr<skottie::OpacityPropertyHandle> handle;
140 };
141
142 struct TextInfo {
143 SkString node_name;
144 std::unique_ptr<skottie::TextPropertyHandle> handle;
145 };
146
147 struct TransformInfo {
148 SkString node_name;
149 std::unique_ptr<skottie::TransformPropertyHandle> handle;
150 };
151
152 void onColorProperty(const char node_name[],
153 const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override {
154 fColors.push_back({SkString(node_name), lh()});
155 fColorsWithFullKeypath.push_back({SkString(fCurrentNode.c_str()), lh()});
156 }
157
158 void onOpacityProperty(const char node_name[],
159 const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override {
160 fOpacities.push_back({SkString(node_name), lh()});
161 }
162
163 void onTextProperty(const char node_name[],
164 const PropertyObserver::LazyHandle<TextPropertyHandle>& lh) override {
165 fTexts.push_back({SkString(node_name), lh()});
166 }
167
168 void onTransformProperty(const char node_name[],
169 const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override {
170 fTransforms.push_back({SkString(node_name), lh()});
171 }
172
173 void onEnterNode(const char node_name[], PropertyObserver::NodeType node_type) override {
174 if (node_name == nullptr) {
175 return;
176 }
177 fCurrentNode = fCurrentNode.empty() ? node_name : fCurrentNode + "." + node_name;
178 }
179
180 void onLeavingNode(const char node_name[], PropertyObserver::NodeType node_type) override {
181 if (node_name == nullptr) {
182 return;
183 }
184 auto length = strlen(node_name);
185 fCurrentNode =
186 fCurrentNode.length() > length
187 ? fCurrentNode.substr(0, fCurrentNode.length() - strlen(node_name) - 1)
188 : "";
189 }
190
191 const std::vector<ColorInfo>& colors() const { return fColors; }
192 const std::vector<OpacityInfo>& opacities() const { return fOpacities; }
193 const std::vector<TextInfo>& texts() const { return fTexts; }
194 const std::vector<TransformInfo>& transforms() const { return fTransforms; }
195 const std::vector<ColorInfo>& colorsWithFullKeypath() const {
196 return fColorsWithFullKeypath;
197 }
198
199 private:
200 std::vector<ColorInfo> fColors;
201 std::vector<OpacityInfo> fOpacities;
202 std::vector<TextInfo> fTexts;
203 std::vector<TransformInfo> fTransforms;
204 std::string fCurrentNode;
205 std::vector<ColorInfo> fColorsWithFullKeypath;
206 };
207
208 // Returns a single specified typeface for all requests.
209 class FakeFontMgr : public SkFontMgr {
210 public:
211 FakeFontMgr(sk_sp<SkTypeface> test_font) : fTestFont(test_font) {}
212
213 int onCountFamilies() const override { return 1; }
214 void onGetFamilyName(int index, SkString* familyName) const override {}
215 SkFontStyleSet* onCreateStyleSet(int index) const override { return nullptr; }
216 SkFontStyleSet* onMatchFamily(const char familyName[]) const override { return nullptr; }
217 SkTypeface* onMatchFamilyStyle(const char familyName[],
218 const SkFontStyle& fontStyle) const override {
219 return nullptr;
220 }
221 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
222 const char* bcp47[], int bcp47Count,
223 SkUnichar character) const override {
224 return nullptr;
225 }
226 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override {
227 return fTestFont;
228 }
229 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>,
230 int ttcIndex) const override {
231 return fTestFont;
232 }
233 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>,
234 const SkFontArguments&) const override {
235 return fTestFont;
236 }
237 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
238 return fTestFont;
239 }
240 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override {
241 return fTestFont;
242 }
243 private:
244 sk_sp<SkTypeface> fTestFont;
245 };
246
247 sk_sp<FakeFontMgr> test_font_manager = sk_make_sp<FakeFontMgr>(test_typeface);
248 SkMemoryStream stream(json, strlen(json));
249 auto observer = sk_make_sp<TestPropertyObserver>();
250
251 auto animation = skottie::Animation::Builder()
252 .setPropertyObserver(observer)
253 .setFontManager(test_font_manager)
254 .make(&stream);
255
256 REPORTER_ASSERT(reporter, animation);
257
258 const auto& colors = observer->colors();
259 REPORTER_ASSERT(reporter, colors.size() == 2);
260 REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0"));
261 REPORTER_ASSERT(reporter, colors[0].handle->get() == 0xffff0000);
262 REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0"));
263 REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00);
264
265 const auto& colorsWithFullKeypath = observer->colorsWithFullKeypath();
266 REPORTER_ASSERT(reporter, colorsWithFullKeypath.size() == 2);
267 REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].node_name.equals("layer_0.fill_0"));
268 REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].handle->get() == 0xffff0000);
269 REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].node_name.equals("layer_0.fill_effect_0"));
270 REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].handle->get() == 0xff00ff00);
271
272 const auto& opacities = observer->opacities();
273 REPORTER_ASSERT(reporter, opacities.size() == 3);
274 REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0"));
275 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].handle->get(), 100));
276 REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0"));
277 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50));
278
279 const auto& transforms = observer->transforms();
280 REPORTER_ASSERT(reporter, transforms.size() == 3);
281 REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0"));
282 REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({
283 SkPoint::Make(0, 0),
284 SkPoint::Make(0, 0),
285 SkVector::Make(100, 100),
286 0,
287 0,
288 0
289 }));
290 REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_1"));
291 REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({
292 SkPoint::Make(0, 0),
293 SkPoint::Make(25, 25),
294 SkVector::Make(100, 100),
295 0,
296 0,
297 0
298 }));
299 REPORTER_ASSERT(reporter, transforms[2].node_name.equals("shape_transform_0"));
300 REPORTER_ASSERT(reporter, transforms[2].handle->get() == skottie::TransformPropertyValue({
301 SkPoint::Make(0, 0),
302 SkPoint::Make(0, 0),
303 SkVector::Make(50, 50),
304 0,
305 0,
306 0
307 }));
308
309 const auto& texts = observer->texts();
310 REPORTER_ASSERT(reporter, texts.size() == 1);
311 REPORTER_ASSERT(reporter, texts[0].node_name.equals("layer_1"));
312 REPORTER_ASSERT(reporter, texts[0].handle->get() == skottie::TextPropertyValue({
313 test_typeface,
314 SkString("inline_text"),
315 100,
316 0, 100,
317 0,
318 120,
319 12,
320 0,
321 0,
322 SkTextUtils::kLeft_Align,
323 Shaper::VAlign::kTopBaseline,
324 Shaper::ResizePolicy::kNone,
325 Shaper::LinebreakPolicy::kExplicit,
326 Shaper::Direction::kLTR,
327 Shaper::Capitalization::kNone,
328 SkRect::MakeEmpty(),
329 SK_ColorTRANSPARENT,
330 SK_ColorTRANSPARENT,
331 TextPaintOrder::kFillStroke,
332 SkPaint::Join::kDefault_Join,
333 false,
334 false,
335 nullptr
336 }));
337 }
338
DEF_TEST(Skottie_Annotations,reporter)339 DEF_TEST(Skottie_Annotations, reporter) {
340 static constexpr char json[] = R"({
341 "v": "5.2.1",
342 "w": 100,
343 "h": 100,
344 "fr": 10,
345 "ip": 0,
346 "op": 100,
347 "layers": [
348 {
349 "ty": 1,
350 "ind": 0,
351 "ip": 0,
352 "op": 1,
353 "ks": {
354 "o": { "a": 0, "k": 50 }
355 },
356 "sw": 100,
357 "sh": 100,
358 "sc": "#ffffff"
359 }
360 ],
361 "markers": [
362 {
363 "cm": "marker_1",
364 "dr": 25,
365 "tm": 25
366 },
367 {
368 "cm": "marker_2",
369 "dr": 0,
370 "tm": 75
371 }
372 ]
373 })";
374
375 class TestMarkerObserver final : public MarkerObserver {
376 public:
377 void onMarker(const char name[], float t0, float t1) override {
378 fMarkers.push_back(std::make_tuple(name, t0, t1));
379 }
380
381 std::vector<std::tuple<std::string, float, float>> fMarkers;
382 };
383
384 SkMemoryStream stream(json, strlen(json));
385 auto observer = sk_make_sp<TestMarkerObserver>();
386
387 auto animation = skottie::Animation::Builder()
388 .setMarkerObserver(observer)
389 .make(&stream);
390
391 REPORTER_ASSERT(reporter, animation);
392 REPORTER_ASSERT(reporter, animation->duration() == 10);
393 REPORTER_ASSERT(reporter, animation->inPoint() == 0.0);
394 REPORTER_ASSERT(reporter, animation->outPoint() == 100.0);
395
396 REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul);
397 REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1");
398 REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f);
399 REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f);
400 REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2");
401 REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f);
402 REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f);
403 }
404
DEF_TEST(Skottie_Image_Loading,reporter)405 DEF_TEST(Skottie_Image_Loading, reporter) {
406 class TestResourceProvider final : public skresources::ResourceProvider {
407 public:
408 TestResourceProvider(sk_sp<skresources::ImageAsset> single_asset,
409 sk_sp<skresources::ImageAsset> multi_asset)
410 : fSingleFrameAsset(std::move(single_asset))
411 , fMultiFrameAsset (std::move( multi_asset)) {}
412
413 private:
414 sk_sp<ImageAsset> loadImageAsset(const char path[],
415 const char name[],
416 const char id[]) const override {
417 return strcmp(id, "single_frame")
418 ? fMultiFrameAsset
419 : fSingleFrameAsset;
420 }
421
422 const sk_sp<skresources::ImageAsset> fSingleFrameAsset,
423 fMultiFrameAsset;
424 };
425
426 auto make_animation = [&reporter] (sk_sp<skresources::ImageAsset> single_asset,
427 sk_sp<skresources::ImageAsset> multi_asset,
428 bool deferred_image_loading) {
429 static constexpr char json[] = R"({
430 "v": "5.2.1",
431 "w": 100,
432 "h": 100,
433 "fr": 10,
434 "ip": 0,
435 "op": 100,
436 "assets": [
437 {
438 "id": "single_frame",
439 "p" : "single_frame.png",
440 "u" : "images/",
441 "w" : 500,
442 "h" : 500
443 },
444 {
445 "id": "multi_frame",
446 "p" : "multi_frame.png",
447 "u" : "images/",
448 "w" : 500,
449 "h" : 500
450 }
451 ],
452 "layers": [
453 {
454 "ty": 2,
455 "refId": "single_frame",
456 "ind": 0,
457 "ip": 0,
458 "op": 100,
459 "ks": {}
460 },
461 {
462 "ty": 2,
463 "refId": "multi_frame",
464 "ind": 1,
465 "ip": 0,
466 "op": 100,
467 "ks": {}
468 }
469 ]
470 })";
471
472 SkMemoryStream stream(json, strlen(json));
473
474 const auto flags = deferred_image_loading
475 ? static_cast<uint32_t>(skottie::Animation::Builder::kDeferImageLoading)
476 : 0;
477 auto animation =
478 skottie::Animation::Builder(flags)
479 .setResourceProvider(sk_make_sp<TestResourceProvider>(std::move(single_asset),
480 std::move( multi_asset)))
481 .make(&stream);
482
483 REPORTER_ASSERT(reporter, animation);
484
485 return animation;
486 };
487
488 class TestAsset final : public skresources::ImageAsset {
489 public:
490 explicit TestAsset(bool multi_frame) : fMultiFrame(multi_frame) {}
491
492 const std::vector<float>& requestedFrames() const { return fRequestedFrames; }
493
494 private:
495 bool isMultiFrame() override { return fMultiFrame; }
496
497 sk_sp<SkImage> getFrame(float t) override {
498 fRequestedFrames.push_back(t);
499
500 return SkSurface::MakeRasterN32Premul(10, 10)->makeImageSnapshot();
501 }
502
503 const bool fMultiFrame;
504
505 std::vector<float> fRequestedFrames;
506 };
507
508 {
509 auto single_asset = sk_make_sp<TestAsset>(false),
510 multi_asset = sk_make_sp<TestAsset>(true);
511
512 // Default image loading: single-frame images are loaded upfront, multi-frame images are
513 // loaded on-demand.
514 auto animation = make_animation(single_asset, multi_asset, false);
515
516 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
517 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0);
518 REPORTER_ASSERT(reporter, SkScalarNearlyZero(single_asset->requestedFrames()[0]));
519
520 animation->seekFrameTime(1);
521 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
522 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1);
523 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[0], 1));
524
525 animation->seekFrameTime(2);
526 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
527 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2);
528 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2));
529 }
530
531 {
532 auto single_asset = sk_make_sp<TestAsset>(false),
533 multi_asset = sk_make_sp<TestAsset>(true);
534
535 // Deferred image loading: both single-frame and multi-frame images are loaded on-demand.
536 auto animation = make_animation(single_asset, multi_asset, true);
537
538 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 0);
539 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0);
540
541 animation->seekFrameTime(1);
542 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
543 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1);
544 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(single_asset->requestedFrames()[0], 1));
545 REPORTER_ASSERT(reporter, SkScalarNearlyEqual (multi_asset->requestedFrames()[0], 1));
546
547 animation->seekFrameTime(2);
548 REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1);
549 REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2);
550 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2));
551 }
552 }
553
DEF_TEST(Skottie_Layer_NoType,r)554 DEF_TEST(Skottie_Layer_NoType, r) {
555 static constexpr char json[] =
556 R"({
557 "v": "5.2.1",
558 "w": 100,
559 "h": 100,
560 "fr": 10,
561 "ip": 0,
562 "op": 100,
563 "layers": [
564 {
565 "ind": 0,
566 "ip": 0,
567 "op": 100,
568 "ks": {}
569 }
570 ]
571 })";
572
573 SkMemoryStream stream(json, strlen(json));
574 auto anim = Animation::Make(&stream);
575
576 // passes if we don't crash
577 REPORTER_ASSERT(r, anim);
578 }
579