• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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