blob: 29124a27b509099e24135f60c1db84d42e29ae9f [file] [log] [blame]
Florin Malita94d4d3e2018-06-18 13:10:51 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Florin Malita42684332019-07-26 14:54:40 -04008#include "include/core/SkFontMgr.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#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 Malitaca449d52019-05-08 10:04:09 -040015#include "modules/skottie/src/text/SkottieShaper.h"
Florin Malita42684332019-07-26 14:54:40 -040016#include "src/core/SkFontDescriptor.h"
Florin Malita87e88502019-06-12 22:02:50 -040017#include "src/core/SkTextBlobPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "tests/Test.h"
Florin Malita42684332019-07-26 14:54:40 -040019#include "tools/ToolUtils.h"
Florin Malita94d4d3e2018-06-18 13:10:51 -040020
Florin Malita0d33eac2019-03-28 13:41:53 -040021#include <cmath>
Mike Klein48e08aa2019-08-26 11:24:50 -050022#include <string>
Florin Malitad9c56b42018-10-09 14:33:08 -040023#include <tuple>
Florin Malitaa85f3a12018-09-24 17:24:59 -040024#include <vector>
25
26using namespace skottie;
27
Florin Malita94d4d3e2018-06-18 13:10:51 -040028DEF_TEST(Skottie_OssFuzz8956, reporter) {
Florin Malitaa85f3a12018-09-24 17:24:59 -040029 static constexpr char json[] =
Florin Malita94d4d3e2018-06-18 13:10:51 -040030 "{\"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 Malitaa85f3a12018-09-24 17:24:59 -040037 auto animation = Animation::Make(&stream);
38}
39
40DEF_TEST(Skottie_Properties, reporter) {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -070041 auto test_typeface = ToolUtils::create_portable_typeface();
42 REPORTER_ASSERT(reporter, test_typeface);
43
44 static const char json[] = R"({
Florin Malitaa85f3a12018-09-24 17:24:59 -040045 "v": "5.2.1",
46 "w": 100,
47 "h": 100,
48 "fr": 1,
49 "ip": 0,
50 "op": 1,
Avinash Parchuri3a543aa2019-08-05 18:30:25 -070051 "fonts": {
52 "list": [
53 {
54 "fName": "test_font",
55 "fFamily": "test-family",
56 "fStyle": "TestFontStyle"
57 }
58 ]
59 },
Florin Malitaa85f3a12018-09-24 17:24:59 -040060 "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 Malitad9a44102019-07-30 09:02:16 -040070 "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 Malitaa85f3a12018-09-24 17:24:59 -040083 "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 Parchuri3a543aa2019-08-05 18:30:25 -0700102 },
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 Malitaa85f3a12018-09-24 17:24:59 -0400126 }
127 ]
Greg Daniel08ada8e2019-08-05 20:00:41 +0000128 })";
Florin Malitaa85f3a12018-09-24 17:24:59 -0400129
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700130
Florin Malitaa85f3a12018-09-24 17:24:59 -0400131 class TestPropertyObserver final : public PropertyObserver {
132 public:
133 struct ColorInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700134 SkString node_name;
135 std::unique_ptr<skottie::ColorPropertyHandle> handle;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400136 };
137
138 struct OpacityInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700139 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 Malitaa85f3a12018-09-24 17:24:59 -0400146 };
147
148 struct TransformInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700149 SkString node_name;
150 std::unique_ptr<skottie::TransformPropertyHandle> handle;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400151 };
152
153 void onColorProperty(const char node_name[],
154 const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700155 fColors.push_back({SkString(node_name), lh()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400156 }
157
158 void onOpacityProperty(const char node_name[],
159 const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700160 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 Malitaa85f3a12018-09-24 17:24:59 -0400166 }
167
168 void onTransformProperty(const char node_name[],
169 const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700170 fTransforms.push_back({SkString(node_name), lh()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400171 }
172
173 const std::vector<ColorInfo>& colors() const { return fColors; }
174 const std::vector<OpacityInfo>& opacities() const { return fOpacities; }
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700175 const std::vector<TextInfo>& texts() const { return fTexts; }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400176 const std::vector<TransformInfo>& transforms() const { return fTransforms; }
177
178 private:
179 std::vector<ColorInfo> fColors;
180 std::vector<OpacityInfo> fOpacities;
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700181 std::vector<TextInfo> fTexts;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400182 std::vector<TransformInfo> fTransforms;
183 };
184
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700185 // 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 Daniel08ada8e2019-08-05 20:00:41 +0000231 SkMemoryStream stream(json, strlen(json));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400232 auto observer = sk_make_sp<TestPropertyObserver>();
233
234 auto animation = skottie::Animation::Builder()
235 .setPropertyObserver(observer)
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700236 .setFontManager(test_font_manager)
Florin Malitaa85f3a12018-09-24 17:24:59 -0400237 .make(&stream);
238
239 REPORTER_ASSERT(reporter, animation);
240
241 const auto& colors = observer->colors();
Florin Malitad9a44102019-07-30 09:02:16 -0400242 REPORTER_ASSERT(reporter, colors.size() == 2);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400243 REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700244 REPORTER_ASSERT(reporter, colors[0].handle->get() == 0xffff0000);
Florin Malitad9a44102019-07-30 09:02:16 -0400245 REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700246 REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400247
248 const auto& opacities = observer->opacities();
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700249 REPORTER_ASSERT(reporter, opacities.size() == 3);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400250 REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700251 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].handle->get(), 100));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400252 REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700253 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400254
255 const auto& transforms = observer->transforms();
256 REPORTER_ASSERT(reporter, transforms.size() == 2);
Florin Malita60c84fd2019-07-02 18:02:54 -0400257 REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700258 REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({
Florin Malita8ac81b72018-11-28 11:39:39 -0500259 SkPoint::Make(0, 0),
260 SkPoint::Make(0, 0),
Florin Malita60c84fd2019-07-02 18:02:54 -0400261 SkVector::Make(100, 100),
Florin Malita8ac81b72018-11-28 11:39:39 -0500262 0,
263 0,
264 0
265 }));
Florin Malita60c84fd2019-07-02 18:02:54 -0400266 REPORTER_ASSERT(reporter, transforms[1].node_name.equals("shape_transform_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700267 REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({
Florin Malita8ac81b72018-11-28 11:39:39 -0500268 SkPoint::Make(0, 0),
269 SkPoint::Make(0, 0),
Florin Malita60c84fd2019-07-02 18:02:54 -0400270 SkVector::Make(50, 50),
Florin Malita8ac81b72018-11-28 11:39:39 -0500271 0,
272 0,
273 0
274 }));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700275
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 Malita94d4d3e2018-06-18 13:10:51 -0400294}
Florin Malitad9c56b42018-10-09 14:33:08 -0400295
296DEF_TEST(Skottie_Annotations, reporter) {
297 static constexpr char json[] = R"({
298 "v": "5.2.1",
299 "w": 100,
300 "h": 100,
Florin Malita91af8d82018-11-30 16:46:45 -0500301 "fr": 10,
Florin Malitad9c56b42018-10-09 14:33:08 -0400302 "ip": 0,
Florin Malita91af8d82018-11-30 16:46:45 -0500303 "op": 100,
Florin Malitad9c56b42018-10-09 14:33:08 -0400304 "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 Malita91af8d82018-11-30 16:46:45 -0500318 "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 Malitad9c56b42018-10-09 14:33:08 -0400330 })";
331
Florin Malita91af8d82018-11-30 16:46:45 -0500332 class TestMarkerObserver final : public MarkerObserver {
Florin Malitad9c56b42018-10-09 14:33:08 -0400333 public:
Florin Malita91af8d82018-11-30 16:46:45 -0500334 void onMarker(const char name[], float t0, float t1) override {
335 fMarkers.push_back(std::make_tuple(name, t0, t1));
Florin Malitad9c56b42018-10-09 14:33:08 -0400336 }
337
Florin Malita91af8d82018-11-30 16:46:45 -0500338 std::vector<std::tuple<std::string, float, float>> fMarkers;
Florin Malitad9c56b42018-10-09 14:33:08 -0400339 };
340
341 SkMemoryStream stream(json, strlen(json));
Florin Malita91af8d82018-11-30 16:46:45 -0500342 auto observer = sk_make_sp<TestMarkerObserver>();
Florin Malitad9c56b42018-10-09 14:33:08 -0400343
344 auto animation = skottie::Animation::Builder()
Florin Malita91af8d82018-11-30 16:46:45 -0500345 .setMarkerObserver(observer)
Florin Malitad9c56b42018-10-09 14:33:08 -0400346 .make(&stream);
347
348 REPORTER_ASSERT(reporter, animation);
349
Florin Malita91af8d82018-11-30 16:46:45 -0500350 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 Malitad9c56b42018-10-09 14:33:08 -0400357}
Florin Malita0d33eac2019-03-28 13:41:53 -0400358
Florin Malita87e88502019-06-12 22:02:50 -0400359static 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
384static 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 Malita347a9702019-04-10 17:04:46 +0000395DEF_TEST(Skottie_Shaper_HAlign, reporter) {
Florin Malita0d33eac2019-03-28 13:41:53 -0400396 auto typeface = SkTypeface::MakeDefault();
397 REPORTER_ASSERT(reporter, typeface);
398
399 static constexpr struct {
400 SkScalar text_size,
401 tolerance;
402 } kTestSizes[] = {
Florin Malita20678f82019-03-28 18:35:44 -0400403 // 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 Malita0d33eac2019-03-28 13:41:53 -0400409 };
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 Malitaa50484a2019-05-08 11:56:27 -0400429 tsize.text_size,
Florin Malitad75fed42019-06-27 09:23:03 -0400430 0,
Florin Malita0d33eac2019-03-28 13:41:53 -0400431 talign.align,
Florin Malita9a9e7232019-04-02 17:15:00 -0400432 skottie::Shaper::VAlign::kTopBaseline,
Florin Malita0fe004c2019-05-30 12:10:07 -0400433 Shaper::Flags::kNone
Florin Malita0d33eac2019-03-28 13:41:53 -0400434 };
435
Florin Malita42684332019-07-26 14:54:40 -0400436 const auto shape_result = skottie::Shaper::Shape(text, desc, text_point,
437 SkFontMgr::RefDefault());
Florin Malitaa8d18192019-05-28 12:58:18 -0400438 REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul);
439 REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob);
Florin Malita0d33eac2019-03-28 13:41:53 -0400440
Florin Malita87e88502019-06-12 22:02:50 -0400441 const auto shape_bounds = ComputeShapeResultBounds(shape_result);
Florin Malita0d33eac2019-03-28 13:41:53 -0400442 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 Wagner3bdb69c2019-04-01 19:01:09 -0400446 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 Malita0d33eac2019-03-28 13:41:53 -0400449
450 const auto expected_r = text_point.x() + shape_bounds.width() * talign.r_selector;
451 REPORTER_ASSERT(reporter,
Ben Wagner3bdb69c2019-04-01 19:01:09 -0400452 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 Malita0d33eac2019-03-28 13:41:53 -0400455
456 }
457 }
458}
Florin Malita347a9702019-04-10 17:04:46 +0000459
460DEF_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 Malita87e88502019-06-12 22:02:50 -0400471 { 10, 4.0f },
472 { 15, 5.5f },
473 { 25, 8.0f },
Florin Malita347a9702019-04-10 17:04:46 +0000474 };
475
476 struct {
477 skottie::Shaper::VAlign align;
478 SkScalar topFactor;
479 } kTestAligns[] = {
Florin Malitaafd2c102019-06-27 14:54:54 -0400480 { skottie::Shaper::VAlign::kVisualTop , 0.0f },
481 { skottie::Shaper::VAlign::kVisualCenter, 0.5f },
Florin Malita347a9702019-04-10 17:04:46 +0000482 // 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 Malitaa50484a2019-05-08 11:56:27 -0400494 tsize.text_size,
Florin Malitad75fed42019-06-27 09:23:03 -0400495 0,
Florin Malita347a9702019-04-10 17:04:46 +0000496 SkTextUtils::Align::kCenter_Align,
497 talign.align,
Florin Malita0fe004c2019-05-30 12:10:07 -0400498 Shaper::Flags::kNone
Florin Malita347a9702019-04-10 17:04:46 +0000499 };
500
Florin Malita42684332019-07-26 14:54:40 -0400501 const auto shape_result = skottie::Shaper::Shape(text, desc, text_box,
502 SkFontMgr::RefDefault());
Florin Malitaa8d18192019-05-28 12:58:18 -0400503 REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul);
504 REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob);
Florin Malita347a9702019-04-10 17:04:46 +0000505
Florin Malita87e88502019-06-12 22:02:50 -0400506 const auto shape_bounds = ComputeShapeResultBounds(shape_result);
Florin Malita347a9702019-04-10 17:04:46 +0000507 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 Malita0fe004c2019-05-30 12:10:07 -0400525
526DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) {
527 skottie::Shaper::TextDesc desc = {
528 SkTypeface::MakeDefault(),
529 18,
530 18,
Florin Malitad75fed42019-06-27 09:23:03 -0400531 0,
Florin Malita0fe004c2019-05-30 12:10:07 -0400532 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 Malita42684332019-07-26 14:54:40 -0400541 const auto shape_result = skottie::Shaper::Shape(text, desc, text_box,
542 SkFontMgr::RefDefault());
Florin Malita0fe004c2019-05-30 12:10:07 -0400543 // 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 Malita42684332019-07-26 14:54:40 -0400550 const auto shape_result = skottie::Shaper::Shape(text, desc, text_box,
551 SkFontMgr::RefDefault());
Florin Malita0fe004c2019-05-30 12:10:07 -0400552 // 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 Malita42684332019-07-26 14:54:40 -0400560
561#if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && !defined(SK_BUILD_FOR_WIN)
562
563DEF_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 Malita02f345f2019-07-28 10:58:40 -0400640 REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 0);
Florin Malita42684332019-07-26 14:54:40 -0400641 }
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 Malita02f345f2019-07-28 10:58:40 -0400651 REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 1ul);
Florin Malita42684332019-07-26 14:54:40 -0400652 }
653}
654
655#endif