blob: f0f7c58f2b89006ebfd4e114d60f03c71a962daf [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",
Florin Malita42725ae2019-12-16 12:58:52 -050081 "mn": "ADBE Fill",
Florin Malitad9a44102019-07-30 09:02:16 -040082 "ty": 21
83 }],
Florin Malitaa85f3a12018-09-24 17:24:59 -040084 "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 Parchuri3a543aa2019-08-05 18:30:25 -0700103 },
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 Malita13c4dbe2020-08-21 12:45:30 -0400121 "lh": 120,
122 "ls": 12
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700123 }
124 }
125 ]
126 }
127 }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400128 }
129 ]
Greg Daniel08ada8e2019-08-05 20:00:41 +0000130 })";
Florin Malitaa85f3a12018-09-24 17:24:59 -0400131
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700132
Florin Malitaa85f3a12018-09-24 17:24:59 -0400133 class TestPropertyObserver final : public PropertyObserver {
134 public:
135 struct ColorInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700136 SkString node_name;
137 std::unique_ptr<skottie::ColorPropertyHandle> handle;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400138 };
139
140 struct OpacityInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700141 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 Malitaa85f3a12018-09-24 17:24:59 -0400148 };
149
150 struct TransformInfo {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700151 SkString node_name;
152 std::unique_ptr<skottie::TransformPropertyHandle> handle;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400153 };
154
155 void onColorProperty(const char node_name[],
156 const PropertyObserver::LazyHandle<ColorPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700157 fColors.push_back({SkString(node_name), lh()});
Shachar Langbeheimbe28d2e2020-02-11 09:48:50 +0200158 fColorsWithFullKeypath.push_back({SkString(fCurrentNode.c_str()), lh()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400159 }
160
161 void onOpacityProperty(const char node_name[],
162 const PropertyObserver::LazyHandle<OpacityPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700163 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 Malitaa85f3a12018-09-24 17:24:59 -0400169 }
170
171 void onTransformProperty(const char node_name[],
172 const PropertyObserver::LazyHandle<TransformPropertyHandle>& lh) override {
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700173 fTransforms.push_back({SkString(node_name), lh()});
Florin Malitaa85f3a12018-09-24 17:24:59 -0400174 }
175
Shachar Langbeheimbe28d2e2020-02-11 09:48:50 +0200176 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 Malitaa85f3a12018-09-24 17:24:59 -0400188 const std::vector<ColorInfo>& colors() const { return fColors; }
189 const std::vector<OpacityInfo>& opacities() const { return fOpacities; }
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700190 const std::vector<TextInfo>& texts() const { return fTexts; }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400191 const std::vector<TransformInfo>& transforms() const { return fTransforms; }
Shachar Langbeheimbe28d2e2020-02-11 09:48:50 +0200192 const std::vector<ColorInfo>& colorsWithFullKeypath() const {
193 return fColorsWithFullKeypath;
194 }
Florin Malitaa85f3a12018-09-24 17:24:59 -0400195
196 private:
197 std::vector<ColorInfo> fColors;
198 std::vector<OpacityInfo> fOpacities;
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700199 std::vector<TextInfo> fTexts;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400200 std::vector<TransformInfo> fTransforms;
Shachar Langbeheimbe28d2e2020-02-11 09:48:50 +0200201 std::string fCurrentNode;
202 std::vector<ColorInfo> fColorsWithFullKeypath;
Florin Malitaa85f3a12018-09-24 17:24:59 -0400203 };
204
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700205 // 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 Daniel08ada8e2019-08-05 20:00:41 +0000251 SkMemoryStream stream(json, strlen(json));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400252 auto observer = sk_make_sp<TestPropertyObserver>();
253
254 auto animation = skottie::Animation::Builder()
255 .setPropertyObserver(observer)
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700256 .setFontManager(test_font_manager)
Florin Malitaa85f3a12018-09-24 17:24:59 -0400257 .make(&stream);
258
259 REPORTER_ASSERT(reporter, animation);
260
261 const auto& colors = observer->colors();
Florin Malitad9a44102019-07-30 09:02:16 -0400262 REPORTER_ASSERT(reporter, colors.size() == 2);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400263 REPORTER_ASSERT(reporter, colors[0].node_name.equals("fill_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700264 REPORTER_ASSERT(reporter, colors[0].handle->get() == 0xffff0000);
Florin Malitad9a44102019-07-30 09:02:16 -0400265 REPORTER_ASSERT(reporter, colors[1].node_name.equals("fill_effect_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700266 REPORTER_ASSERT(reporter, colors[1].handle->get() == 0xff00ff00);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400267
Shachar Langbeheimbe28d2e2020-02-11 09:48:50 +0200268 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 Malitaa85f3a12018-09-24 17:24:59 -0400275 const auto& opacities = observer->opacities();
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700276 REPORTER_ASSERT(reporter, opacities.size() == 3);
Florin Malitaa85f3a12018-09-24 17:24:59 -0400277 REPORTER_ASSERT(reporter, opacities[0].node_name.equals("shape_transform_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700278 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[0].handle->get(), 100));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400279 REPORTER_ASSERT(reporter, opacities[1].node_name.equals("layer_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700280 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50));
Florin Malitaa85f3a12018-09-24 17:24:59 -0400281
282 const auto& transforms = observer->transforms();
Florin Malitac6fbedc2019-10-28 13:57:12 -0400283 REPORTER_ASSERT(reporter, transforms.size() == 3);
Florin Malita60c84fd2019-07-02 18:02:54 -0400284 REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700285 REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({
Florin Malita8ac81b72018-11-28 11:39:39 -0500286 SkPoint::Make(0, 0),
287 SkPoint::Make(0, 0),
Florin Malita60c84fd2019-07-02 18:02:54 -0400288 SkVector::Make(100, 100),
Florin Malita8ac81b72018-11-28 11:39:39 -0500289 0,
290 0,
291 0
292 }));
Florin Malitac6fbedc2019-10-28 13:57:12 -0400293 REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_1"));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700294 REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({
Florin Malita8ac81b72018-11-28 11:39:39 -0500295 SkPoint::Make(0, 0),
Florin Malitac6fbedc2019-10-28 13:57:12 -0400296 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 Malita8ac81b72018-11-28 11:39:39 -0500305 SkPoint::Make(0, 0),
Florin Malita60c84fd2019-07-02 18:02:54 -0400306 SkVector::Make(50, 50),
Florin Malita8ac81b72018-11-28 11:39:39 -0500307 0,
308 0,
309 0
310 }));
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700311
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 Malita13c4dbe2020-08-21 12:45:30 -0400321 12,
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700322 0,
323 SkTextUtils::kLeft_Align,
324 Shaper::VAlign::kTopBaseline,
Florin Malitaad911002020-01-28 13:21:47 -0500325 Shaper::ResizePolicy::kNone,
Florin Malita71bcfef2020-10-02 14:15:50 -0400326 Shaper::LinebreakPolicy::kExplicit,
Florin Malitaebbacd82020-11-13 13:38:39 -0500327 Shaper::Direction::kLTR,
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700328 SkRect::MakeEmpty(),
329 SK_ColorTRANSPARENT,
330 SK_ColorTRANSPARENT,
Florin Malita082323b2020-07-10 09:58:48 -0400331 TextPaintOrder::kFillStroke,
Avinash Parchuri3a543aa2019-08-05 18:30:25 -0700332 false,
333 false
334 }));
Florin Malita94d4d3e2018-06-18 13:10:51 -0400335}
Florin Malitad9c56b42018-10-09 14:33:08 -0400336
337DEF_TEST(Skottie_Annotations, reporter) {
338 static constexpr char json[] = R"({
339 "v": "5.2.1",
340 "w": 100,
341 "h": 100,
Florin Malita91af8d82018-11-30 16:46:45 -0500342 "fr": 10,
Florin Malitad9c56b42018-10-09 14:33:08 -0400343 "ip": 0,
Florin Malita91af8d82018-11-30 16:46:45 -0500344 "op": 100,
Florin Malitad9c56b42018-10-09 14:33:08 -0400345 "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 Malita91af8d82018-11-30 16:46:45 -0500359 "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 Malitad9c56b42018-10-09 14:33:08 -0400371 })";
372
Florin Malita91af8d82018-11-30 16:46:45 -0500373 class TestMarkerObserver final : public MarkerObserver {
Florin Malitad9c56b42018-10-09 14:33:08 -0400374 public:
Florin Malita91af8d82018-11-30 16:46:45 -0500375 void onMarker(const char name[], float t0, float t1) override {
376 fMarkers.push_back(std::make_tuple(name, t0, t1));
Florin Malitad9c56b42018-10-09 14:33:08 -0400377 }
378
Florin Malita91af8d82018-11-30 16:46:45 -0500379 std::vector<std::tuple<std::string, float, float>> fMarkers;
Florin Malitad9c56b42018-10-09 14:33:08 -0400380 };
381
382 SkMemoryStream stream(json, strlen(json));
Florin Malita91af8d82018-11-30 16:46:45 -0500383 auto observer = sk_make_sp<TestMarkerObserver>();
Florin Malitad9c56b42018-10-09 14:33:08 -0400384
385 auto animation = skottie::Animation::Builder()
Florin Malita91af8d82018-11-30 16:46:45 -0500386 .setMarkerObserver(observer)
Florin Malitad9c56b42018-10-09 14:33:08 -0400387 .make(&stream);
388
389 REPORTER_ASSERT(reporter, animation);
Florin Malita8bfe3912020-08-07 09:05:50 -0400390 REPORTER_ASSERT(reporter, animation->duration() == 10);
391 REPORTER_ASSERT(reporter, animation->inPoint() == 0.0);
392 REPORTER_ASSERT(reporter, animation->outPoint() == 100.0);
Florin Malitad9c56b42018-10-09 14:33:08 -0400393
Florin Malita91af8d82018-11-30 16:46:45 -0500394 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 Malitad9c56b42018-10-09 14:33:08 -0400401}
Florin Malita0d33eac2019-03-28 13:41:53 -0400402
Florin Malita87e88502019-06-12 22:02:50 -0400403static 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
428static 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 Malita347a9702019-04-10 17:04:46 +0000439DEF_TEST(Skottie_Shaper_HAlign, reporter) {
Florin Malita0d33eac2019-03-28 13:41:53 -0400440 auto typeface = SkTypeface::MakeDefault();
441 REPORTER_ASSERT(reporter, typeface);
442
443 static constexpr struct {
444 SkScalar text_size,
445 tolerance;
446 } kTestSizes[] = {
Florin Malita20678f82019-03-28 18:35:44 -0400447 // 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 Malita0d33eac2019-03-28 13:41:53 -0400453 };
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 Malitaa50484a2019-05-08 11:56:27 -0400473 tsize.text_size,
Florin Malitad75fed42019-06-27 09:23:03 -0400474 0,
Florin Malita13c4dbe2020-08-21 12:45:30 -0400475 0,
Florin Malita0d33eac2019-03-28 13:41:53 -0400476 talign.align,
Florin Malita71bcfef2020-10-02 14:15:50 -0400477 Shaper::VAlign::kTopBaseline,
478 Shaper::ResizePolicy::kNone,
479 Shaper::LinebreakPolicy::kExplicit,
Florin Malitaebbacd82020-11-13 13:38:39 -0500480 Shaper::Direction::kLTR,
Florin Malita0fe004c2019-05-30 12:10:07 -0400481 Shaper::Flags::kNone
Florin Malita0d33eac2019-03-28 13:41:53 -0400482 };
483
Florin Malita71bcfef2020-10-02 14:15:50 -0400484 const auto shape_result = Shaper::Shape(text, desc, text_point,
485 SkFontMgr::RefDefault());
Florin Malitaa8d18192019-05-28 12:58:18 -0400486 REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul);
487 REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob);
Florin Malita0d33eac2019-03-28 13:41:53 -0400488
Florin Malita87e88502019-06-12 22:02:50 -0400489 const auto shape_bounds = ComputeShapeResultBounds(shape_result);
Florin Malita0d33eac2019-03-28 13:41:53 -0400490 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 Wagner3bdb69c2019-04-01 19:01:09 -0400494 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 Malita0d33eac2019-03-28 13:41:53 -0400497
498 const auto expected_r = text_point.x() + shape_bounds.width() * talign.r_selector;
499 REPORTER_ASSERT(reporter,
Ben Wagner3bdb69c2019-04-01 19:01:09 -0400500 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 Malita0d33eac2019-03-28 13:41:53 -0400503
504 }
505 }
506}
Florin Malita347a9702019-04-10 17:04:46 +0000507
508DEF_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 Malita87e88502019-06-12 22:02:50 -0400519 { 10, 4.0f },
520 { 15, 5.5f },
521 { 25, 8.0f },
Florin Malita347a9702019-04-10 17:04:46 +0000522 };
523
524 struct {
525 skottie::Shaper::VAlign align;
526 SkScalar topFactor;
527 } kTestAligns[] = {
Florin Malitaafd2c102019-06-27 14:54:54 -0400528 { skottie::Shaper::VAlign::kVisualTop , 0.0f },
529 { skottie::Shaper::VAlign::kVisualCenter, 0.5f },
Florin Malita347a9702019-04-10 17:04:46 +0000530 // 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 Malitaa50484a2019-05-08 11:56:27 -0400542 tsize.text_size,
Florin Malitad75fed42019-06-27 09:23:03 -0400543 0,
Florin Malita13c4dbe2020-08-21 12:45:30 -0400544 0,
Florin Malita347a9702019-04-10 17:04:46 +0000545 SkTextUtils::Align::kCenter_Align,
546 talign.align,
Florin Malita71bcfef2020-10-02 14:15:50 -0400547 Shaper::ResizePolicy::kNone,
548 Shaper::LinebreakPolicy::kParagraph,
Florin Malitaebbacd82020-11-13 13:38:39 -0500549 Shaper::Direction::kLTR,
Florin Malita0fe004c2019-05-30 12:10:07 -0400550 Shaper::Flags::kNone
Florin Malita347a9702019-04-10 17:04:46 +0000551 };
552
Florin Malita71bcfef2020-10-02 14:15:50 -0400553 const auto shape_result = Shaper::Shape(text, desc, text_box, SkFontMgr::RefDefault());
Florin Malitaa8d18192019-05-28 12:58:18 -0400554 REPORTER_ASSERT(reporter, shape_result.fFragments.size() == 1ul);
555 REPORTER_ASSERT(reporter, shape_result.fFragments[0].fBlob);
Florin Malita347a9702019-04-10 17:04:46 +0000556
Florin Malita87e88502019-06-12 22:02:50 -0400557 const auto shape_bounds = ComputeShapeResultBounds(shape_result);
Florin Malita347a9702019-04-10 17:04:46 +0000558 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 Malita0fe004c2019-05-30 12:10:07 -0400576
577DEF_TEST(Skottie_Shaper_FragmentGlyphs, reporter) {
578 skottie::Shaper::TextDesc desc = {
579 SkTypeface::MakeDefault(),
580 18,
581 18,
Florin Malitad75fed42019-06-27 09:23:03 -0400582 0,
Florin Malita13c4dbe2020-08-21 12:45:30 -0400583 0,
Florin Malita0fe004c2019-05-30 12:10:07 -0400584 SkTextUtils::Align::kCenter_Align,
585 Shaper::VAlign::kTop,
Florin Malita71bcfef2020-10-02 14:15:50 -0400586 Shaper::ResizePolicy::kNone,
587 Shaper::LinebreakPolicy::kParagraph,
Florin Malitaebbacd82020-11-13 13:38:39 -0500588 Shaper::Direction::kLTR,
Florin Malita0fe004c2019-05-30 12:10:07 -0400589 Shaper::Flags::kNone
590 };
591
592 const SkString text("Foo bar baz");
593 const auto text_box = SkRect::MakeWH(100, 100);
594
595 {
Florin Malita71bcfef2020-10-02 14:15:50 -0400596 const auto shape_result = Shaper::Shape(text, desc, text_box, SkFontMgr::RefDefault());
Florin Malita0fe004c2019-05-30 12:10:07 -0400597 // 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 Malita42684332019-07-26 14:54:40 -0400604 const auto shape_result = skottie::Shaper::Shape(text, desc, text_box,
605 SkFontMgr::RefDefault());
Florin Malita0fe004c2019-05-30 12:10:07 -0400606 // 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 Malita42684332019-07-26 14:54:40 -0400614
615#if defined(SK_SHAPER_HARFBUZZ_AVAILABLE) && !defined(SK_BUILD_FOR_WIN)
616
617DEF_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 Malita13c4dbe2020-08-21 12:45:30 -0400680 0,
Florin Malita42684332019-07-26 14:54:40 -0400681 SkTextUtils::Align::kCenter_Align,
682 Shaper::VAlign::kTop,
Florin Malitaad911002020-01-28 13:21:47 -0500683 Shaper::ResizePolicy::kNone,
Florin Malita71bcfef2020-10-02 14:15:50 -0400684 Shaper::LinebreakPolicy::kParagraph,
Florin Malitaebbacd82020-11-13 13:38:39 -0500685 Shaper::Direction::kLTR,
Florin Malita42684332019-07-26 14:54:40 -0400686 Shaper::Flags::kNone
687 };
688
689 const auto text_box = SkRect::MakeWH(100, 100);
690
691 {
Florin Malita71bcfef2020-10-02 14:15:50 -0400692 const auto shape_result = Shaper::Shape(SkString("foo bar"), desc, text_box, fontmgr);
Florin Malita42684332019-07-26 14:54:40 -0400693
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 Malita02f345f2019-07-28 10:58:40 -0400697 REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 0);
Florin Malita42684332019-07-26 14:54:40 -0400698 }
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 Malita02f345f2019-07-28 10:58:40 -0400708 REPORTER_ASSERT(reporter, shape_result.fMissingGlyphCount == 1ul);
Florin Malita42684332019-07-26 14:54:40 -0400709 }
710}
711
712#endif
Florin Malitacc92b272019-12-05 09:43:35 -0500713
714DEF_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 Salomond0072812020-07-21 17:03:56 -0400725 const char id[]) const override {
Florin Malitacc92b272019-12-05 09:43:35 -0500726 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}