Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 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 | |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 8 | #include "include/core/SkFontMgr.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 9 | #include "include/core/SkMatrix.h" |
| 10 | #include "include/core/SkStream.h" |
| 11 | #include "include/core/SkTextBlob.h" |
| 12 | #include "include/core/SkTypeface.h" |
| 13 | #include "modules/skottie/include/Skottie.h" |
| 14 | #include "modules/skottie/include/SkottieProperty.h" |
Florin Malita | ca449d5 | 2019-05-08 10:04:09 -0400 | [diff] [blame] | 15 | #include "modules/skottie/src/text/SkottieShaper.h" |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 16 | #include "src/core/SkFontDescriptor.h" |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 17 | #include "src/core/SkTextBlobPriv.h" |
Mike Klein | c0bd9f9 | 2019-04-23 12:05:21 -0500 | [diff] [blame] | 18 | #include "tests/Test.h" |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 19 | #include "tools/ToolUtils.h" |
Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 20 | |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 21 | #include <cmath> |
Mike Klein | 48e08aa | 2019-08-26 11:24:50 -0500 | [diff] [blame] | 22 | #include <string> |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 23 | #include <tuple> |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 24 | #include <vector> |
| 25 | |
| 26 | using namespace skottie; |
| 27 | |
Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 28 | DEF_TEST(Skottie_OssFuzz8956, reporter) { |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 29 | static constexpr char json[] = |
Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 30 | "{\"v\":\" \",\"fr\":3,\"w\":4,\"h\":3,\"layers\":[{\"ty\": 1, \"sw\": 10, \"sh\": 10," |
| 31 | " \"sc\":\"#ffffff\", \"ks\":{\"o\":{\"a\": true, \"k\":" |
| 32 | " [{\"t\": 0, \"s\": 0, \"e\": 1, \"i\": {\"x\":[]}}]}}}]}"; |
| 33 | |
| 34 | SkMemoryStream stream(json, strlen(json)); |
| 35 | |
| 36 | // Passes if parsing doesn't crash. |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 37 | auto animation = Animation::Make(&stream); |
| 38 | } |
| 39 | |
| 40 | DEF_TEST(Skottie_Properties, reporter) { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 41 | auto test_typeface = ToolUtils::create_portable_typeface(); |
| 42 | REPORTER_ASSERT(reporter, test_typeface); |
| 43 | |
| 44 | static const char json[] = R"({ |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 45 | "v": "5.2.1", |
| 46 | "w": 100, |
| 47 | "h": 100, |
| 48 | "fr": 1, |
| 49 | "ip": 0, |
| 50 | "op": 1, |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 51 | "fonts": { |
| 52 | "list": [ |
| 53 | { |
| 54 | "fName": "test_font", |
| 55 | "fFamily": "test-family", |
| 56 | "fStyle": "TestFontStyle" |
| 57 | } |
| 58 | ] |
| 59 | }, |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 60 | "layers": [ |
| 61 | { |
| 62 | "ty": 4, |
| 63 | "nm": "layer_0", |
| 64 | "ind": 0, |
| 65 | "ip": 0, |
| 66 | "op": 1, |
| 67 | "ks": { |
| 68 | "o": { "a": 0, "k": 50 } |
| 69 | }, |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 70 | "ef": [{ |
| 71 | "ef": [ |
| 72 | {}, |
| 73 | {}, |
| 74 | { "v": { "a": 0, "k": [ 0, 1, 0 ] }}, |
| 75 | {}, |
| 76 | {}, |
| 77 | {}, |
| 78 | { "v": { "a": 0, "k": 1 }} |
| 79 | ], |
| 80 | "nm": "fill_effect_0", |
Florin Malita | 42725ae | 2019-12-16 12:58:52 -0500 | [diff] [blame] | 81 | "mn": "ADBE Fill", |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 82 | "ty": 21 |
| 83 | }], |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 84 | "shapes": [ |
| 85 | { |
| 86 | "ty": "el", |
| 87 | "nm": "geometry_0", |
| 88 | "p": { "a": 0, "k": [ 50, 50 ] }, |
| 89 | "s": { "a": 0, "k": [ 50, 50 ] } |
| 90 | }, |
| 91 | { |
| 92 | "ty": "fl", |
| 93 | "nm": "fill_0", |
| 94 | "c": { "a": 0, "k": [ 1, 0, 0] } |
| 95 | }, |
| 96 | { |
| 97 | "ty": "tr", |
| 98 | "nm": "shape_transform_0", |
| 99 | "o": { "a": 0, "k": 100 }, |
| 100 | "s": { "a": 0, "k": [ 50, 50 ] } |
| 101 | } |
| 102 | ] |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 103 | }, |
| 104 | { |
| 105 | "ty": 5, |
| 106 | "nm": "layer_1", |
| 107 | "ip": 0, |
| 108 | "op": 1, |
| 109 | "ks": { |
| 110 | "p": { "a": 0, "k": [25, 25] } |
| 111 | }, |
| 112 | "t": { |
| 113 | "d": { |
| 114 | "k": [ |
| 115 | { |
| 116 | "t": 0, |
| 117 | "s": { |
| 118 | "f": "test_font", |
| 119 | "s": 100, |
| 120 | "t": "inline_text", |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 121 | "lh": 120, |
| 122 | "ls": 12 |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 123 | } |
| 124 | } |
| 125 | ] |
| 126 | } |
| 127 | } |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 128 | } |
| 129 | ] |
Greg Daniel | 08ada8e | 2019-08-05 20:00:41 +0000 | [diff] [blame] | 130 | })"; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 131 | |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 132 | |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 133 | class TestPropertyObserver final : public PropertyObserver { |
| 134 | public: |
| 135 | struct ColorInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 136 | SkString node_name; |
| 137 | std::unique_ptr<skottie::ColorPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 138 | }; |
| 139 | |
| 140 | struct OpacityInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 141 | SkString node_name; |
| 142 | std::unique_ptr<skottie::OpacityPropertyHandle> handle; |
| 143 | }; |
| 144 | |
| 145 | struct TextInfo { |
| 146 | SkString node_name; |
| 147 | std::unique_ptr<skottie::TextPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 148 | }; |
| 149 | |
| 150 | struct TransformInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 151 | SkString node_name; |
| 152 | std::unique_ptr<skottie::TransformPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 153 | }; |
| 154 | |
| 155 | void onColorProperty(const char node_name[], |
| 156 | const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 157 | fColors.push_back({SkString(node_name), lh()}); |
Shachar Langbeheim | be28d2e | 2020-02-11 09:48:50 +0200 | [diff] [blame] | 158 | fColorsWithFullKeypath.push_back({SkString(fCurrentNode.c_str()), lh()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | void onOpacityProperty(const char node_name[], |
| 162 | const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 163 | fOpacities.push_back({SkString(node_name), lh()}); |
| 164 | } |
| 165 | |
| 166 | void onTextProperty(const char node_name[], |
| 167 | const PropertyObserver::LazyHandle<TextPropertyHandle>& lh) override { |
| 168 | fTexts.push_back({SkString(node_name), lh()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | void onTransformProperty(const char node_name[], |
| 172 | const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 173 | fTransforms.push_back({SkString(node_name), lh()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 174 | } |
| 175 | |
Shachar Langbeheim | be28d2e | 2020-02-11 09:48:50 +0200 | [diff] [blame] | 176 | void onEnterNode(const char node_name[]) override { |
| 177 | fCurrentNode = fCurrentNode.empty() ? node_name : fCurrentNode + "." + node_name; |
| 178 | } |
| 179 | |
| 180 | void onLeavingNode(const char node_name[]) override { |
| 181 | auto length = strlen(node_name); |
| 182 | fCurrentNode = |
| 183 | fCurrentNode.length() > length |
| 184 | ? fCurrentNode.substr(0, fCurrentNode.length() - strlen(node_name) - 1) |
| 185 | : ""; |
| 186 | } |
| 187 | |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 188 | const std::vector<ColorInfo>& colors() const { return fColors; } |
| 189 | const std::vector<OpacityInfo>& opacities() const { return fOpacities; } |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 190 | const std::vector<TextInfo>& texts() const { return fTexts; } |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 191 | const std::vector<TransformInfo>& transforms() const { return fTransforms; } |
Shachar Langbeheim | be28d2e | 2020-02-11 09:48:50 +0200 | [diff] [blame] | 192 | const std::vector<ColorInfo>& colorsWithFullKeypath() const { |
| 193 | return fColorsWithFullKeypath; |
| 194 | } |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 195 | |
| 196 | private: |
| 197 | std::vector<ColorInfo> fColors; |
| 198 | std::vector<OpacityInfo> fOpacities; |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 199 | std::vector<TextInfo> fTexts; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 200 | std::vector<TransformInfo> fTransforms; |
Shachar Langbeheim | be28d2e | 2020-02-11 09:48:50 +0200 | [diff] [blame] | 201 | std::string fCurrentNode; |
| 202 | std::vector<ColorInfo> fColorsWithFullKeypath; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 203 | }; |
| 204 | |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 205 | // Returns a single specified typeface for all requests. |
| 206 | class DummyFontMgr : public SkFontMgr { |
| 207 | public: |
| 208 | DummyFontMgr(sk_sp<SkTypeface> test_font) : fTestFont(test_font) {} |
| 209 | |
| 210 | int onCountFamilies() const override { return 1; } |
| 211 | void onGetFamilyName(int index, SkString* familyName) const override {} |
| 212 | SkFontStyleSet* onCreateStyleSet(int index) const override { return nullptr; } |
| 213 | SkFontStyleSet* onMatchFamily(const char familyName[]) const override { return nullptr; } |
| 214 | SkTypeface* onMatchFamilyStyle(const char familyName[], |
| 215 | const SkFontStyle& fontStyle) const override { |
| 216 | return nullptr; |
| 217 | } |
| 218 | SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, |
| 219 | const char* bcp47[], int bcp47Count, |
| 220 | SkUnichar character) const override { |
| 221 | return nullptr; |
| 222 | } |
| 223 | SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) 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> onMakeFromFontData(std::unique_ptr<SkFontData>) const override { |
| 238 | return fTestFont; |
| 239 | } |
| 240 | sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override { |
| 241 | return fTestFont; |
| 242 | } |
| 243 | sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override { |
| 244 | return fTestFont; |
| 245 | } |
| 246 | private: |
| 247 | sk_sp<SkTypeface> fTestFont; |
| 248 | }; |
| 249 | |
| 250 | sk_sp<DummyFontMgr> test_font_manager = sk_make_sp<DummyFontMgr>(test_typeface); |
Greg Daniel | 08ada8e | 2019-08-05 20:00:41 +0000 | [diff] [blame] | 251 | SkMemoryStream stream(json, strlen(json)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 252 | auto observer = sk_make_sp<TestPropertyObserver>(); |
| 253 | |
| 254 | auto animation = skottie::Animation::Builder() |
| 255 | .setPropertyObserver(observer) |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 256 | .setFontManager(test_font_manager) |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 257 | .make(&stream); |
| 258 | |
| 259 | REPORTER_ASSERT(reporter, animation); |
| 260 | |
| 261 | const auto& colors = observer->colors(); |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 262 | REPORTER_ASSERT(reporter, colors.size() == 2); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 263 | REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 264 | REPORTER_ASSERT(reporter, colors[0].handle->get() == 0xffff0000); |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 265 | REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 266 | REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 267 | |
Shachar Langbeheim | be28d2e | 2020-02-11 09:48:50 +0200 | [diff] [blame] | 268 | const auto& colorsWithFullKeypath = observer->colorsWithFullKeypath(); |
| 269 | REPORTER_ASSERT(reporter, colorsWithFullKeypath.size() == 2); |
| 270 | REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].node_name.equals("layer_0.fill_0")); |
| 271 | REPORTER_ASSERT(reporter, colorsWithFullKeypath[0].handle->get() == 0xffff0000); |
| 272 | REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].node_name.equals("layer_0.fill_effect_0")); |
| 273 | REPORTER_ASSERT(reporter, colorsWithFullKeypath[1].handle->get() == 0xff00ff00); |
| 274 | |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 275 | const auto& opacities = observer->opacities(); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 276 | REPORTER_ASSERT(reporter, opacities.size() == 3); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 277 | REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 278 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].handle->get(), 100)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 279 | REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 280 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 281 | |
| 282 | const auto& transforms = observer->transforms(); |
Florin Malita | c6fbedc | 2019-10-28 13:57:12 -0400 | [diff] [blame] | 283 | REPORTER_ASSERT(reporter, transforms.size() == 3); |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 284 | REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 285 | REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({ |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 286 | SkPoint::Make(0, 0), |
| 287 | SkPoint::Make(0, 0), |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 288 | SkVector::Make(100, 100), |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 289 | 0, |
| 290 | 0, |
| 291 | 0 |
| 292 | })); |
Florin Malita | c6fbedc | 2019-10-28 13:57:12 -0400 | [diff] [blame] | 293 | REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_1")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 294 | REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({ |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 295 | SkPoint::Make(0, 0), |
Florin Malita | c6fbedc | 2019-10-28 13:57:12 -0400 | [diff] [blame] | 296 | SkPoint::Make(25, 25), |
| 297 | SkVector::Make(100, 100), |
| 298 | 0, |
| 299 | 0, |
| 300 | 0 |
| 301 | })); |
| 302 | REPORTER_ASSERT(reporter, transforms[2].node_name.equals("shape_transform_0")); |
| 303 | REPORTER_ASSERT(reporter, transforms[2].handle->get() == skottie::TransformPropertyValue({ |
| 304 | SkPoint::Make(0, 0), |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 305 | SkPoint::Make(0, 0), |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 306 | SkVector::Make(50, 50), |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 307 | 0, |
| 308 | 0, |
| 309 | 0 |
| 310 | })); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 311 | |
| 312 | const auto& texts = observer->texts(); |
| 313 | REPORTER_ASSERT(reporter, texts.size() == 1); |
| 314 | REPORTER_ASSERT(reporter, texts[0].node_name.equals("layer_1")); |
| 315 | REPORTER_ASSERT(reporter, texts[0].handle->get() == skottie::TextPropertyValue({ |
| 316 | test_typeface, |
| 317 | SkString("inline_text"), |
| 318 | 100, |
| 319 | 0, |
| 320 | 120, |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 321 | 12, |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 322 | 0, |
| 323 | SkTextUtils::kLeft_Align, |
| 324 | Shaper::VAlign::kTopBaseline, |
Florin Malita | ad91100 | 2020-01-28 13:21:47 -0500 | [diff] [blame] | 325 | Shaper::ResizePolicy::kNone, |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 326 | Shaper::LinebreakPolicy::kExplicit, |
Florin Malita | ebbacd8 | 2020-11-13 13:38:39 -0500 | [diff] [blame] | 327 | Shaper::Direction::kLTR, |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 328 | SkRect::MakeEmpty(), |
| 329 | SK_ColorTRANSPARENT, |
| 330 | SK_ColorTRANSPARENT, |
Florin Malita | 082323b | 2020-07-10 09:58:48 -0400 | [diff] [blame] | 331 | TextPaintOrder::kFillStroke, |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 332 | false, |
| 333 | false |
| 334 | })); |
Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 335 | } |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 336 | |
| 337 | DEF_TEST(Skottie_Annotations, reporter) { |
| 338 | static constexpr char json[] = R"({ |
| 339 | "v": "5.2.1", |
| 340 | "w": 100, |
| 341 | "h": 100, |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 342 | "fr": 10, |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 343 | "ip": 0, |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 344 | "op": 100, |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 345 | "layers": [ |
| 346 | { |
| 347 | "ty": 1, |
| 348 | "ind": 0, |
| 349 | "ip": 0, |
| 350 | "op": 1, |
| 351 | "ks": { |
| 352 | "o": { "a": 0, "k": 50 } |
| 353 | }, |
| 354 | "sw": 100, |
| 355 | "sh": 100, |
| 356 | "sc": "#ffffff" |
| 357 | } |
| 358 | ], |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 359 | "markers": [ |
| 360 | { |
| 361 | "cm": "marker_1", |
| 362 | "dr": 25, |
| 363 | "tm": 25 |
| 364 | }, |
| 365 | { |
| 366 | "cm": "marker_2", |
| 367 | "dr": 0, |
| 368 | "tm": 75 |
| 369 | } |
| 370 | ] |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 371 | })"; |
| 372 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 373 | class TestMarkerObserver final : public MarkerObserver { |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 374 | public: |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 375 | void onMarker(const char name[], float t0, float t1) override { |
| 376 | fMarkers.push_back(std::make_tuple(name, t0, t1)); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 377 | } |
| 378 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 379 | std::vector<std::tuple<std::string, float, float>> fMarkers; |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 380 | }; |
| 381 | |
| 382 | SkMemoryStream stream(json, strlen(json)); |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 383 | auto observer = sk_make_sp<TestMarkerObserver>(); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 384 | |
| 385 | auto animation = skottie::Animation::Builder() |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 386 | .setMarkerObserver(observer) |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 387 | .make(&stream); |
| 388 | |
| 389 | REPORTER_ASSERT(reporter, animation); |
Florin Malita | 8bfe391 | 2020-08-07 09:05:50 -0400 | [diff] [blame] | 390 | REPORTER_ASSERT(reporter, animation->duration() == 10); |
| 391 | REPORTER_ASSERT(reporter, animation->inPoint() == 0.0); |
| 392 | REPORTER_ASSERT(reporter, animation->outPoint() == 100.0); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 393 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 394 | REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul); |
| 395 | REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1"); |
| 396 | REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f); |
| 397 | REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f); |
| 398 | REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2"); |
| 399 | REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f); |
| 400 | REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 401 | } |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 402 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 403 | static SkRect ComputeBlobBounds(const sk_sp<SkTextBlob>& blob) { |
| 404 | auto bounds = SkRect::MakeEmpty(); |
| 405 | |
| 406 | if (!blob) { |
| 407 | return bounds; |
| 408 | } |
| 409 | |
| 410 | SkAutoSTArray<16, SkRect> glyphBounds; |
| 411 | |
| 412 | SkTextBlobRunIterator it(blob.get()); |
| 413 | |
| 414 | for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { |
| 415 | glyphBounds.reset(SkToInt(it.glyphCount())); |
| 416 | it.font().getBounds(it.glyphs(), it.glyphCount(), glyphBounds.get(), nullptr); |
| 417 | |
| 418 | SkASSERT(it.positioning() == SkTextBlobRunIterator::kFull_Positioning); |
| 419 | for (uint32_t i = 0; i < it.glyphCount(); ++i) { |
| 420 | bounds.join(glyphBounds[i].makeOffset(it.pos()[i * 2 ], |
| 421 | it.pos()[i * 2 + 1])); |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | return bounds; |
| 426 | } |
| 427 | |
| 428 | static SkRect ComputeShapeResultBounds(const skottie::Shaper::Result& res) { |
| 429 | auto bounds = SkRect::MakeEmpty(); |
| 430 | |
| 431 | for (const auto& fragment : res.fFragments) { |
| 432 | bounds.join(ComputeBlobBounds(fragment.fBlob).makeOffset(fragment.fPos.x(), |
| 433 | fragment.fPos.y())); |
| 434 | } |
| 435 | |
| 436 | return bounds; |
| 437 | } |
| 438 | |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 439 | DEF_TEST(Skottie_Shaper_HAlign, reporter) { |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 440 | auto typeface = SkTypeface::MakeDefault(); |
| 441 | REPORTER_ASSERT(reporter, typeface); |
| 442 | |
| 443 | static constexpr struct { |
| 444 | SkScalar text_size, |
| 445 | tolerance; |
| 446 | } kTestSizes[] = { |
Florin Malita | 20678f8 | 2019-03-28 18:35:44 -0400 | [diff] [blame] | 447 | // These gross tolerances are required for the test to pass on NativeFonts bots. |
| 448 | // Might be worth investigating why we need so much slack. |
| 449 | { 5, 2.0f }, |
| 450 | { 10, 2.0f }, |
| 451 | { 15, 2.4f }, |
| 452 | { 25, 4.4f }, |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 453 | }; |
| 454 | |
| 455 | static constexpr struct { |
| 456 | SkTextUtils::Align align; |
| 457 | SkScalar l_selector, |
| 458 | r_selector; |
| 459 | } kTestAligns[] = { |
| 460 | { SkTextUtils:: kLeft_Align, 0.0f, 1.0f }, |
| 461 | { SkTextUtils::kCenter_Align, 0.5f, 0.5f }, |
| 462 | { SkTextUtils:: kRight_Align, 1.0f, 0.0f }, |
| 463 | }; |
| 464 | |
| 465 | const SkString text("Foo, bar.\rBaz."); |
| 466 | const SkPoint text_point = SkPoint::Make(100, 100); |
| 467 | |
| 468 | for (const auto& tsize : kTestSizes) { |
| 469 | for (const auto& talign : kTestAligns) { |
| 470 | const skottie::Shaper::TextDesc desc = { |
| 471 | typeface, |
| 472 | tsize.text_size, |
Florin Malita | a50484a | 2019-05-08 11:56:27 -0400 | [diff] [blame] | 473 | tsize.text_size, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 474 | 0, |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 475 | 0, |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 476 | talign.align, |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 477 | Shaper::VAlign::kTopBaseline, |
| 478 | Shaper::ResizePolicy::kNone, |
| 479 | Shaper::LinebreakPolicy::kExplicit, |
Florin Malita | ebbacd8 | 2020-11-13 13:38:39 -0500 | [diff] [blame] | 480 | Shaper::Direction::kLTR, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 481 | Shaper::Flags::kNone |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 482 | }; |
| 483 | |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 484 | const auto shape_result = Shaper::Shape(text, desc, text_point, |
| 485 | SkFontMgr::RefDefault()); |
Florin Malita | a8d1819 | 2019-05-28 12:58:18 -0400 | [diff] [blame] | 486 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 487 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 488 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 489 | const auto shape_bounds = ComputeShapeResultBounds(shape_result); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 490 | REPORTER_ASSERT(reporter, !shape_bounds.isEmpty()); |
| 491 | |
| 492 | const auto expected_l = text_point.x() - shape_bounds.width() * talign.l_selector; |
| 493 | REPORTER_ASSERT(reporter, |
Ben Wagner | 3bdb69c | 2019-04-01 19:01:09 -0400 | [diff] [blame] | 494 | std::fabs(shape_bounds.left() - expected_l) < tsize.tolerance, |
| 495 | "%f %f %f %f %d", shape_bounds.left(), expected_l, tsize.tolerance, |
| 496 | tsize.text_size, talign.align); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 497 | |
| 498 | const auto expected_r = text_point.x() + shape_bounds.width() * talign.r_selector; |
| 499 | REPORTER_ASSERT(reporter, |
Ben Wagner | 3bdb69c | 2019-04-01 19:01:09 -0400 | [diff] [blame] | 500 | std::fabs(shape_bounds.right() - expected_r) < tsize.tolerance, |
| 501 | "%f %f %f %f %d", shape_bounds.right(), expected_r, tsize.tolerance, |
| 502 | tsize.text_size, talign.align); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 503 | |
| 504 | } |
| 505 | } |
| 506 | } |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 507 | |
| 508 | DEF_TEST(Skottie_Shaper_VAlign, reporter) { |
| 509 | auto typeface = SkTypeface::MakeDefault(); |
| 510 | REPORTER_ASSERT(reporter, typeface); |
| 511 | |
| 512 | static constexpr struct { |
| 513 | SkScalar text_size, |
| 514 | tolerance; |
| 515 | } kTestSizes[] = { |
| 516 | // These gross tolerances are required for the test to pass on NativeFonts bots. |
| 517 | // Might be worth investigating why we need so much slack. |
| 518 | { 5, 2.0f }, |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 519 | { 10, 4.0f }, |
| 520 | { 15, 5.5f }, |
| 521 | { 25, 8.0f }, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 522 | }; |
| 523 | |
| 524 | struct { |
| 525 | skottie::Shaper::VAlign align; |
| 526 | SkScalar topFactor; |
| 527 | } kTestAligns[] = { |
Florin Malita | afd2c10 | 2019-06-27 14:54:54 -0400 | [diff] [blame] | 528 | { skottie::Shaper::VAlign::kVisualTop , 0.0f }, |
| 529 | { skottie::Shaper::VAlign::kVisualCenter, 0.5f }, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 530 | // TODO: any way to test kTopBaseline? |
| 531 | }; |
| 532 | |
| 533 | const SkString text("Foo, bar.\rBaz."); |
| 534 | const auto text_box = SkRect::MakeXYWH(100, 100, 1000, 1000); // large-enough to avoid breaks. |
| 535 | |
| 536 | |
| 537 | for (const auto& tsize : kTestSizes) { |
| 538 | for (const auto& talign : kTestAligns) { |
| 539 | const skottie::Shaper::TextDesc desc = { |
| 540 | typeface, |
| 541 | tsize.text_size, |
Florin Malita | a50484a | 2019-05-08 11:56:27 -0400 | [diff] [blame] | 542 | tsize.text_size, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 543 | 0, |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 544 | 0, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 545 | SkTextUtils::Align::kCenter_Align, |
| 546 | talign.align, |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 547 | Shaper::ResizePolicy::kNone, |
| 548 | Shaper::LinebreakPolicy::kParagraph, |
Florin Malita | ebbacd8 | 2020-11-13 13:38:39 -0500 | [diff] [blame] | 549 | Shaper::Direction::kLTR, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 550 | Shaper::Flags::kNone |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 551 | }; |
| 552 | |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 553 | const auto shape_result = Shaper::Shape(text, desc, text_box, SkFontMgr::RefDefault()); |
Florin Malita | a8d1819 | 2019-05-28 12:58:18 -0400 | [diff] [blame] | 554 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 555 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 556 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 557 | const auto shape_bounds = ComputeShapeResultBounds(shape_result); |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 558 | REPORTER_ASSERT(reporter, !shape_bounds.isEmpty()); |
| 559 | |
| 560 | const auto v_diff = text_box.height() - shape_bounds.height(); |
| 561 | |
| 562 | const auto expected_t = text_box.top() + v_diff * talign.topFactor; |
| 563 | REPORTER_ASSERT(reporter, |
| 564 | std::fabs(shape_bounds.top() - expected_t) < tsize.tolerance, |
| 565 | "%f %f %f %f %d", shape_bounds.top(), expected_t, tsize.tolerance, |
| 566 | tsize.text_size, SkToU32(talign.align)); |
| 567 | |
| 568 | const auto expected_b = text_box.bottom() - v_diff * (1 - talign.topFactor); |
| 569 | REPORTER_ASSERT(reporter, |
| 570 | std::fabs(shape_bounds.bottom() - expected_b) < tsize.tolerance, |
| 571 | "%f %f %f %f %d", shape_bounds.bottom(), expected_b, tsize.tolerance, |
| 572 | tsize.text_size, SkToU32(talign.align)); |
| 573 | } |
| 574 | } |
| 575 | } |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 576 | |
| 577 | DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) { |
| 578 | skottie::Shaper::TextDesc desc = { |
| 579 | SkTypeface::MakeDefault(), |
| 580 | 18, |
| 581 | 18, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 582 | 0, |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 583 | 0, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 584 | SkTextUtils::Align::kCenter_Align, |
| 585 | Shaper::VAlign::kTop, |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 586 | Shaper::ResizePolicy::kNone, |
| 587 | Shaper::LinebreakPolicy::kParagraph, |
Florin Malita | ebbacd8 | 2020-11-13 13:38:39 -0500 | [diff] [blame] | 588 | Shaper::Direction::kLTR, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 589 | Shaper::Flags::kNone |
| 590 | }; |
| 591 | |
| 592 | const SkString text("Foo bar baz"); |
| 593 | const auto text_box = SkRect::MakeWH(100, 100); |
| 594 | |
| 595 | { |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 596 | const auto shape_result = Shaper::Shape(text, desc, text_box, SkFontMgr::RefDefault()); |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 597 | // Default/consolidated mode => single blob result. |
| 598 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 599 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 600 | } |
| 601 | |
| 602 | { |
| 603 | desc.fFlags = Shaper::Flags::kFragmentGlyphs; |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 604 | const auto shape_result = skottie::Shaper::Shape(text, desc, text_box, |
| 605 | SkFontMgr::RefDefault()); |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 606 | // Fragmented mode => one blob per glyph. |
| 607 | const size_t expectedSize = text.size(); |
| 608 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == expectedSize); |
| 609 | for (size_t i = 0; i < expectedSize; ++i) { |
| 610 | REPORTER_ASSERT(reporter, shape_result.fFragments[i].fBlob); |
| 611 | } |
| 612 | } |
| 613 | } |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 614 | |
| 615 | #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && !defined(SK_BUILD_FOR_WIN) |
| 616 | |
| 617 | DEF_TEST(Skottie_Shaper_ExplicitFontMgr, reporter) { |
| 618 | class CountingFontMgr : public SkFontMgr { |
| 619 | public: |
| 620 | size_t fallbackCount() const { return fFallbackCount; } |
| 621 | |
| 622 | protected: |
| 623 | int onCountFamilies() const override { return 0; } |
| 624 | void onGetFamilyName(int index, SkString* familyName) const override { |
| 625 | SkDEBUGFAIL("onGetFamilyName called with bad index"); |
| 626 | } |
| 627 | SkFontStyleSet* onCreateStyleSet(int index) const override { |
| 628 | SkDEBUGFAIL("onCreateStyleSet called with bad index"); |
| 629 | return nullptr; |
| 630 | } |
| 631 | SkFontStyleSet* onMatchFamily(const char[]) const override { |
| 632 | return SkFontStyleSet::CreateEmpty(); |
| 633 | } |
| 634 | |
| 635 | SkTypeface* onMatchFamilyStyle(const char[], const SkFontStyle&) const override { |
| 636 | return nullptr; |
| 637 | } |
| 638 | SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], |
| 639 | const SkFontStyle& style, |
| 640 | const char* bcp47[], |
| 641 | int bcp47Count, |
| 642 | SkUnichar character) const override { |
| 643 | fFallbackCount++; |
| 644 | return nullptr; |
| 645 | } |
| 646 | SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const override { |
| 647 | return nullptr; |
| 648 | } |
| 649 | |
| 650 | sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int) const override { |
| 651 | return nullptr; |
| 652 | } |
| 653 | sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, int) const override { |
| 654 | return nullptr; |
| 655 | } |
| 656 | sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, |
| 657 | const SkFontArguments&) const override { |
| 658 | return nullptr; |
| 659 | } |
| 660 | sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override { |
| 661 | return nullptr; |
| 662 | } |
| 663 | sk_sp<SkTypeface> onMakeFromFile(const char[], int) const override { |
| 664 | return nullptr; |
| 665 | } |
| 666 | sk_sp<SkTypeface> onLegacyMakeTypeface(const char [], SkFontStyle) const override { |
| 667 | return nullptr; |
| 668 | } |
| 669 | private: |
| 670 | mutable size_t fFallbackCount = 0; |
| 671 | }; |
| 672 | |
| 673 | auto fontmgr = sk_make_sp<CountingFontMgr>(); |
| 674 | |
| 675 | skottie::Shaper::TextDesc desc = { |
| 676 | ToolUtils::create_portable_typeface(), |
| 677 | 18, |
| 678 | 18, |
| 679 | 0, |
Florin Malita | 13c4dbe | 2020-08-21 12:45:30 -0400 | [diff] [blame] | 680 | 0, |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 681 | SkTextUtils::Align::kCenter_Align, |
| 682 | Shaper::VAlign::kTop, |
Florin Malita | ad91100 | 2020-01-28 13:21:47 -0500 | [diff] [blame] | 683 | Shaper::ResizePolicy::kNone, |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 684 | Shaper::LinebreakPolicy::kParagraph, |
Florin Malita | ebbacd8 | 2020-11-13 13:38:39 -0500 | [diff] [blame] | 685 | Shaper::Direction::kLTR, |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 686 | Shaper::Flags::kNone |
| 687 | }; |
| 688 | |
| 689 | const auto text_box = SkRect::MakeWH(100, 100); |
| 690 | |
| 691 | { |
Florin Malita | 71bcfef | 2020-10-02 14:15:50 -0400 | [diff] [blame] | 692 | const auto shape_result = Shaper::Shape(SkString("foo bar"), desc, text_box, fontmgr); |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 693 | |
| 694 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 695 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 696 | REPORTER_ASSERT(reporter, fontmgr->fallbackCount() == 0ul); |
Florin Malita | 02f345f | 2019-07-28 10:58:40 -0400 | [diff] [blame] | 697 | REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 0); |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 698 | } |
| 699 | |
| 700 | { |
| 701 | // An unassigned codepoint should trigger fallback. |
| 702 | const auto shape_result = skottie::Shaper::Shape(SkString("foo\U000DFFFFbar"), |
| 703 | desc, text_box, fontmgr); |
| 704 | |
| 705 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 706 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 707 | REPORTER_ASSERT(reporter, fontmgr->fallbackCount() == 1ul); |
Florin Malita | 02f345f | 2019-07-28 10:58:40 -0400 | [diff] [blame] | 708 | REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 1ul); |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 709 | } |
| 710 | } |
| 711 | |
| 712 | #endif |
Florin Malita | cc92b27 | 2019-12-05 09:43:35 -0500 | [diff] [blame] | 713 | |
| 714 | DEF_TEST(Skottie_Image_Loading, reporter) { |
| 715 | class TestResourceProvider final : public skresources::ResourceProvider { |
| 716 | public: |
| 717 | TestResourceProvider(sk_sp<skresources::ImageAsset> single_asset, |
| 718 | sk_sp<skresources::ImageAsset> multi_asset) |
| 719 | : fSingleFrameAsset(std::move(single_asset)) |
| 720 | , fMultiFrameAsset (std::move( multi_asset)) {} |
| 721 | |
| 722 | private: |
| 723 | sk_sp<ImageAsset> loadImageAsset(const char path[], |
| 724 | const char name[], |
Brian Salomon | d007281 | 2020-07-21 17:03:56 -0400 | [diff] [blame] | 725 | const char id[]) const override { |
Florin Malita | cc92b27 | 2019-12-05 09:43:35 -0500 | [diff] [blame] | 726 | return strcmp(id, "single_frame") |
| 727 | ? fMultiFrameAsset |
| 728 | : fSingleFrameAsset; |
| 729 | } |
| 730 | |
| 731 | const sk_sp<skresources::ImageAsset> fSingleFrameAsset, |
| 732 | fMultiFrameAsset; |
| 733 | }; |
| 734 | |
| 735 | auto make_animation = [&reporter] (sk_sp<skresources::ImageAsset> single_asset, |
| 736 | sk_sp<skresources::ImageAsset> multi_asset, |
| 737 | bool deferred_image_loading) { |
| 738 | static constexpr char json[] = R"({ |
| 739 | "v": "5.2.1", |
| 740 | "w": 100, |
| 741 | "h": 100, |
| 742 | "fr": 10, |
| 743 | "ip": 0, |
| 744 | "op": 100, |
| 745 | "assets": [ |
| 746 | { |
| 747 | "id": "single_frame", |
| 748 | "p" : "single_frame.png", |
| 749 | "u" : "images/", |
| 750 | "w" : 500, |
| 751 | "h" : 500 |
| 752 | }, |
| 753 | { |
| 754 | "id": "multi_frame", |
| 755 | "p" : "multi_frame.png", |
| 756 | "u" : "images/", |
| 757 | "w" : 500, |
| 758 | "h" : 500 |
| 759 | } |
| 760 | ], |
| 761 | "layers": [ |
| 762 | { |
| 763 | "ty": 2, |
| 764 | "refId": "single_frame", |
| 765 | "ind": 0, |
| 766 | "ip": 0, |
| 767 | "op": 100, |
| 768 | "ks": {} |
| 769 | }, |
| 770 | { |
| 771 | "ty": 2, |
| 772 | "refId": "multi_frame", |
| 773 | "ind": 1, |
| 774 | "ip": 0, |
| 775 | "op": 100, |
| 776 | "ks": {} |
| 777 | } |
| 778 | ] |
| 779 | })"; |
| 780 | |
| 781 | SkMemoryStream stream(json, strlen(json)); |
| 782 | |
| 783 | const auto flags = deferred_image_loading |
| 784 | ? static_cast<uint32_t>(skottie::Animation::Builder::kDeferImageLoading) |
| 785 | : 0; |
| 786 | auto animation = |
| 787 | skottie::Animation::Builder(flags) |
| 788 | .setResourceProvider(sk_make_sp<TestResourceProvider>(std::move(single_asset), |
| 789 | std::move( multi_asset))) |
| 790 | .make(&stream); |
| 791 | |
| 792 | REPORTER_ASSERT(reporter, animation); |
| 793 | |
| 794 | return animation; |
| 795 | }; |
| 796 | |
| 797 | class TestAsset final : public skresources::ImageAsset { |
| 798 | public: |
| 799 | explicit TestAsset(bool multi_frame) : fMultiFrame(multi_frame) {} |
| 800 | |
| 801 | const std::vector<float>& requestedFrames() const { return fRequestedFrames; } |
| 802 | |
| 803 | private: |
| 804 | bool isMultiFrame() override { return fMultiFrame; } |
| 805 | |
| 806 | sk_sp<SkImage> getFrame(float t) override { |
| 807 | fRequestedFrames.push_back(t); |
| 808 | |
| 809 | return SkSurface::MakeRasterN32Premul(10, 10)->makeImageSnapshot(); |
| 810 | } |
| 811 | |
| 812 | const bool fMultiFrame; |
| 813 | |
| 814 | std::vector<float> fRequestedFrames; |
| 815 | }; |
| 816 | |
| 817 | { |
| 818 | auto single_asset = sk_make_sp<TestAsset>(false), |
| 819 | multi_asset = sk_make_sp<TestAsset>(true); |
| 820 | |
| 821 | // Default image loading: single-frame images are loaded upfront, multi-frame images are |
| 822 | // loaded on-demand. |
| 823 | auto animation = make_animation(single_asset, multi_asset, false); |
| 824 | |
| 825 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1); |
| 826 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0); |
| 827 | REPORTER_ASSERT(reporter, SkScalarNearlyZero(single_asset->requestedFrames()[0])); |
| 828 | |
| 829 | animation->seekFrameTime(1); |
| 830 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1); |
| 831 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1); |
| 832 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[0], 1)); |
| 833 | |
| 834 | animation->seekFrameTime(2); |
| 835 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1); |
| 836 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2); |
| 837 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2)); |
| 838 | } |
| 839 | |
| 840 | { |
| 841 | auto single_asset = sk_make_sp<TestAsset>(false), |
| 842 | multi_asset = sk_make_sp<TestAsset>(true); |
| 843 | |
| 844 | // Deferred image loading: both single-frame and multi-frame images are loaded on-demand. |
| 845 | auto animation = make_animation(single_asset, multi_asset, true); |
| 846 | |
| 847 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 0); |
| 848 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 0); |
| 849 | |
| 850 | animation->seekFrameTime(1); |
| 851 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1); |
| 852 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 1); |
| 853 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(single_asset->requestedFrames()[0], 1)); |
| 854 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual (multi_asset->requestedFrames()[0], 1)); |
| 855 | |
| 856 | animation->seekFrameTime(2); |
| 857 | REPORTER_ASSERT(reporter, single_asset->requestedFrames().size() == 1); |
| 858 | REPORTER_ASSERT(reporter, multi_asset->requestedFrames().size() == 2); |
| 859 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(multi_asset->requestedFrames()[1], 2)); |
| 860 | } |
| 861 | } |