1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkFontStyle.h"
15 #include "include/core/SkFontTypes.h"
16 #include "include/core/SkImageInfo.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkPaint.h"
19 #include "include/core/SkPoint.h"
20 #include "include/core/SkRefCnt.h"
21 #include "include/core/SkScalar.h"
22 #include "include/core/SkSurface.h"
23 #include "include/core/SkSurfaceProps.h"
24 #include "include/core/SkTextBlob.h"
25 #include "include/core/SkTypeface.h"
26 #include "include/core/SkTypes.h"
27 #include "include/gpu/GpuTypes.h"
28 #include "include/gpu/GrDirectContext.h"
29 #include "src/core/SkDevice.h"
30 #include "src/core/SkScalerContext.h"
31 #include "src/text/GlyphRun.h"
32 #include "src/text/gpu/SDFTControl.h"
33 #include "src/text/gpu/SubRunAllocator.h"
34 #include "src/text/gpu/TextBlob.h"
35 #include "tests/CtsEnforcement.h"
36 #include "tests/Test.h"
37 #include "tools/ToolUtils.h"
38
39 #include <cmath>
40 #include <cstddef>
41 #include <cstdint>
42 #include <limits>
43 #include <memory>
44 #include <tuple>
45 #include <utility>
46
47 class GrRecordingContext;
48 struct GrContextOptions;
49
50 using BagOfBytes = sktext::gpu::BagOfBytes;
51 using SubRunAllocator = sktext::gpu::SubRunAllocator;
52
rasterize_blob(SkTextBlob * blob,const SkPaint & paint,GrRecordingContext * rContext,const SkMatrix & matrix)53 SkBitmap rasterize_blob(SkTextBlob* blob,
54 const SkPaint& paint,
55 GrRecordingContext* rContext,
56 const SkMatrix& matrix) {
57 const SkImageInfo info =
58 SkImageInfo::Make(500, 500, kN32_SkColorType, kPremul_SkAlphaType);
59 auto surface = SkSurface::MakeRenderTarget(rContext, skgpu::Budgeted::kNo, info);
60 auto canvas = surface->getCanvas();
61 canvas->drawColor(SK_ColorWHITE);
62 canvas->concat(matrix);
63 canvas->drawTextBlob(blob, 10, 250, paint);
64 SkBitmap bitmap;
65 bitmap.allocN32Pixels(500, 500);
66 surface->readPixels(bitmap, 0, 0);
67 return bitmap;
68 }
69
check_for_black(const SkBitmap & bm)70 bool check_for_black(const SkBitmap& bm) {
71 for (int y = 0; y < bm.height(); y++) {
72 for (int x = 0; x < bm.width(); x++) {
73 if (bm.getColor(x, y) == SK_ColorBLACK) {
74 return true;
75 }
76 }
77 }
78 return false;
79 }
80
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrTextBlobScaleAnimation,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)81 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrTextBlobScaleAnimation,
82 reporter,
83 ctxInfo,
84 CtsEnforcement::kApiLevel_T) {
85 auto tf = ToolUtils::create_portable_typeface("Mono", SkFontStyle());
86 SkFont font{tf};
87 font.setHinting(SkFontHinting::kNormal);
88 font.setSize(12);
89 font.setEdging(SkFont::Edging::kAntiAlias);
90 font.setSubpixel(true);
91
92 SkTextBlobBuilder builder;
93 const auto& runBuffer = builder.allocRunPosH(font, 30, 0, nullptr);
94
95 for (int i = 0; i < 30; i++) {
96 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
97 runBuffer.pos[i] = SkIntToScalar(i);
98 }
99 auto blob = builder.make();
100
101 auto dContext = ctxInfo.directContext();
102 bool anyBlack = false;
103 for (int n = -13; n < 5; n++) {
104 SkMatrix m = SkMatrix::Scale(std::exp2(n), std::exp2(n));
105 auto bm = rasterize_blob(blob.get(), SkPaint(), dContext, m);
106 anyBlack |= check_for_black(bm);
107 }
108 REPORTER_ASSERT(reporter, anyBlack);
109 }
110
111 // Test extreme positions for all combinations of positions, origins, and translation matrices.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrTextBlobMoveAround,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)112 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrTextBlobMoveAround,
113 reporter,
114 ctxInfo,
115 CtsEnforcement::kApiLevel_T) {
116 auto tf = ToolUtils::create_portable_typeface("Mono", SkFontStyle());
117 SkFont font{tf};
118 font.setHinting(SkFontHinting::kNormal);
119 font.setSize(12);
120 font.setEdging(SkFont::Edging::kAntiAlias);
121 font.setSubpixel(true);
122
123 auto makeBlob = [&](SkPoint delta) {
124 SkTextBlobBuilder builder;
125 const auto& runBuffer = builder.allocRunPos(font, 30, nullptr);
126
127 for (int i = 0; i < 30; i++) {
128 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
129 runBuffer.points()[i] = SkPoint::Make(SkIntToScalar(i*10) + delta.x(), 50 + delta.y());
130 }
131 return builder.make();
132 };
133
134 auto dContext = ctxInfo.directContext();
135 auto rasterizeBlob = [&](SkTextBlob* blob, SkPoint origin, const SkMatrix& matrix) {
136 SkPaint paint;
137 const SkImageInfo info =
138 SkImageInfo::Make(350, 80, kN32_SkColorType, kPremul_SkAlphaType);
139 auto surface = SkSurface::MakeRenderTarget(dContext, skgpu::Budgeted::kNo, info);
140 auto canvas = surface->getCanvas();
141 canvas->drawColor(SK_ColorWHITE);
142 canvas->concat(matrix);
143 canvas->drawTextBlob(blob, 10 + origin.x(), 40 + origin.y(), paint);
144 SkBitmap bitmap;
145 bitmap.allocN32Pixels(350, 80);
146 surface->readPixels(bitmap, 0, 0);
147 return bitmap;
148 };
149
150 SkBitmap benchMark;
151 {
152 auto blob = makeBlob({0, 0});
153 benchMark = rasterizeBlob(blob.get(), {0,0}, SkMatrix::I());
154 }
155
156 auto checkBitmap = [&](const SkBitmap& bitmap) {
157 REPORTER_ASSERT(reporter, benchMark.width() == bitmap.width());
158 REPORTER_ASSERT(reporter, benchMark.width() == bitmap.width());
159
160 for (int y = 0; y < benchMark.height(); y++) {
161 for (int x = 0; x < benchMark.width(); x++) {
162 if (benchMark.getColor(x, y) != bitmap.getColor(x, y)) {
163 return false;
164 }
165 }
166 }
167 return true;
168 };
169
170 SkScalar interestingNumbers[] = {-10'000'000, -1'000'000, -1, 0, +1, +1'000'000, +10'000'000};
171 for (auto originX : interestingNumbers) {
172 for (auto originY : interestingNumbers) {
173 for (auto translateX : interestingNumbers) {
174 for (auto translateY : interestingNumbers) {
175 // Make sure everything adds to zero.
176 SkScalar deltaPosX = -(originX + translateX);
177 SkScalar deltaPosY = -(originY + translateY);
178 auto blob = makeBlob({deltaPosX, deltaPosY});
179 SkMatrix t = SkMatrix::Translate(translateX, translateY);
180 auto bitmap = rasterizeBlob(blob.get(), {originX, originY}, t);
181 REPORTER_ASSERT(reporter, checkBitmap(bitmap));
182 }
183 }
184 }
185 }
186 }
187
DEF_TEST(BagOfBytesBasic,r)188 DEF_TEST(BagOfBytesBasic, r) {
189 const int k4K = 1 << 12;
190 {
191 // GrBagOfBytes::MinimumSizeWithOverhead(-1); // This should fail
192 BagOfBytes::PlatformMinimumSizeWithOverhead(0, 16);
193 BagOfBytes::PlatformMinimumSizeWithOverhead(
194 std::numeric_limits<int>::max() - k4K - 1, 16);
195 // GrBagOfBytes::MinimumSizeWithOverhead(std::numeric_limits<int>::max() - k4K); // Fail
196 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(0, 1, 16, 16) == 31);
197 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(1, 1, 16, 16) == 32);
198 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(63, 1, 16, 16) == 94);
199 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(0, 8, 16, 16) == 24);
200 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(1, 8, 16, 16) == 32);
201 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(63, 8, 16, 16) == 88);
202 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(0, 16, 16, 16) == 16);
203 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(1, 16, 16, 16) == 32);
204 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(63, 16, 16, 16) == 80);
205
206 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(0, 1, 8, 16) == 23);
207 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(1, 1, 8, 16) == 24);
208 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(63, 1, 8, 16) == 86);
209 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(0, 8, 8, 16) == 16);
210 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(1, 8, 8, 16) == 24);
211 REPORTER_ASSERT(r, BagOfBytes::MinimumSizeWithOverhead(63, 8, 8, 16) == 80);
212 }
213
214 {
215 BagOfBytes bob;
216 // bob.alignedBytes(0, 1); // This should fail
217 // bob.alignedBytes(1, 0); // This should fail
218 // bob.alignedBytes(1, 3); // This should fail
219
220 struct Big {
221 char stuff[std::numeric_limits<int>::max()];
222 };
223 // bob.alignedBytes(sizeof(Big), 1); // this should fail
224 // bob.allocateBytesFor<Big>(); // this should not compile
225 // The following should run, but should not be regularly tested.
226 // bob.allocateBytesFor<int>((std::numeric_limits<int>::max() - (1<<12)) / sizeof(int) - 1);
227 // The following should fail
228 // bob.allocateBytesFor<int>((std::numeric_limits<int>::max() - (1<<12)) / sizeof(int));
229 bob.alignedBytes(1, 1); // To avoid unused variable problems.
230 }
231
232 // Force multiple block allocation
233 {
234 BagOfBytes bob;
235 const int k64K = 1 << 16;
236 // By default allocation block sizes start at 1K and go up with fib. This should allocate
237 // 10 individual blocks.
238 for (int i = 0; i < 10; i++) {
239 bob.alignedBytes(k64K, 1);
240 }
241 }
242 }
243
DEF_TEST(SubRunAllocator,r)244 DEF_TEST(SubRunAllocator, r) {
245 static int created = 0;
246 static int destroyed = 0;
247 struct Foo {
248 Foo() : fI{-2}, fX{-3} { created++; }
249 Foo(int i, float x) : fI{i}, fX{x} { created++; }
250 ~Foo() { destroyed++; }
251 int fI;
252 float fX;
253 };
254
255 struct alignas(8) OddAlignment {
256 char buf[10];
257 };
258
259 auto exercise = [&](SubRunAllocator* alloc) {
260 created = 0;
261 destroyed = 0;
262 {
263 int* p = alloc->makePOD<int>(3);
264 REPORTER_ASSERT(r, *p == 3);
265 int* q = alloc->makePOD<int>(7);
266 REPORTER_ASSERT(r, *q == 7);
267
268 REPORTER_ASSERT(r, *alloc->makePOD<int>(3) == 3);
269 auto foo = alloc->makeUnique<Foo>(3, 4.0f);
270 REPORTER_ASSERT(r, foo->fI == 3);
271 REPORTER_ASSERT(r, foo->fX == 4.0f);
272 REPORTER_ASSERT(r, created == 1);
273 REPORTER_ASSERT(r, destroyed == 0);
274
275 alloc->makePODArray<int>(10);
276
277 auto fooArray = alloc->makeUniqueArray<Foo>(10);
278 REPORTER_ASSERT(r, fooArray[3].fI == -2);
279 REPORTER_ASSERT(r, fooArray[4].fX == -3.0f);
280 REPORTER_ASSERT(r, created == 11);
281 REPORTER_ASSERT(r, destroyed == 0);
282 alloc->makePOD<OddAlignment>();
283 }
284
285 REPORTER_ASSERT(r, created == 11);
286 REPORTER_ASSERT(r, destroyed == 11);
287 };
288
289 // Exercise default arena
290 {
291 SubRunAllocator arena{0};
292 exercise(&arena);
293 }
294
295 // Exercise on stack arena
296 {
297 sktext::gpu::STSubRunAllocator<64, 16> arena;
298 exercise(&arena);
299 }
300
301 // Exercise arena with a heap allocated starting block
302 {
303 std::unique_ptr<char[]> block{new char[1024]};
304 SubRunAllocator arena{block.get(), 1024, 0};
305 exercise(&arena);
306 }
307
308 // Exercise the singly-link list of unique_ptrs use case
309 {
310 created = 0;
311 destroyed = 0;
312 SubRunAllocator arena;
313
314 struct Node {
315 Node(std::unique_ptr<Node, SubRunAllocator::Destroyer> next)
316 : fNext{std::move(next)} { created++; }
317 ~Node() { destroyed++; }
318 std::unique_ptr<Node, SubRunAllocator::Destroyer> fNext;
319 };
320
321 std::unique_ptr<Node, SubRunAllocator::Destroyer> current = nullptr;
322 for (int i = 0; i < 128; i++) {
323 current = arena.makeUnique<Node>(std::move(current));
324 }
325 REPORTER_ASSERT(r, created == 128);
326 REPORTER_ASSERT(r, destroyed == 0);
327 }
328 REPORTER_ASSERT(r, created == 128);
329 REPORTER_ASSERT(r, destroyed == 128);
330
331 // Exercise the array ctor w/ a mapping function
332 {
333 struct I {
334 I(int v) : i{v} {}
335 ~I() {}
336 int i;
337 };
338 sktext::gpu::STSubRunAllocator<64, 16> arena;
339 auto a = arena.makeUniqueArray<I>(8, [](size_t i) { return i; });
340 for (size_t i = 0; i < 8; i++) {
341 REPORTER_ASSERT(r, a[i].i == (int)i);
342 }
343 }
344
345 {
346 SubRunAllocator arena(4096);
347 void* ptr = arena.alignedBytes(4081, 8);
348 REPORTER_ASSERT(r, ((intptr_t)ptr & 7) == 0);
349 }
350 }
351
352 using TextBlob = sktext::gpu::TextBlob;
353
DEF_TEST(KeyEqualityOnPerspective,r)354 DEF_TEST(KeyEqualityOnPerspective, r) {
355 SkTextBlobBuilder builder;
356 SkFont font(SkTypeface::MakeDefault(), 16);
357 auto runBuffer = builder.allocRun(font, 1, 0.0f, 0.0f);
358 runBuffer.glyphs[0] = 3;
359 auto blob = builder.make();
360 sktext::GlyphRunBuilder grBuilder;
361 auto glyphRunList = grBuilder.blobToGlyphRunList(*blob, {100, 100});
362 SkPaint paint;
363
364 // Build the strike device.
365 SkSurfaceProps props;
366 #if !defined(SK_DISABLE_SDF_TEXT)
367 sktext::gpu::SDFTControl control(false, false, false, 1, 100);
368 #else
369 sktext::gpu::SDFTControl control{};
370 #endif
371 SkStrikeDeviceInfo strikeDevice{props, SkScalerContextFlags::kBoostContrast, &control};
372 SkMatrix matrix1;
373 matrix1.setAll(1, 0, 0, 0, 1, 0, 1, 1, 1);
374 SkMatrix matrix2;
375 matrix2.setAll(1, 0, 0, 0, 1, 0, 2, 2, 1);
376 auto key1 = std::get<1>(
377 TextBlob::Key::Make(glyphRunList, paint, matrix1, strikeDevice));
378 auto key2 = std::get<1>(
379 TextBlob::Key::Make(glyphRunList, paint, matrix1, strikeDevice));
380 auto key3 = std::get<1>(
381 TextBlob::Key::Make(glyphRunList, paint, matrix2, strikeDevice));
382 REPORTER_ASSERT(r, key1 == key2);
383 REPORTER_ASSERT(r, key1 == key3);
384 }
385