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", |
| 81 | "ty": 21 |
| 82 | }], |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 83 | "shapes": [ |
| 84 | { |
| 85 | "ty": "el", |
| 86 | "nm": "geometry_0", |
| 87 | "p": { "a": 0, "k": [ 50, 50 ] }, |
| 88 | "s": { "a": 0, "k": [ 50, 50 ] } |
| 89 | }, |
| 90 | { |
| 91 | "ty": "fl", |
| 92 | "nm": "fill_0", |
| 93 | "c": { "a": 0, "k": [ 1, 0, 0] } |
| 94 | }, |
| 95 | { |
| 96 | "ty": "tr", |
| 97 | "nm": "shape_transform_0", |
| 98 | "o": { "a": 0, "k": 100 }, |
| 99 | "s": { "a": 0, "k": [ 50, 50 ] } |
| 100 | } |
| 101 | ] |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 102 | }, |
| 103 | { |
| 104 | "ty": 5, |
| 105 | "nm": "layer_1", |
| 106 | "ip": 0, |
| 107 | "op": 1, |
| 108 | "ks": { |
| 109 | "p": { "a": 0, "k": [25, 25] } |
| 110 | }, |
| 111 | "t": { |
| 112 | "d": { |
| 113 | "k": [ |
| 114 | { |
| 115 | "t": 0, |
| 116 | "s": { |
| 117 | "f": "test_font", |
| 118 | "s": 100, |
| 119 | "t": "inline_text", |
| 120 | "lh": 120 |
| 121 | } |
| 122 | } |
| 123 | ] |
| 124 | } |
| 125 | } |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 126 | } |
| 127 | ] |
Greg Daniel | 08ada8e | 2019-08-05 20:00:41 +0000 | [diff] [blame] | 128 | })"; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 129 | |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 130 | |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 131 | class TestPropertyObserver final : public PropertyObserver { |
| 132 | public: |
| 133 | struct ColorInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 134 | SkString node_name; |
| 135 | std::unique_ptr<skottie::ColorPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 136 | }; |
| 137 | |
| 138 | struct OpacityInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 139 | SkString node_name; |
| 140 | std::unique_ptr<skottie::OpacityPropertyHandle> handle; |
| 141 | }; |
| 142 | |
| 143 | struct TextInfo { |
| 144 | SkString node_name; |
| 145 | std::unique_ptr<skottie::TextPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 146 | }; |
| 147 | |
| 148 | struct TransformInfo { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 149 | SkString node_name; |
| 150 | std::unique_ptr<skottie::TransformPropertyHandle> handle; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 151 | }; |
| 152 | |
| 153 | void onColorProperty(const char node_name[], |
| 154 | const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 155 | fColors.push_back({SkString(node_name), lh()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | void onOpacityProperty(const char node_name[], |
| 159 | const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 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()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | void onTransformProperty(const char node_name[], |
| 169 | const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override { |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 170 | fTransforms.push_back({SkString(node_name), lh()}); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | const std::vector<ColorInfo>& colors() const { return fColors; } |
| 174 | const std::vector<OpacityInfo>& opacities() const { return fOpacities; } |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 175 | const std::vector<TextInfo>& texts() const { return fTexts; } |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 176 | const std::vector<TransformInfo>& transforms() const { return fTransforms; } |
| 177 | |
| 178 | private: |
| 179 | std::vector<ColorInfo> fColors; |
| 180 | std::vector<OpacityInfo> fOpacities; |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 181 | std::vector<TextInfo> fTexts; |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 182 | std::vector<TransformInfo> fTransforms; |
| 183 | }; |
| 184 | |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 185 | // Returns a single specified typeface for all requests. |
| 186 | class DummyFontMgr : public SkFontMgr { |
| 187 | public: |
| 188 | DummyFontMgr(sk_sp<SkTypeface> test_font) : fTestFont(test_font) {} |
| 189 | |
| 190 | int onCountFamilies() const override { return 1; } |
| 191 | void onGetFamilyName(int index, SkString* familyName) const override {} |
| 192 | SkFontStyleSet* onCreateStyleSet(int index) const override { return nullptr; } |
| 193 | SkFontStyleSet* onMatchFamily(const char familyName[]) const override { return nullptr; } |
| 194 | SkTypeface* onMatchFamilyStyle(const char familyName[], |
| 195 | const SkFontStyle& fontStyle) const override { |
| 196 | return nullptr; |
| 197 | } |
| 198 | SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&, |
| 199 | const char* bcp47[], int bcp47Count, |
| 200 | SkUnichar character) const override { |
| 201 | return nullptr; |
| 202 | } |
| 203 | SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const override { |
| 204 | return nullptr; |
| 205 | } |
| 206 | sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int ttcIndex) const override { |
| 207 | return fTestFont; |
| 208 | } |
| 209 | sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, |
| 210 | int ttcIndex) const override { |
| 211 | return fTestFont; |
| 212 | } |
| 213 | sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, |
| 214 | const SkFontArguments&) const override { |
| 215 | return fTestFont; |
| 216 | } |
| 217 | sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override { |
| 218 | return fTestFont; |
| 219 | } |
| 220 | sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override { |
| 221 | return fTestFont; |
| 222 | } |
| 223 | sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle) const override { |
| 224 | return fTestFont; |
| 225 | } |
| 226 | private: |
| 227 | sk_sp<SkTypeface> fTestFont; |
| 228 | }; |
| 229 | |
| 230 | sk_sp<DummyFontMgr> test_font_manager = sk_make_sp<DummyFontMgr>(test_typeface); |
Greg Daniel | 08ada8e | 2019-08-05 20:00:41 +0000 | [diff] [blame] | 231 | SkMemoryStream stream(json, strlen(json)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 232 | auto observer = sk_make_sp<TestPropertyObserver>(); |
| 233 | |
| 234 | auto animation = skottie::Animation::Builder() |
| 235 | .setPropertyObserver(observer) |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 236 | .setFontManager(test_font_manager) |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 237 | .make(&stream); |
| 238 | |
| 239 | REPORTER_ASSERT(reporter, animation); |
| 240 | |
| 241 | const auto& colors = observer->colors(); |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 242 | REPORTER_ASSERT(reporter, colors.size() == 2); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 243 | REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 244 | REPORTER_ASSERT(reporter, colors[0].handle->get() == 0xffff0000); |
Florin Malita | d9a4410 | 2019-07-30 09:02:16 -0400 | [diff] [blame] | 245 | REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 246 | REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 247 | |
| 248 | const auto& opacities = observer->opacities(); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 249 | REPORTER_ASSERT(reporter, opacities.size() == 3); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 250 | REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 251 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].handle->get(), 100)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 252 | REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 253 | REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50)); |
Florin Malita | a85f3a1 | 2018-09-24 17:24:59 -0400 | [diff] [blame] | 254 | |
| 255 | const auto& transforms = observer->transforms(); |
| 256 | REPORTER_ASSERT(reporter, transforms.size() == 2); |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 257 | REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 258 | REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({ |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 259 | SkPoint::Make(0, 0), |
| 260 | SkPoint::Make(0, 0), |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 261 | SkVector::Make(100, 100), |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 262 | 0, |
| 263 | 0, |
| 264 | 0 |
| 265 | })); |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 266 | REPORTER_ASSERT(reporter, transforms[1].node_name.equals("shape_transform_0")); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 267 | REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({ |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 268 | SkPoint::Make(0, 0), |
| 269 | SkPoint::Make(0, 0), |
Florin Malita | 60c84fd | 2019-07-02 18:02:54 -0400 | [diff] [blame] | 270 | SkVector::Make(50, 50), |
Florin Malita | 8ac81b7 | 2018-11-28 11:39:39 -0500 | [diff] [blame] | 271 | 0, |
| 272 | 0, |
| 273 | 0 |
| 274 | })); |
Avinash Parchuri | 3a543aa | 2019-08-05 18:30:25 -0700 | [diff] [blame] | 275 | |
| 276 | const auto& texts = observer->texts(); |
| 277 | REPORTER_ASSERT(reporter, texts.size() == 1); |
| 278 | REPORTER_ASSERT(reporter, texts[0].node_name.equals("layer_1")); |
| 279 | REPORTER_ASSERT(reporter, texts[0].handle->get() == skottie::TextPropertyValue({ |
| 280 | test_typeface, |
| 281 | SkString("inline_text"), |
| 282 | 100, |
| 283 | 0, |
| 284 | 120, |
| 285 | 0, |
| 286 | SkTextUtils::kLeft_Align, |
| 287 | Shaper::VAlign::kTopBaseline, |
| 288 | SkRect::MakeEmpty(), |
| 289 | SK_ColorTRANSPARENT, |
| 290 | SK_ColorTRANSPARENT, |
| 291 | false, |
| 292 | false |
| 293 | })); |
Florin Malita | 94d4d3e | 2018-06-18 13:10:51 -0400 | [diff] [blame] | 294 | } |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 295 | |
| 296 | DEF_TEST(Skottie_Annotations, reporter) { |
| 297 | static constexpr char json[] = R"({ |
| 298 | "v": "5.2.1", |
| 299 | "w": 100, |
| 300 | "h": 100, |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 301 | "fr": 10, |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 302 | "ip": 0, |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 303 | "op": 100, |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 304 | "layers": [ |
| 305 | { |
| 306 | "ty": 1, |
| 307 | "ind": 0, |
| 308 | "ip": 0, |
| 309 | "op": 1, |
| 310 | "ks": { |
| 311 | "o": { "a": 0, "k": 50 } |
| 312 | }, |
| 313 | "sw": 100, |
| 314 | "sh": 100, |
| 315 | "sc": "#ffffff" |
| 316 | } |
| 317 | ], |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 318 | "markers": [ |
| 319 | { |
| 320 | "cm": "marker_1", |
| 321 | "dr": 25, |
| 322 | "tm": 25 |
| 323 | }, |
| 324 | { |
| 325 | "cm": "marker_2", |
| 326 | "dr": 0, |
| 327 | "tm": 75 |
| 328 | } |
| 329 | ] |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 330 | })"; |
| 331 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 332 | class TestMarkerObserver final : public MarkerObserver { |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 333 | public: |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 334 | void onMarker(const char name[], float t0, float t1) override { |
| 335 | fMarkers.push_back(std::make_tuple(name, t0, t1)); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 336 | } |
| 337 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 338 | std::vector<std::tuple<std::string, float, float>> fMarkers; |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 339 | }; |
| 340 | |
| 341 | SkMemoryStream stream(json, strlen(json)); |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 342 | auto observer = sk_make_sp<TestMarkerObserver>(); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 343 | |
| 344 | auto animation = skottie::Animation::Builder() |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 345 | .setMarkerObserver(observer) |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 346 | .make(&stream); |
| 347 | |
| 348 | REPORTER_ASSERT(reporter, animation); |
| 349 | |
Florin Malita | 91af8d8 | 2018-11-30 16:46:45 -0500 | [diff] [blame] | 350 | REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul); |
| 351 | REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1"); |
| 352 | REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f); |
| 353 | REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f); |
| 354 | REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2"); |
| 355 | REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f); |
| 356 | REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f); |
Florin Malita | d9c56b4 | 2018-10-09 14:33:08 -0400 | [diff] [blame] | 357 | } |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 358 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 359 | static SkRect ComputeBlobBounds(const sk_sp<SkTextBlob>& blob) { |
| 360 | auto bounds = SkRect::MakeEmpty(); |
| 361 | |
| 362 | if (!blob) { |
| 363 | return bounds; |
| 364 | } |
| 365 | |
| 366 | SkAutoSTArray<16, SkRect> glyphBounds; |
| 367 | |
| 368 | SkTextBlobRunIterator it(blob.get()); |
| 369 | |
| 370 | for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { |
| 371 | glyphBounds.reset(SkToInt(it.glyphCount())); |
| 372 | it.font().getBounds(it.glyphs(), it.glyphCount(), glyphBounds.get(), nullptr); |
| 373 | |
| 374 | SkASSERT(it.positioning() == SkTextBlobRunIterator::kFull_Positioning); |
| 375 | for (uint32_t i = 0; i < it.glyphCount(); ++i) { |
| 376 | bounds.join(glyphBounds[i].makeOffset(it.pos()[i * 2 ], |
| 377 | it.pos()[i * 2 + 1])); |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | return bounds; |
| 382 | } |
| 383 | |
| 384 | static SkRect ComputeShapeResultBounds(const skottie::Shaper::Result& res) { |
| 385 | auto bounds = SkRect::MakeEmpty(); |
| 386 | |
| 387 | for (const auto& fragment : res.fFragments) { |
| 388 | bounds.join(ComputeBlobBounds(fragment.fBlob).makeOffset(fragment.fPos.x(), |
| 389 | fragment.fPos.y())); |
| 390 | } |
| 391 | |
| 392 | return bounds; |
| 393 | } |
| 394 | |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 395 | DEF_TEST(Skottie_Shaper_HAlign, reporter) { |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 396 | auto typeface = SkTypeface::MakeDefault(); |
| 397 | REPORTER_ASSERT(reporter, typeface); |
| 398 | |
| 399 | static constexpr struct { |
| 400 | SkScalar text_size, |
| 401 | tolerance; |
| 402 | } kTestSizes[] = { |
Florin Malita | 20678f8 | 2019-03-28 18:35:44 -0400 | [diff] [blame] | 403 | // These gross tolerances are required for the test to pass on NativeFonts bots. |
| 404 | // Might be worth investigating why we need so much slack. |
| 405 | { 5, 2.0f }, |
| 406 | { 10, 2.0f }, |
| 407 | { 15, 2.4f }, |
| 408 | { 25, 4.4f }, |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 409 | }; |
| 410 | |
| 411 | static constexpr struct { |
| 412 | SkTextUtils::Align align; |
| 413 | SkScalar l_selector, |
| 414 | r_selector; |
| 415 | } kTestAligns[] = { |
| 416 | { SkTextUtils:: kLeft_Align, 0.0f, 1.0f }, |
| 417 | { SkTextUtils::kCenter_Align, 0.5f, 0.5f }, |
| 418 | { SkTextUtils:: kRight_Align, 1.0f, 0.0f }, |
| 419 | }; |
| 420 | |
| 421 | const SkString text("Foo, bar.\rBaz."); |
| 422 | const SkPoint text_point = SkPoint::Make(100, 100); |
| 423 | |
| 424 | for (const auto& tsize : kTestSizes) { |
| 425 | for (const auto& talign : kTestAligns) { |
| 426 | const skottie::Shaper::TextDesc desc = { |
| 427 | typeface, |
| 428 | tsize.text_size, |
Florin Malita | a50484a | 2019-05-08 11:56:27 -0400 | [diff] [blame] | 429 | tsize.text_size, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 430 | 0, |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 431 | talign.align, |
Florin Malita | 9a9e723 | 2019-04-02 17:15:00 -0400 | [diff] [blame] | 432 | skottie::Shaper::VAlign::kTopBaseline, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 433 | Shaper::Flags::kNone |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 434 | }; |
| 435 | |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 436 | const auto shape_result = skottie::Shaper::Shape(text, desc, text_point, |
| 437 | SkFontMgr::RefDefault()); |
Florin Malita | a8d1819 | 2019-05-28 12:58:18 -0400 | [diff] [blame] | 438 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 439 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 440 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 441 | const auto shape_bounds = ComputeShapeResultBounds(shape_result); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 442 | REPORTER_ASSERT(reporter, !shape_bounds.isEmpty()); |
| 443 | |
| 444 | const auto expected_l = text_point.x() - shape_bounds.width() * talign.l_selector; |
| 445 | REPORTER_ASSERT(reporter, |
Ben Wagner | 3bdb69c | 2019-04-01 19:01:09 -0400 | [diff] [blame] | 446 | std::fabs(shape_bounds.left() - expected_l) < tsize.tolerance, |
| 447 | "%f %f %f %f %d", shape_bounds.left(), expected_l, tsize.tolerance, |
| 448 | tsize.text_size, talign.align); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 449 | |
| 450 | const auto expected_r = text_point.x() + shape_bounds.width() * talign.r_selector; |
| 451 | REPORTER_ASSERT(reporter, |
Ben Wagner | 3bdb69c | 2019-04-01 19:01:09 -0400 | [diff] [blame] | 452 | std::fabs(shape_bounds.right() - expected_r) < tsize.tolerance, |
| 453 | "%f %f %f %f %d", shape_bounds.right(), expected_r, tsize.tolerance, |
| 454 | tsize.text_size, talign.align); |
Florin Malita | 0d33eac | 2019-03-28 13:41:53 -0400 | [diff] [blame] | 455 | |
| 456 | } |
| 457 | } |
| 458 | } |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 459 | |
| 460 | DEF_TEST(Skottie_Shaper_VAlign, reporter) { |
| 461 | auto typeface = SkTypeface::MakeDefault(); |
| 462 | REPORTER_ASSERT(reporter, typeface); |
| 463 | |
| 464 | static constexpr struct { |
| 465 | SkScalar text_size, |
| 466 | tolerance; |
| 467 | } kTestSizes[] = { |
| 468 | // These gross tolerances are required for the test to pass on NativeFonts bots. |
| 469 | // Might be worth investigating why we need so much slack. |
| 470 | { 5, 2.0f }, |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 471 | { 10, 4.0f }, |
| 472 | { 15, 5.5f }, |
| 473 | { 25, 8.0f }, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 474 | }; |
| 475 | |
| 476 | struct { |
| 477 | skottie::Shaper::VAlign align; |
| 478 | SkScalar topFactor; |
| 479 | } kTestAligns[] = { |
Florin Malita | afd2c10 | 2019-06-27 14:54:54 -0400 | [diff] [blame] | 480 | { skottie::Shaper::VAlign::kVisualTop , 0.0f }, |
| 481 | { skottie::Shaper::VAlign::kVisualCenter, 0.5f }, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 482 | // TODO: any way to test kTopBaseline? |
| 483 | }; |
| 484 | |
| 485 | const SkString text("Foo, bar.\rBaz."); |
| 486 | const auto text_box = SkRect::MakeXYWH(100, 100, 1000, 1000); // large-enough to avoid breaks. |
| 487 | |
| 488 | |
| 489 | for (const auto& tsize : kTestSizes) { |
| 490 | for (const auto& talign : kTestAligns) { |
| 491 | const skottie::Shaper::TextDesc desc = { |
| 492 | typeface, |
| 493 | tsize.text_size, |
Florin Malita | a50484a | 2019-05-08 11:56:27 -0400 | [diff] [blame] | 494 | tsize.text_size, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 495 | 0, |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 496 | SkTextUtils::Align::kCenter_Align, |
| 497 | talign.align, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 498 | Shaper::Flags::kNone |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 499 | }; |
| 500 | |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 501 | const auto shape_result = skottie::Shaper::Shape(text, desc, text_box, |
| 502 | SkFontMgr::RefDefault()); |
Florin Malita | a8d1819 | 2019-05-28 12:58:18 -0400 | [diff] [blame] | 503 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 504 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 505 | |
Florin Malita | 87e8850 | 2019-06-12 22:02:50 -0400 | [diff] [blame] | 506 | const auto shape_bounds = ComputeShapeResultBounds(shape_result); |
Florin Malita | 347a970 | 2019-04-10 17:04:46 +0000 | [diff] [blame] | 507 | REPORTER_ASSERT(reporter, !shape_bounds.isEmpty()); |
| 508 | |
| 509 | const auto v_diff = text_box.height() - shape_bounds.height(); |
| 510 | |
| 511 | const auto expected_t = text_box.top() + v_diff * talign.topFactor; |
| 512 | REPORTER_ASSERT(reporter, |
| 513 | std::fabs(shape_bounds.top() - expected_t) < tsize.tolerance, |
| 514 | "%f %f %f %f %d", shape_bounds.top(), expected_t, tsize.tolerance, |
| 515 | tsize.text_size, SkToU32(talign.align)); |
| 516 | |
| 517 | const auto expected_b = text_box.bottom() - v_diff * (1 - talign.topFactor); |
| 518 | REPORTER_ASSERT(reporter, |
| 519 | std::fabs(shape_bounds.bottom() - expected_b) < tsize.tolerance, |
| 520 | "%f %f %f %f %d", shape_bounds.bottom(), expected_b, tsize.tolerance, |
| 521 | tsize.text_size, SkToU32(talign.align)); |
| 522 | } |
| 523 | } |
| 524 | } |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 525 | |
| 526 | DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) { |
| 527 | skottie::Shaper::TextDesc desc = { |
| 528 | SkTypeface::MakeDefault(), |
| 529 | 18, |
| 530 | 18, |
Florin Malita | d75fed4 | 2019-06-27 09:23:03 -0400 | [diff] [blame] | 531 | 0, |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 532 | SkTextUtils::Align::kCenter_Align, |
| 533 | Shaper::VAlign::kTop, |
| 534 | Shaper::Flags::kNone |
| 535 | }; |
| 536 | |
| 537 | const SkString text("Foo bar baz"); |
| 538 | const auto text_box = SkRect::MakeWH(100, 100); |
| 539 | |
| 540 | { |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 541 | const auto shape_result = skottie::Shaper::Shape(text, desc, text_box, |
| 542 | SkFontMgr::RefDefault()); |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 543 | // Default/consolidated mode => single blob result. |
| 544 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 545 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 546 | } |
| 547 | |
| 548 | { |
| 549 | desc.fFlags = Shaper::Flags::kFragmentGlyphs; |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 550 | const auto shape_result = skottie::Shaper::Shape(text, desc, text_box, |
| 551 | SkFontMgr::RefDefault()); |
Florin Malita | 0fe004c | 2019-05-30 12:10:07 -0400 | [diff] [blame] | 552 | // Fragmented mode => one blob per glyph. |
| 553 | const size_t expectedSize = text.size(); |
| 554 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == expectedSize); |
| 555 | for (size_t i = 0; i < expectedSize; ++i) { |
| 556 | REPORTER_ASSERT(reporter, shape_result.fFragments[i].fBlob); |
| 557 | } |
| 558 | } |
| 559 | } |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 560 | |
| 561 | #if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && !defined(SK_BUILD_FOR_WIN) |
| 562 | |
| 563 | DEF_TEST(Skottie_Shaper_ExplicitFontMgr, reporter) { |
| 564 | class CountingFontMgr : public SkFontMgr { |
| 565 | public: |
| 566 | size_t fallbackCount() const { return fFallbackCount; } |
| 567 | |
| 568 | protected: |
| 569 | int onCountFamilies() const override { return 0; } |
| 570 | void onGetFamilyName(int index, SkString* familyName) const override { |
| 571 | SkDEBUGFAIL("onGetFamilyName called with bad index"); |
| 572 | } |
| 573 | SkFontStyleSet* onCreateStyleSet(int index) const override { |
| 574 | SkDEBUGFAIL("onCreateStyleSet called with bad index"); |
| 575 | return nullptr; |
| 576 | } |
| 577 | SkFontStyleSet* onMatchFamily(const char[]) const override { |
| 578 | return SkFontStyleSet::CreateEmpty(); |
| 579 | } |
| 580 | |
| 581 | SkTypeface* onMatchFamilyStyle(const char[], const SkFontStyle&) const override { |
| 582 | return nullptr; |
| 583 | } |
| 584 | SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], |
| 585 | const SkFontStyle& style, |
| 586 | const char* bcp47[], |
| 587 | int bcp47Count, |
| 588 | SkUnichar character) const override { |
| 589 | fFallbackCount++; |
| 590 | return nullptr; |
| 591 | } |
| 592 | SkTypeface* onMatchFaceStyle(const SkTypeface*, const SkFontStyle&) const override { |
| 593 | return nullptr; |
| 594 | } |
| 595 | |
| 596 | sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData>, int) const override { |
| 597 | return nullptr; |
| 598 | } |
| 599 | sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset>, int) const override { |
| 600 | return nullptr; |
| 601 | } |
| 602 | sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset>, |
| 603 | const SkFontArguments&) const override { |
| 604 | return nullptr; |
| 605 | } |
| 606 | sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData>) const override { |
| 607 | return nullptr; |
| 608 | } |
| 609 | sk_sp<SkTypeface> onMakeFromFile(const char[], int) const override { |
| 610 | return nullptr; |
| 611 | } |
| 612 | sk_sp<SkTypeface> onLegacyMakeTypeface(const char [], SkFontStyle) const override { |
| 613 | return nullptr; |
| 614 | } |
| 615 | private: |
| 616 | mutable size_t fFallbackCount = 0; |
| 617 | }; |
| 618 | |
| 619 | auto fontmgr = sk_make_sp<CountingFontMgr>(); |
| 620 | |
| 621 | skottie::Shaper::TextDesc desc = { |
| 622 | ToolUtils::create_portable_typeface(), |
| 623 | 18, |
| 624 | 18, |
| 625 | 0, |
| 626 | SkTextUtils::Align::kCenter_Align, |
| 627 | Shaper::VAlign::kTop, |
| 628 | Shaper::Flags::kNone |
| 629 | }; |
| 630 | |
| 631 | const auto text_box = SkRect::MakeWH(100, 100); |
| 632 | |
| 633 | { |
| 634 | const auto shape_result = skottie::Shaper::Shape(SkString("foo bar"), |
| 635 | desc, text_box, fontmgr); |
| 636 | |
| 637 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 638 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 639 | REPORTER_ASSERT(reporter, fontmgr->fallbackCount() == 0ul); |
Florin Malita | 02f345f | 2019-07-28 10:58:40 -0400 | [diff] [blame] | 640 | REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 0); |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 641 | } |
| 642 | |
| 643 | { |
| 644 | // An unassigned codepoint should trigger fallback. |
| 645 | const auto shape_result = skottie::Shaper::Shape(SkString("foo\U000DFFFFbar"), |
| 646 | desc, text_box, fontmgr); |
| 647 | |
| 648 | REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul); |
| 649 | REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob); |
| 650 | REPORTER_ASSERT(reporter, fontmgr->fallbackCount() == 1ul); |
Florin Malita | 02f345f | 2019-07-28 10:58:40 -0400 | [diff] [blame] | 651 | REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 1ul); |
Florin Malita | 4268433 | 2019-07-26 14:54:40 -0400 | [diff] [blame] | 652 | } |
| 653 | } |
| 654 | |
| 655 | #endif |