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
8 #include "include/core/SkGraphics.h"
9 #include "include/core/SkSurface.h"
10 #include "include/core/SkTextBlob.h"
11 #include "include/private/SkMutex.h"
12 #include "src/core/SkDraw.h"
13 #include "src/core/SkRemoteGlyphCache.h"
14 #include "src/core/SkStrike.h"
15 #include "src/core/SkStrikeCache.h"
16 #include "src/core/SkStrikeSpec.h"
17 #include "src/core/SkSurfacePriv.h"
18 #include "src/core/SkTypeface_remote.h"
19 #include "tests/Test.h"
20 #include "tools/Resources.h"
21 #include "tools/ToolUtils.h"
22 #include "tools/fonts/TestEmptyTypeface.h"
23
24 #include "src/gpu/text/GrTextContext.h"
25
26 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
27 public SkStrikeClient::DiscardableHandleManager {
28 public:
DiscardableManager()29 DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
30 ~DiscardableManager() override = default;
31
32 // Server implementation.
createHandle()33 SkDiscardableHandleId createHandle() override {
34 SkAutoMutexExclusive l(fMutex);
35
36 // Handles starts as locked.
37 fLockedHandles.add(++fNextHandleId);
38 return fNextHandleId;
39 }
lockHandle(SkDiscardableHandleId id)40 bool lockHandle(SkDiscardableHandleId id) override {
41 SkAutoMutexExclusive l(fMutex);
42
43 if (id <= fLastDeletedHandleId) return false;
44 fLockedHandles.add(id);
45 return true;
46 }
47
48 // Client implementation.
deleteHandle(SkDiscardableHandleId id)49 bool deleteHandle(SkDiscardableHandleId id) override {
50 SkAutoMutexExclusive l(fMutex);
51
52 return id <= fLastDeletedHandleId;
53 }
54
notifyCacheMiss(SkStrikeClient::CacheMissType type)55 void notifyCacheMiss(SkStrikeClient::CacheMissType type) override {
56 SkAutoMutexExclusive l(fMutex);
57
58 fCacheMissCount[type]++;
59 }
isHandleDeleted(SkDiscardableHandleId id)60 bool isHandleDeleted(SkDiscardableHandleId id) override {
61 SkAutoMutexExclusive l(fMutex);
62
63 return id <= fLastDeletedHandleId;
64 }
65
unlockAll()66 void unlockAll() {
67 SkAutoMutexExclusive l(fMutex);
68
69 fLockedHandles.reset();
70 }
unlockAndDeleteAll()71 void unlockAndDeleteAll() {
72 SkAutoMutexExclusive l(fMutex);
73
74 fLockedHandles.reset();
75 fLastDeletedHandleId = fNextHandleId;
76 }
lockedHandles() const77 const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const {
78 SkAutoMutexExclusive l(fMutex);
79
80 return fLockedHandles;
81 }
handleCount()82 SkDiscardableHandleId handleCount() {
83 SkAutoMutexExclusive l(fMutex);
84
85 return fNextHandleId;
86 }
cacheMissCount(uint32_t type)87 int cacheMissCount(uint32_t type) {
88 SkAutoMutexExclusive l(fMutex);
89
90 return fCacheMissCount[type];
91 }
hasCacheMiss() const92 bool hasCacheMiss() const {
93 SkAutoMutexExclusive l(fMutex);
94
95 for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
96 if (fCacheMissCount[i] > 0) return true;
97 }
98 return false;
99 }
resetCacheMissCounts()100 void resetCacheMissCounts() {
101 SkAutoMutexExclusive l(fMutex);
102 sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount));
103 }
104
105 private:
106 // The tests below run in parallel on multiple threads and use the same
107 // process global SkStrikeCache. So the implementation needs to be
108 // thread-safe.
109 mutable SkMutex fMutex;
110
111 SkDiscardableHandleId fNextHandleId = 0u;
112 SkDiscardableHandleId fLastDeletedHandleId = 0u;
113 SkTHashSet<SkDiscardableHandleId> fLockedHandles;
114 int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
115 };
116
buildTextBlob(sk_sp<SkTypeface> tf,int glyphCount)117 sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
118 SkFont font;
119 font.setTypeface(tf);
120 font.setHinting(SkFontHinting::kNormal);
121 font.setSize(1u);
122 font.setEdging(SkFont::Edging::kAntiAlias);
123 font.setSubpixel(true);
124
125 SkTextBlobBuilder builder;
126 SkRect bounds = SkRect::MakeWH(10, 10);
127 const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
128 SkASSERT(runBuffer.utf8text == nullptr);
129 SkASSERT(runBuffer.clusters == nullptr);
130
131 for (int i = 0; i < glyphCount; i++) {
132 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
133 runBuffer.pos[i] = SkIntToScalar(i);
134 }
135 return builder.make();
136 }
137
compare_blobs(const SkBitmap & expected,const SkBitmap & actual,skiatest::Reporter * reporter,int tolerance=0)138 static void compare_blobs(const SkBitmap& expected, const SkBitmap& actual,
139 skiatest::Reporter* reporter, int tolerance = 0) {
140 SkASSERT(expected.width() == actual.width());
141 SkASSERT(expected.height() == actual.height());
142 for (int i = 0; i < expected.width(); ++i) {
143 for (int j = 0; j < expected.height(); ++j) {
144 SkColor expectedColor = expected.getColor(i, j);
145 SkColor actualColor = actual.getColor(i, j);
146 if (0 == tolerance) {
147 REPORTER_ASSERT(reporter, expectedColor == actualColor);
148 } else {
149 for (int k = 0; k < 4; ++k) {
150 int expectedChannel = (expectedColor >> (k*8)) & 0xff;
151 int actualChannel = (actualColor >> (k*8)) & 0xff;
152 REPORTER_ASSERT(reporter, abs(expectedChannel - actualChannel) <= tolerance);
153 }
154 }
155 }
156 }
157 }
158
MakeSurface(int width,int height,GrContext * context)159 sk_sp<SkSurface> MakeSurface(int width, int height, GrContext* context) {
160 const SkImageInfo info =
161 SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
162 return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
163 }
164
FindSurfaceProps(GrContext * context)165 const SkSurfaceProps FindSurfaceProps(GrContext* context) {
166 auto surface = MakeSurface(1, 1, context);
167 return surface->props();
168 }
169
RasterBlob(sk_sp<SkTextBlob> blob,int width,int height,const SkPaint & paint,GrContext * context,const SkMatrix * matrix=nullptr,SkScalar x=0)170 SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
171 GrContext* context, const SkMatrix* matrix = nullptr,
172 SkScalar x = 0) {
173 auto surface = MakeSurface(width, height, context);
174 if (matrix) surface->getCanvas()->concat(*matrix);
175 surface->getCanvas()->drawTextBlob(blob.get(), x, height/2, paint);
176 SkBitmap bitmap;
177 bitmap.allocN32Pixels(width, height);
178 surface->readPixels(bitmap, 0, 0);
179 return bitmap;
180 }
181
DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization,reporter)182 DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
183 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
184 SkStrikeServer server(discardableManager.get());
185 SkStrikeClient client(discardableManager, false);
186
187 auto server_tf = SkTypeface::MakeDefault();
188 auto tf_data = server.serializeTypeface(server_tf.get());
189
190 auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
191 REPORTER_ASSERT(reporter, client_tf);
192 REPORTER_ASSERT(reporter, static_cast<SkTypefaceProxy*>(client_tf.get())->remoteTypefaceID() ==
193 server_tf->uniqueID());
194
195 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
196 discardableManager->unlockAndDeleteAll();
197 }
198
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization,reporter,ctxInfo)199 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, reporter, ctxInfo) {
200 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
201 SkStrikeServer server(discardableManager.get());
202 SkStrikeClient client(discardableManager, false);
203 const SkPaint paint;
204
205 // Server.
206 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
207 auto serverTfData = server.serializeTypeface(serverTf.get());
208
209 int glyphCount = 10;
210 auto serverBlob = buildTextBlob(serverTf, glyphCount);
211 auto props = FindSurfaceProps(ctxInfo.grContext());
212 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server,
213 ctxInfo.grContext()->supportsDistanceFieldText());
214 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
215
216 std::vector<uint8_t> serverStrikeData;
217 server.writeStrikeData(&serverStrikeData);
218
219 // Client.
220 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
221 REPORTER_ASSERT(reporter,
222 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
223 auto clientBlob = buildTextBlob(clientTf, glyphCount);
224
225 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
226 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
227 compare_blobs(expected, actual, reporter);
228 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
229
230 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
231 discardableManager->unlockAndDeleteAll();
232 }
233
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,reporter,ctxInfo)234 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace, reporter, ctxInfo) {
235 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
236 SkStrikeServer server(discardableManager.get());
237 SkStrikeClient client(discardableManager, false);
238
239 // Server.
240 auto serverTf = TestEmptyTypeface::Make();
241 auto serverTfData = server.serializeTypeface(serverTf.get());
242 REPORTER_ASSERT(reporter, serverTf->unique());
243
244 {
245 const SkPaint paint;
246 int glyphCount = 10;
247 auto serverBlob = buildTextBlob(serverTf, glyphCount);
248 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
249 SkTextBlobCacheDiffCanvas cache_diff_canvas(
250 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
251 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
252 REPORTER_ASSERT(reporter, !serverTf->unique());
253
254 std::vector<uint8_t> serverStrikeData;
255 server.writeStrikeData(&serverStrikeData);
256 }
257 REPORTER_ASSERT(reporter, serverTf->unique());
258
259 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
260 discardableManager->unlockAndDeleteAll();
261 }
262
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer,reporter)263 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
264 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
265 SkStrikeServer server(discardableManager.get());
266 SkStrikeClient client(discardableManager, false);
267
268 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
269 server.serializeTypeface(serverTf.get());
270 int glyphCount = 10;
271 auto serverBlob = buildTextBlob(serverTf, glyphCount);
272
273 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
274 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
275 SkPaint paint;
276 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
277
278 // The strike from the blob should be locked after it has been drawn on the canvas.
279 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
280 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
281
282 // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
283 // again.
284 std::vector<uint8_t> fontData;
285 server.writeStrikeData(&fontData);
286 discardableManager->unlockAll();
287 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
288
289 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
290 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
291 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
292
293 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
294 discardableManager->unlockAndDeleteAll();
295 }
296
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer,reporter)297 DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
298 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
299 SkStrikeServer server(discardableManager.get());
300 SkStrikeClient client(discardableManager, false);
301
302 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
303 server.serializeTypeface(serverTf.get());
304 int glyphCount = 10;
305 auto serverBlob = buildTextBlob(serverTf, glyphCount);
306
307 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
308 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
309 SkPaint paint;
310 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
311 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
312
313 // Write the strike data and delete all the handles. Re-analyzing the blob should create new
314 // handles.
315 std::vector<uint8_t> fontData;
316 server.writeStrikeData(&fontData);
317 discardableManager->unlockAndDeleteAll();
318 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
319 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
320
321 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
322 discardableManager->unlockAndDeleteAll();
323 }
324
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient,reporter)325 DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
326 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
327 SkStrikeServer server(discardableManager.get());
328 SkStrikeClient client(discardableManager, false);
329
330 // Server.
331 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
332 auto serverTfData = server.serializeTypeface(serverTf.get());
333
334 int glyphCount = 10;
335 auto serverBlob = buildTextBlob(serverTf, glyphCount);
336
337 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
338 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
339 SkPaint paint;
340 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
341
342 std::vector<uint8_t> serverStrikeData;
343 server.writeStrikeData(&serverStrikeData);
344
345 // Client.
346 REPORTER_ASSERT(reporter,
347 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
348 auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
349
350 // The cache remains alive until it is pinned in the discardable manager.
351 SkGraphics::PurgeFontCache();
352 REPORTER_ASSERT(reporter, !clientTf->unique());
353
354 // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
355 // clientTf.
356 discardableManager->unlockAndDeleteAll();
357 SkGraphics::PurgeFontCache();
358 REPORTER_ASSERT(reporter, clientTf->unique());
359
360 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
361 discardableManager->unlockAndDeleteAll();
362 }
363
DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting,reporter)364 DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
365 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
366 SkStrikeServer server(discardableManager.get());
367 SkStrikeClient client(discardableManager, false);
368
369 // Server.
370 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
371 auto serverTfData = server.serializeTypeface(serverTf.get());
372
373 int glyphCount = 10;
374 auto serverBlob = buildTextBlob(serverTf, glyphCount);
375
376 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
377 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
378 SkPaint paint;
379 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
380
381 std::vector<uint8_t> serverStrikeData;
382 server.writeStrikeData(&serverStrikeData);
383
384 // Client.
385 REPORTER_ASSERT(reporter,
386 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
387 SkStrikeCache::ValidateGlyphCacheDataSize();
388
389 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
390 discardableManager->unlockAndDeleteAll();
391 }
392
DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries,reporter)393 DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
394 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
395 SkStrikeServer server(discardableManager.get());
396 server.setMaxEntriesInDescriptorMapForTesting(1u);
397 SkStrikeClient client(discardableManager, false);
398
399 {
400 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
401 int glyphCount = 10;
402 auto serverBlob = buildTextBlob(serverTf, glyphCount);
403
404 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
405 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
406 SkPaint paint;
407 REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 0u);
408 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
409 REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
410 }
411
412 // Serialize to release the lock from the strike server and delete all current
413 // handles.
414 std::vector<uint8_t> fontData;
415 server.writeStrikeData(&fontData);
416 discardableManager->unlockAndDeleteAll();
417
418 // Use a different typeface. Creating a new strike should evict the previous
419 // one.
420 {
421 auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
422 int glyphCount = 10;
423 auto serverBlob = buildTextBlob(serverTf, glyphCount);
424
425 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
426 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
427 SkPaint paint;
428 REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
429 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
430 REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
431 }
432
433 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
434 discardableManager->unlockAndDeleteAll();
435 }
436
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,reporter,ctxInfo)437 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
438 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
439 SkStrikeServer server(discardableManager.get());
440 SkStrikeClient client(discardableManager, false);
441 SkPaint paint;
442 paint.setStyle(SkPaint::kStroke_Style);
443 paint.setStrokeWidth(0);
444 REPORTER_ASSERT(reporter,
445 SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I()));
446
447 // Server.
448 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
449 auto serverTfData = server.serializeTypeface(serverTf.get());
450
451 int glyphCount = 10;
452 auto serverBlob = buildTextBlob(serverTf, glyphCount);
453 auto props = FindSurfaceProps(ctxInfo.grContext());
454 SkTextBlobCacheDiffCanvas cache_diff_canvas(
455 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
456 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
457
458 std::vector<uint8_t> serverStrikeData;
459 server.writeStrikeData(&serverStrikeData);
460
461 // Client.
462 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
463 REPORTER_ASSERT(reporter,
464 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
465 auto clientBlob = buildTextBlob(clientTf, glyphCount);
466
467 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
468 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
469 compare_blobs(expected, actual, reporter, 1);
470 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
471 SkStrikeCache::ValidateGlyphCacheDataSize();
472
473 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
474 discardableManager->unlockAndDeleteAll();
475 }
476
make_blob_causing_fallback(sk_sp<SkTypeface> targetTf,const SkTypeface * glyphTf,skiatest::Reporter * reporter)477 sk_sp<SkTextBlob> make_blob_causing_fallback(
478 sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
479 SkFont font;
480 font.setSubpixel(true);
481 font.setSize(96);
482 font.setHinting(SkFontHinting::kNormal);
483 font.setTypeface(targetTf);
484
485 REPORTER_ASSERT(reporter,
486 !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I()));
487
488 char s[] = "Skia";
489 int runSize = strlen(s);
490
491 SkTextBlobBuilder builder;
492 SkRect bounds = SkRect::MakeIWH(100, 100);
493 const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
494 SkASSERT(runBuffer.utf8text == nullptr);
495 SkASSERT(runBuffer.clusters == nullptr);
496
497 SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
498 runBuffer.glyphs, runSize);
499
500 SkRect glyphBounds;
501 font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
502
503 REPORTER_ASSERT(reporter, glyphBounds.width() > SkStrikeCommon::kSkSideTooBigForAtlas);
504
505 for (int i = 0; i < runSize; i++) {
506 runBuffer.pos[i] = i * 10;
507 }
508
509 return builder.make();
510 }
511
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,reporter,ctxInfo)512 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
513 reporter, ctxInfo) {
514 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
515 SkStrikeServer server(discardableManager.get());
516 SkStrikeClient client(discardableManager, false);
517
518 SkPaint paint;
519
520 auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
521 // TODO: when the cq bots can handle this font remove the check.
522 if (serverTf == nullptr) {
523 return;
524 }
525 auto serverTfData = server.serializeTypeface(serverTf.get());
526
527 auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
528
529 auto props = FindSurfaceProps(ctxInfo.grContext());
530 SkTextBlobCacheDiffCanvas cache_diff_canvas(
531 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
532 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
533
534 std::vector<uint8_t> serverStrikeData;
535 server.writeStrikeData(&serverStrikeData);
536
537 // Client.
538 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
539 REPORTER_ASSERT(reporter,
540 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
541
542 auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
543
544 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
545 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
546 compare_blobs(expected, actual, reporter);
547 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
548 SkStrikeCache::ValidateGlyphCacheDataSize();
549
550 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
551 discardableManager->unlockAndDeleteAll();
552 }
553
554 #if 0
555 // TODO: turn this one when I figure out how to deal with the pixel variance from linear
556 // interpolation from GPU to GPU.
557 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsSDFTWithAllARGBFallback,
558 reporter, ctxInfo) {
559 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
560 SkStrikeServer server(discardableManager.get());
561 SkStrikeClient client(discardableManager, false);
562
563 SkPaint paint;
564
565 auto serverTf = ToolUtils::planet_typeface();
566 // TODO: when the cq bots can handle this font remove the check.
567 if (serverTf == nullptr) {
568 return;
569 }
570 auto serverTfData = server.serializeTypeface(serverTf.get());
571
572 auto makeBlob = [&reporter](sk_sp<SkTypeface> typeface) {
573 SkFont font;
574 font.setSubpixel(true);
575 font.setSize(96);
576 font.setHinting(SkFontHinting::kNormal);
577 font.setTypeface(typeface);
578
579 REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
580
581 // Mercury to Uranus.
582 SkGlyphID glyphs[] = {1, 2, 3, 4, 5, 6, 7, 8};
583
584 SkTextBlobBuilder builder;
585 SkRect bounds = SkRect::MakeIWH(100, 100);
586 const auto& runBuffer = builder.allocRunPosH(font, SK_ARRAY_COUNT(glyphs), 100, &bounds);
587 SkASSERT(runBuffer.utf8text == nullptr);
588 SkASSERT(runBuffer.clusters == nullptr);
589
590 std::copy(std::begin(glyphs), std::end(glyphs), runBuffer.glyphs);
591
592 for (size_t i = 0; i < SK_ARRAY_COUNT(glyphs); i++) {
593 runBuffer.pos[i] = i * 100;
594 }
595
596 return builder.make();
597 };
598
599 auto serverBlob = makeBlob(serverTf);
600
601 auto props = FindSurfaceProps(ctxInfo.grContext());
602 SkTextBlobCacheDiffCanvas cache_diff_canvas(
603 800, 800, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
604 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 400, paint);
605
606 std::vector<uint8_t> serverStrikeData;
607 server.writeStrikeData(&serverStrikeData);
608
609 // Client.
610 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
611 REPORTER_ASSERT(reporter,
612 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
613
614 auto clientBlob = makeBlob(clientTf);
615
616 SkBitmap expected = RasterBlob(serverBlob, 800, 800, paint, ctxInfo.grContext());
617 SkBitmap actual = RasterBlob(clientBlob, 800, 800, paint, ctxInfo.grContext());
618
619 // Pixel variance can be high because of the atlas placement, and large scaling in the linear
620 // interpolation.
621 compare_blobs(expected, actual, reporter, 36);
622 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
623 SkStrikeCache::ValidateGlyphCacheDataSize();
624
625 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
626 discardableManager->unlockAndDeleteAll();
627 }
628 #endif
629
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,reporter,ctxInfo)630 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
631 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
632 SkStrikeServer server(discardableManager.get());
633 SkStrikeClient client(discardableManager, false);
634 SkPaint paint;
635 paint.setAntiAlias(true);
636
637 // Server.
638 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
639 auto serverTfData = server.serializeTypeface(serverTf.get());
640
641 int glyphCount = 10;
642 auto serverBlob = buildTextBlob(serverTf, glyphCount);
643 auto props = FindSurfaceProps(ctxInfo.grContext());
644 SkTextBlobCacheDiffCanvas cache_diff_canvas(
645 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
646 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint);
647
648 std::vector<uint8_t> serverStrikeData;
649 server.writeStrikeData(&serverStrikeData);
650
651 // Client.
652 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
653 REPORTER_ASSERT(reporter,
654 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
655 auto clientBlob = buildTextBlob(clientTf, glyphCount);
656
657 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
658 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
659 compare_blobs(expected, actual, reporter);
660 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
661 SkStrikeCache::ValidateGlyphCacheDataSize();
662
663 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
664 discardableManager->unlockAndDeleteAll();
665 }
666
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,reporter,ctxInfo)667 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
668 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
669 SkStrikeServer server(discardableManager.get());
670 SkStrikeClient client(discardableManager, false);
671 SkPaint paint;
672 SkFont font;
673
674 // A perspective transform forces fallback to dft.
675 SkMatrix matrix = SkMatrix::I();
676 matrix[SkMatrix::kMPersp0] = 0.5f;
677 REPORTER_ASSERT(reporter, matrix.hasPerspective());
678 SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
679 GrTextContext::Options options;
680 GrTextContext::SanitizeOptions(&options);
681 REPORTER_ASSERT(reporter, GrTextContext::CanDrawAsDistanceFields(
682 paint, font, matrix, surfaceProps, true, options));
683
684 // Server.
685 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
686 auto serverTfData = server.serializeTypeface(serverTf.get());
687
688 int glyphCount = 10;
689 auto serverBlob = buildTextBlob(serverTf, glyphCount);
690 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
691 SkTextBlobCacheDiffCanvas cache_diff_canvas(
692 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
693 cache_diff_canvas.concat(matrix);
694 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
695
696 std::vector<uint8_t> serverStrikeData;
697 server.writeStrikeData(&serverStrikeData);
698
699 // Client.
700 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
701 REPORTER_ASSERT(reporter,
702 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
703 auto clientBlob = buildTextBlob(clientTf, glyphCount);
704
705 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
706 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
707 compare_blobs(expected, actual, reporter);
708 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
709 SkStrikeCache::ValidateGlyphCacheDataSize();
710
711 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
712 discardableManager->unlockAndDeleteAll();
713 }
714
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,reporter,ctxInfo)715 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
716 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
717 SkStrikeServer server(discardableManager.get());
718 SkStrikeClient client(discardableManager, false);
719
720 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
721 auto tfData = server.serializeTypeface(serverTf.get());
722 auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
723 REPORTER_ASSERT(reporter, clientTf);
724 int glyphCount = 10;
725 auto clientBlob = buildTextBlob(clientTf, glyphCount);
726
727 // Raster the client-side blob without the glyph data, we should get cache miss notifications.
728 SkPaint paint;
729 SkMatrix matrix = SkMatrix::I();
730 RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
731 REPORTER_ASSERT(reporter,
732 discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
733 REPORTER_ASSERT(reporter,
734 discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
735
736 // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
737 // miss.
738 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
739 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
740
741 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
742 discardableManager->unlockAndDeleteAll();
743 }
744
MakeEmojiBlob(sk_sp<SkTypeface> serverTf,SkScalar textSize,sk_sp<SkTypeface> clientTf=nullptr)745 sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize,
746 sk_sp<SkTypeface> clientTf = nullptr) {
747 SkFont font;
748 font.setTypeface(serverTf);
749 font.setSize(textSize);
750
751 const char* text = ToolUtils::emoji_sample_text();
752 SkFont serverFont = font;
753 auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
754 if (clientTf == nullptr) return blob;
755
756 SkSerialProcs s_procs;
757 s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
758 return SkData::MakeUninitialized(1u);
759 };
760 auto serialized = blob->serialize(s_procs);
761
762 SkDeserialProcs d_procs;
763 d_procs.fTypefaceCtx = &clientTf;
764 d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
765 return *(static_cast<sk_sp<SkTypeface>*>(ctx));
766 };
767 return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
768 }
769
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths,reporter,ctxInfo)770 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths, reporter, ctxInfo) {
771 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
772 SkStrikeServer server(discardableManager.get());
773 SkStrikeClient client(discardableManager, false);
774
775 auto serverTf = ToolUtils::emoji_typeface();
776 auto serverTfData = server.serializeTypeface(serverTf.get());
777 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
778
779 for (SkScalar textSize : { 70, 180, 270, 340}) {
780 auto serverBlob = MakeEmojiBlob(serverTf, textSize);
781 auto props = FindSurfaceProps(ctxInfo.grContext());
782 SkTextBlobCacheDiffCanvas cache_diff_canvas(
783 500, 500, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
784 SkPaint paint;
785 cache_diff_canvas.drawTextBlob(serverBlob.get(), 100, 100, paint);
786
787 std::vector<uint8_t> serverStrikeData;
788 server.writeStrikeData(&serverStrikeData);
789
790 REPORTER_ASSERT(reporter,
791 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
792 auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
793 REPORTER_ASSERT(reporter, clientBlob);
794
795 RasterBlob(clientBlob, 500, 500, paint, ctxInfo.grContext());
796 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
797 SkStrikeCache::ValidateGlyphCacheDataSize();
798 discardableManager->resetCacheMissCounts();
799 }
800
801 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
802 discardableManager->unlockAndDeleteAll();
803 }
804
DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation,reporter)805 DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) {
806 // Build proxy typeface on the client for initializing the cache.
807 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
808 SkStrikeServer server(discardableManager.get());
809 SkStrikeClient client(discardableManager, false);
810
811 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
812 auto tfData = server.serializeTypeface(serverTf.get());
813 auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
814 REPORTER_ASSERT(reporter, clientTf);
815
816 SkFont font;
817 font.setTypeface(clientTf);
818 font.setSubpixel(true);
819 SkPaint paint;
820 paint.setAntiAlias(true);
821 paint.setColor(SK_ColorRED);
822
823 auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
824 const uint8_t glyphImage[] = {0xFF, 0xFF};
825
826 SkStrikeCache strikeCache;
827
828 // Build a fallback cache.
829 {
830 SkAutoDescriptor ad;
831 SkScalerContextRec rec;
832 SkScalerContextEffects effects;
833 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
834 SkScalerContext::MakeRecAndEffects(
835 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
836 SkMatrix::I(), &rec, &effects);
837 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
838
839 auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
840 SkGlyphPrototype proto = {lostGlyphID, 0.f, 0.f, 2, 1, 0, 0, SkMask::kA8_Format, false};
841 fallbackCache->glyphFromPrototype(proto, (void*)glyphImage);
842 }
843
844 // Make sure we can find the fall back cache.
845 {
846 SkAutoDescriptor ad;
847 SkScalerContextRec rec;
848 SkScalerContextEffects effects;
849 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
850 SkScalerContext::MakeRecAndEffects(
851 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
852 SkMatrix::I(), &rec, &effects);
853 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
854 auto testCache = strikeCache.findStrikeExclusive(*desc);
855 REPORTER_ASSERT(reporter, !(testCache == nullptr));
856 }
857
858 // Create the target cache.
859 SkExclusiveStrikePtr testCache;
860 SkAutoDescriptor ad;
861 SkScalerContextRec rec;
862 SkScalerContextEffects effects;
863 SkScalerContextFlags flags = SkScalerContextFlags::kNone;
864 SkScalerContext::MakeRecAndEffects(
865 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
866 SkMatrix::I(), &rec, &effects);
867 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
868 testCache = strikeCache.findStrikeExclusive(*desc);
869 REPORTER_ASSERT(reporter, testCache == nullptr);
870 testCache = strikeCache.createStrikeExclusive(*desc,
871 clientTf->createScalerContext(effects, desc));
872 auto scalerProxy = static_cast<SkScalerContextProxy*>(testCache->getScalerContext());
873 scalerProxy->initCache(testCache.get(), &strikeCache);
874
875 // Look for the lost glyph.
876 {
877 SkPoint pt{SkFixedToScalar(lostGlyphID.getSubXFixed()),
878 SkFixedToScalar(lostGlyphID.getSubYFixed())};
879 SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.code(), pt);
880 testCache->prepareImage(lostGlyph);
881
882 REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
883 REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
884 REPORTER_ASSERT(reporter, lostGlyph->maskFormat() == SkMask::kA8_Format);
885 REPORTER_ASSERT(reporter, memcmp(lostGlyph->image(), glyphImage, sizeof(glyphImage)) == 0);
886 }
887
888 // Look for the lost glyph with a different sub-pix position.
889 {
890 SkPoint pt{SkFixedToScalar(SK_FixedQuarter),
891 SkFixedToScalar(SK_FixedQuarter)};
892 SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.code(), pt);
893 testCache->prepareImage(lostGlyph);
894
895 REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
896 REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
897 REPORTER_ASSERT(reporter, lostGlyph->maskFormat() == SkMask::kA8_Format);
898 REPORTER_ASSERT(reporter, memcmp(lostGlyph->image(), glyphImage, sizeof(glyphImage)) == 0);
899 }
900
901 for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
902 if (i == SkStrikeClient::CacheMissType::kGlyphMetricsFallback ||
903 i == SkStrikeClient::CacheMissType::kFontMetrics) {
904 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 2);
905 } else {
906 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 0);
907 }
908 }
909 strikeCache.validateGlyphCacheDataSize();
910
911 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
912 discardableManager->unlockAndDeleteAll();
913 }
914
DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph,reporter)915 DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph, reporter) {
916 // Build proxy typeface on the client for initializing the cache.
917 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
918 SkStrikeServer server(discardableManager.get());
919 SkStrikeClient client(discardableManager, false);
920
921 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
922 auto tfData = server.serializeTypeface(serverTf.get());
923 auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
924 REPORTER_ASSERT(reporter, clientTf);
925
926 SkFont font;
927 font.setEdging(SkFont::Edging::kAntiAlias);
928 SkPaint paint;
929 paint.setColor(SK_ColorRED);
930
931 auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
932 const uint8_t glyphImage[] = {0xFF, 0xFF};
933 SkMask::Format realMask;
934 SkMask::Format fakeMask;
935
936 SkStrikeCache strikeCache;
937
938 {
939 SkAutoDescriptor ad;
940 SkScalerContextRec rec;
941 SkScalerContextEffects effects;
942 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
943 font.setTypeface(serverTf);
944 SkScalerContext::MakeRecAndEffects(
945 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
946 SkMatrix::I(), &rec, &effects);
947 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
948
949 auto context = serverTf->createScalerContext(effects, desc, false);
950 SkGlyph glyph{lostGlyphID};
951 context->getMetrics(&glyph);
952 realMask = glyph.maskFormat();
953 }
954
955 // Build a fallback cache.
956 {
957 SkAutoDescriptor ad;
958 SkScalerContextRec rec;
959 SkScalerContextEffects effects;
960 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
961 font.setTypeface(clientTf);
962 SkScalerContext::MakeRecAndEffects(
963 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
964 SkMatrix::I(), &rec, &effects);
965 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
966
967 auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
968 fakeMask = (realMask == SkMask::kA8_Format) ? SkMask::kBW_Format : SkMask::kA8_Format;
969 SkGlyphPrototype proto = {lostGlyphID, 0.f, 0.f, 2, 1, 0, 0, fakeMask, false};
970 fallbackCache->glyphFromPrototype(proto, (void *)glyphImage);
971 }
972
973 // Send over the real glyph and make sure the client cache stays intact.
974 {
975 SkAutoDescriptor ad;
976 SkScalerContextEffects effects;
977 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
978 font.setTypeface(serverTf);
979 auto* cacheState = server.getOrCreateCache(
980 paint, font, SkSurfacePropsCopyOrDefault(nullptr),
981 SkMatrix::I(), flags, &effects);
982 SkStrikeServer::AddGlyphForTesting(cacheState, lostGlyphID, false);
983
984 std::vector<uint8_t> serverStrikeData;
985 server.writeStrikeData(&serverStrikeData);
986 REPORTER_ASSERT(reporter,
987 client.readStrikeData(
988 serverStrikeData.data(),
989 serverStrikeData.size()));
990 }
991
992 {
993 SkAutoDescriptor ad;
994 SkScalerContextRec rec;
995 SkScalerContextEffects effects;
996 SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
997 font.setTypeface(clientTf);
998 SkScalerContext::MakeRecAndEffects(
999 font, paint, SkSurfaceProps(0, kUnknown_SkPixelGeometry), flags,
1000 SkMatrix::I(), &rec, &effects);
1001 auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
1002
1003 auto fallbackCache = strikeCache.findStrikeExclusive(*desc);
1004 REPORTER_ASSERT(reporter, fallbackCache.get() != nullptr);
1005 auto glyph = fallbackCache->glyphOrNull(lostGlyphID);
1006 REPORTER_ASSERT(reporter, glyph && glyph->maskFormat() == fakeMask);
1007 if (glyph) {
1008 REPORTER_ASSERT(reporter,
1009 memcmp(glyph->image(), glyphImage, glyph->imageSize()) == 0);
1010 }
1011 }
1012
1013 strikeCache.validateGlyphCacheDataSize();
1014
1015 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
1016 discardableManager->unlockAndDeleteAll();
1017 }
1018