• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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