1 /*
2 * Copyright 2006 The Android Open Source Project
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
9 #include "SkGlyphCache.h"
10 #include "SkGlyphCache_Globals.h"
11 #include "SkGraphics.h"
12 #include "SkOnce.h"
13 #include "SkPath.h"
14 #include "SkTemplates.h"
15 #include "SkTraceMemoryDump.h"
16 #include "SkTypeface.h"
17
18 #include <cctype>
19
20 //#define SPEW_PURGE_STATUS
21
22 namespace {
23 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
24 } // namespace
25
26 // Returns the shared globals
get_globals()27 static SkGlyphCache_Globals& get_globals() {
28 static SkOnce once;
29 static SkGlyphCache_Globals* globals;
30
31 once([]{ globals = new SkGlyphCache_Globals; });
32 return *globals;
33 }
34
35 ///////////////////////////////////////////////////////////////////////////////
36
SkGlyphCache(const SkDescriptor * desc,std::unique_ptr<SkScalerContext> ctx)37 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx)
38 : fDesc(desc->copy())
39 , fScalerContext(std::move(ctx)) {
40 SkASSERT(desc);
41 SkASSERT(fScalerContext);
42
43 fPrev = fNext = nullptr;
44
45 fScalerContext->getFontMetrics(&fFontMetrics);
46
47 fMemoryUsed = sizeof(*this);
48 }
49
~SkGlyphCache()50 SkGlyphCache::~SkGlyphCache() {
51 fGlyphMap.foreach([](SkGlyph* g) {
52 if (g->fPathData) {
53 delete g->fPathData->fPath;
54 }
55 });
56 }
57
getCharGlyphRec(SkPackedUnicharID packedUnicharID)58 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) {
59 if (!fPackedUnicharIDToPackedGlyphID) {
60 fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]);
61 }
62
63 return &fPackedUnicharIDToPackedGlyphID[packedUnicharID.hash() & kHashMask];
64 }
65
66 ///////////////////////////////////////////////////////////////////////////////
67
68 #ifdef SK_DEBUG
69 #define VALIDATE() AutoValidate av(this)
70 #else
71 #define VALIDATE()
72 #endif
73
unicharToGlyph(SkUnichar charCode)74 SkGlyphID SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
75 VALIDATE();
76 SkPackedUnicharID packedUnicharID(charCode);
77 CharGlyphRec* rec = this->getCharGlyphRec(packedUnicharID);
78
79 if (rec->fPackedUnicharID == packedUnicharID) {
80 // The glyph exists in the unichar to glyph mapping cache. Return it.
81 return rec->fPackedGlyphID.code();
82 } else {
83 // The glyph is not in the unichar to glyph mapping cache. Insert it.
84 rec->fPackedUnicharID = packedUnicharID;
85 SkGlyphID glyphID = fScalerContext->charToGlyphID(charCode);
86 rec->fPackedGlyphID = SkPackedGlyphID(glyphID);
87 return glyphID;
88 }
89 }
90
glyphToUnichar(SkGlyphID glyphID)91 SkUnichar SkGlyphCache::glyphToUnichar(SkGlyphID glyphID) {
92 return fScalerContext->glyphIDToChar(glyphID);
93 }
94
getGlyphCount() const95 unsigned SkGlyphCache::getGlyphCount() const {
96 return fScalerContext->getGlyphCount();
97 }
98
countCachedGlyphs() const99 int SkGlyphCache::countCachedGlyphs() const {
100 return fGlyphMap.count();
101 }
102
103 ///////////////////////////////////////////////////////////////////////////////
104
getUnicharAdvance(SkUnichar charCode)105 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
106 VALIDATE();
107 return *this->lookupByChar(charCode, kJustAdvance_MetricsType);
108 }
109
getGlyphIDAdvance(uint16_t glyphID)110 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
111 VALIDATE();
112 SkPackedGlyphID packedGlyphID(glyphID);
113 return *this->lookupByPackedGlyphID(packedGlyphID, kJustAdvance_MetricsType);
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////
117
getUnicharMetrics(SkUnichar charCode)118 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
119 VALIDATE();
120 return *this->lookupByChar(charCode, kFull_MetricsType);
121 }
122
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)123 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, SkFixed x, SkFixed y) {
124 VALIDATE();
125 return *this->lookupByChar(charCode, kFull_MetricsType, x, y);
126 }
127
getGlyphIDMetrics(uint16_t glyphID)128 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
129 VALIDATE();
130 SkPackedGlyphID packedGlyphID(glyphID);
131 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
132 }
133
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)134 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) {
135 VALIDATE();
136 SkPackedGlyphID packedGlyphID(glyphID, x, y);
137 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
138 }
139
lookupByChar(SkUnichar charCode,MetricsType type,SkFixed x,SkFixed y)140 SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) {
141 SkPackedUnicharID id(charCode, x, y);
142 CharGlyphRec* rec = this->getCharGlyphRec(id);
143 if (rec->fPackedUnicharID != id) {
144 rec->fPackedUnicharID = id;
145 rec->fPackedGlyphID = SkPackedGlyphID(fScalerContext->charToGlyphID(charCode), x, y);
146 }
147 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type);
148 }
149
lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID,MetricsType type)150 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type) {
151 SkGlyph* glyph = fGlyphMap.find(packedGlyphID);
152
153 if (nullptr == glyph) {
154 glyph = this->allocateNewGlyph(packedGlyphID, type);
155 } else {
156 if (type == kFull_MetricsType && glyph->isJustAdvance()) {
157 fScalerContext->getMetrics(glyph);
158 }
159 }
160 return glyph;
161 }
162
allocateNewGlyph(SkPackedGlyphID packedGlyphID,MetricsType mtype)163 SkGlyph* SkGlyphCache::allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType mtype) {
164 fMemoryUsed += sizeof(SkGlyph);
165
166 SkGlyph* glyphPtr;
167 {
168 SkGlyph glyph;
169 glyph.initWithGlyphID(packedGlyphID);
170 glyphPtr = fGlyphMap.set(glyph);
171 }
172
173 if (kJustAdvance_MetricsType == mtype) {
174 fScalerContext->getAdvance(glyphPtr);
175 } else {
176 SkASSERT(kFull_MetricsType == mtype);
177 fScalerContext->getMetrics(glyphPtr);
178 }
179
180 SkASSERT(glyphPtr->fID != SkPackedGlyphID());
181 return glyphPtr;
182 }
183
findImage(const SkGlyph & glyph)184 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
185 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
186 if (nullptr == glyph.fImage) {
187 size_t size = const_cast<SkGlyph&>(glyph).allocImage(&fAlloc);
188 // check that alloc() actually succeeded
189 if (glyph.fImage) {
190 fScalerContext->getImage(glyph);
191 // TODO: the scaler may have changed the maskformat during
192 // getImage (e.g. from AA or LCD to BW) which means we may have
193 // overallocated the buffer. Check if the new computedImageSize
194 // is smaller, and if so, strink the alloc size in fImageAlloc.
195 fMemoryUsed += size;
196 }
197 }
198 }
199 return glyph.fImage;
200 }
201
findPath(const SkGlyph & glyph)202 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
203 if (glyph.fWidth) {
204 if (glyph.fPathData == nullptr) {
205 SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>();
206 const_cast<SkGlyph&>(glyph).fPathData = pathData;
207 pathData->fIntercept = nullptr;
208 SkPath* path = pathData->fPath = new SkPath;
209 fScalerContext->getPath(glyph.getPackedID(), path);
210 fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint);
211 }
212 }
213 return glyph.fPathData ? glyph.fPathData->fPath : nullptr;
214 }
215
216 #include "../pathops/SkPathOpsCubic.h"
217 #include "../pathops/SkPathOpsQuad.h"
218
quad_in_bounds(const SkScalar * pts,const SkScalar bounds[2])219 static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) {
220 SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]);
221 if (bounds[1] < min) {
222 return false;
223 }
224 SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]);
225 return bounds[0] < max;
226 }
227
cubic_in_bounds(const SkScalar * pts,const SkScalar bounds[2])228 static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) {
229 SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]);
230 if (bounds[1] < min) {
231 return false;
232 }
233 SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]);
234 return bounds[0] < max;
235 }
236
OffsetResults(const SkGlyph::Intercept * intercept,SkScalar scale,SkScalar xPos,SkScalar * array,int * count)237 void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale,
238 SkScalar xPos, SkScalar* array, int* count) {
239 if (array) {
240 array += *count;
241 for (int index = 0; index < 2; index++) {
242 *array++ = intercept->fInterval[index] * scale + xPos;
243 }
244 }
245 *count += 2;
246 }
247
AddInterval(SkScalar val,SkGlyph::Intercept * intercept)248 void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) {
249 intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val);
250 intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val);
251 }
252
AddPoints(const SkPoint * pts,int ptCount,const SkScalar bounds[2],bool yAxis,SkGlyph::Intercept * intercept)253 void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2],
254 bool yAxis, SkGlyph::Intercept* intercept) {
255 for (int i = 0; i < ptCount; ++i) {
256 SkScalar val = *(&pts[i].fY - yAxis);
257 if (bounds[0] < val && val < bounds[1]) {
258 AddInterval(*(&pts[i].fX + yAxis), intercept);
259 }
260 }
261 }
262
AddLine(const SkPoint pts[2],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)263 void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis,
264 SkGlyph::Intercept* intercept) {
265 SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX)
266 : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY);
267 if (0 <= t && t < 1) { // this handles divide by zero above
268 AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY)
269 : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept);
270 }
271 }
272
AddQuad(const SkPoint pts[3],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)273 void SkGlyphCache::AddQuad(const SkPoint pts[3], SkScalar axis, bool yAxis,
274 SkGlyph::Intercept* intercept) {
275 SkDQuad quad;
276 quad.set(pts);
277 double roots[2];
278 int count = yAxis ? quad.verticalIntersect(axis, roots)
279 : quad.horizontalIntersect(axis, roots);
280 while (--count >= 0) {
281 SkPoint pt = quad.ptAtT(roots[count]).asSkPoint();
282 AddInterval(*(&pt.fX + yAxis), intercept);
283 }
284 }
285
AddCubic(const SkPoint pts[4],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)286 void SkGlyphCache::AddCubic(const SkPoint pts[4], SkScalar axis, bool yAxis,
287 SkGlyph::Intercept* intercept) {
288 SkDCubic cubic;
289 cubic.set(pts);
290 double roots[3];
291 int count = yAxis ? cubic.verticalIntersect(axis, roots)
292 : cubic.horizontalIntersect(axis, roots);
293 while (--count >= 0) {
294 SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint();
295 AddInterval(*(&pt.fX + yAxis), intercept);
296 }
297 }
298
MatchBounds(const SkGlyph * glyph,const SkScalar bounds[2])299 const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph,
300 const SkScalar bounds[2]) {
301 if (!glyph->fPathData) {
302 return nullptr;
303 }
304 const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept;
305 while (intercept) {
306 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) {
307 return intercept;
308 }
309 intercept = intercept->fNext;
310 }
311 return nullptr;
312 }
313
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,bool yAxis,SkGlyph * glyph,SkScalar * array,int * count)314 void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
315 bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) {
316 const SkGlyph::Intercept* match = MatchBounds(glyph, bounds);
317
318 if (match) {
319 if (match->fInterval[0] < match->fInterval[1]) {
320 OffsetResults(match, scale, xPos, array, count);
321 }
322 return;
323 }
324
325 SkGlyph::Intercept* intercept = fAlloc.make<SkGlyph::Intercept>();
326 intercept->fNext = glyph->fPathData->fIntercept;
327 intercept->fBounds[0] = bounds[0];
328 intercept->fBounds[1] = bounds[1];
329 intercept->fInterval[0] = SK_ScalarMax;
330 intercept->fInterval[1] = SK_ScalarMin;
331 glyph->fPathData->fIntercept = intercept;
332 const SkPath* path = glyph->fPathData->fPath;
333 const SkRect& pathBounds = path->getBounds();
334 if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) {
335 return;
336 }
337 SkPath::Iter iter(*path, false);
338 SkPoint pts[4];
339 SkPath::Verb verb;
340 while (SkPath::kDone_Verb != (verb = iter.next(pts))) {
341 switch (verb) {
342 case SkPath::kMove_Verb:
343 break;
344 case SkPath::kLine_Verb:
345 AddLine(pts, bounds[0], yAxis, intercept);
346 AddLine(pts, bounds[1], yAxis, intercept);
347 AddPoints(pts, 2, bounds, yAxis, intercept);
348 break;
349 case SkPath::kQuad_Verb:
350 if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) {
351 break;
352 }
353 AddQuad(pts, bounds[0], yAxis, intercept);
354 AddQuad(pts, bounds[1], yAxis, intercept);
355 AddPoints(pts, 3, bounds, yAxis, intercept);
356 break;
357 case SkPath::kConic_Verb:
358 SkASSERT(0); // no support for text composed of conics
359 break;
360 case SkPath::kCubic_Verb:
361 if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) {
362 break;
363 }
364 AddCubic(pts, bounds[0], yAxis, intercept);
365 AddCubic(pts, bounds[1], yAxis, intercept);
366 AddPoints(pts, 4, bounds, yAxis, intercept);
367 break;
368 case SkPath::kClose_Verb:
369 break;
370 default:
371 SkASSERT(0);
372 break;
373 }
374 }
375 if (intercept->fInterval[0] >= intercept->fInterval[1]) {
376 intercept->fInterval[0] = SK_ScalarMax;
377 intercept->fInterval[1] = SK_ScalarMin;
378 return;
379 }
380 OffsetResults(intercept, scale, xPos, array, count);
381 }
382
dump() const383 void SkGlyphCache::dump() const {
384 const SkTypeface* face = fScalerContext->getTypeface();
385 const SkScalerContextRec& rec = fScalerContext->getRec();
386 SkMatrix matrix;
387 rec.getSingleMatrix(&matrix);
388 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
389 SkString name;
390 face->getFamilyName(&name);
391
392 SkString msg;
393 SkFontStyle style = face->fontStyle();
394 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
395 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
396 rec.dump().c_str(), fGlyphMap.count());
397 SkDebugf("%s\n", msg.c_str());
398 }
399
400 ///////////////////////////////////////////////////////////////////////////////
401 ///////////////////////////////////////////////////////////////////////////////
402
getTotalMemoryUsed() const403 size_t SkGlyphCache_Globals::getTotalMemoryUsed() const {
404 SkAutoExclusive ac(fLock);
405 return fTotalMemoryUsed;
406 }
407
getCacheCountUsed() const408 int SkGlyphCache_Globals::getCacheCountUsed() const {
409 SkAutoExclusive ac(fLock);
410 return fCacheCount;
411 }
412
getCacheCountLimit() const413 int SkGlyphCache_Globals::getCacheCountLimit() const {
414 SkAutoExclusive ac(fLock);
415 return fCacheCountLimit;
416 }
417
setCacheSizeLimit(size_t newLimit)418 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
419 static const size_t minLimit = 256 * 1024;
420 if (newLimit < minLimit) {
421 newLimit = minLimit;
422 }
423
424 SkAutoExclusive ac(fLock);
425
426 size_t prevLimit = fCacheSizeLimit;
427 fCacheSizeLimit = newLimit;
428 this->internalPurge();
429 return prevLimit;
430 }
431
getCacheSizeLimit() const432 size_t SkGlyphCache_Globals::getCacheSizeLimit() const {
433 SkAutoExclusive ac(fLock);
434 return fCacheSizeLimit;
435 }
436
setCacheCountLimit(int newCount)437 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
438 if (newCount < 0) {
439 newCount = 0;
440 }
441
442 SkAutoExclusive ac(fLock);
443
444 int prevCount = fCacheCountLimit;
445 fCacheCountLimit = newCount;
446 this->internalPurge();
447 return prevCount;
448 }
449
getCachePointSizeLimit() const450 int SkGlyphCache_Globals::getCachePointSizeLimit() const {
451 SkAutoExclusive ac(fLock);
452 return fPointSizeLimit;
453 }
454
setCachePointSizeLimit(int newLimit)455 int SkGlyphCache_Globals::setCachePointSizeLimit(int newLimit) {
456 if (newLimit < 0) {
457 newLimit = 0;
458 }
459
460 SkAutoExclusive ac(fLock);
461
462 int prevLimit = fPointSizeLimit;
463 fPointSizeLimit = newLimit;
464 return prevLimit;
465 }
466
purgeAll()467 void SkGlyphCache_Globals::purgeAll() {
468 SkAutoExclusive ac(fLock);
469 this->internalPurge(fTotalMemoryUsed);
470 }
471
472 /* This guy calls the visitor from within the mutext lock, so the visitor
473 cannot:
474 - take too much time
475 - try to acquire the mutext again
476 - call a fontscaler (which might call into the cache)
477 */
VisitCache(SkTypeface * typeface,const SkScalerContextEffects & effects,const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)478 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
479 const SkScalerContextEffects& effects,
480 const SkDescriptor* desc,
481 bool (*proc)(const SkGlyphCache*, void*),
482 void* context) {
483 if (!typeface) {
484 typeface = SkTypeface::GetDefaultTypeface();
485 }
486 SkASSERT(desc);
487
488 // Precondition: the typeface id must be the fFontID in the descriptor
489 SkDEBUGCODE(
490 uint32_t length = 0;
491 const SkScalerContextRec* rec = static_cast<const SkScalerContextRec*>(
492 desc->findEntry(kRec_SkDescriptorTag, &length));
493 SkASSERT(rec);
494 SkASSERT(length == sizeof(*rec));
495 SkASSERT(typeface->uniqueID() == rec->fFontID);
496 )
497
498 SkGlyphCache_Globals& globals = get_globals();
499 SkGlyphCache* cache;
500
501 {
502 SkAutoExclusive ac(globals.fLock);
503
504 globals.validate();
505
506 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
507 if (*cache->fDesc == *desc) {
508 globals.internalDetachCache(cache);
509 if (!proc(cache, context)) {
510 globals.internalAttachCacheToHead(cache);
511 cache = nullptr;
512 }
513 return cache;
514 }
515 }
516 }
517
518 // Check if we can create a scaler-context before creating the glyphcache.
519 // If not, we may have exhausted OS/font resources, so try purging the
520 // cache once and try again.
521 {
522 // pass true the first time, to notice if the scalercontext failed,
523 // so we can try the purge.
524 std::unique_ptr<SkScalerContext> ctx = typeface->createScalerContext(effects, desc, true);
525 if (!ctx) {
526 get_globals().purgeAll();
527 ctx = typeface->createScalerContext(effects, desc, false);
528 SkASSERT(ctx);
529 }
530 cache = new SkGlyphCache(desc, std::move(ctx));
531 }
532
533 AutoValidate av(cache);
534
535 if (!proc(cache, context)) { // need to reattach
536 globals.attachCacheToHead(cache);
537 cache = nullptr;
538 }
539 return cache;
540 }
541
AttachCache(SkGlyphCache * cache)542 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
543 SkASSERT(cache);
544 SkASSERT(cache->fNext == nullptr);
545
546 get_globals().attachCacheToHead(cache);
547 }
548
dump_visitor(const SkGlyphCache & cache,void * context)549 static void dump_visitor(const SkGlyphCache& cache, void* context) {
550 int* counter = (int*)context;
551 int index = *counter;
552 *counter += 1;
553
554 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
555
556 SkDebugf("index %d\n", index);
557 SkDebugf("%s", rec.dump().c_str());
558 }
559
Dump()560 void SkGlyphCache::Dump() {
561 SkDebugf("GlyphCache [ used budget ]\n");
562 SkDebugf(" bytes [ %8zu %8zu ]\n",
563 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
564 SkDebugf(" count [ %8zu %8zu ]\n",
565 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
566
567 int counter = 0;
568 SkGlyphCache::VisitAll(dump_visitor, &counter);
569 }
570
sk_trace_dump_visitor(const SkGlyphCache & cache,void * context)571 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) {
572 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
573
574 const SkTypeface* face = cache.getScalerContext()->getTypeface();
575 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
576
577 SkString fontName;
578 face->getFamilyName(&fontName);
579 // Replace all special characters with '_'.
580 for (size_t index = 0; index < fontName.size(); ++index) {
581 if (!std::isalnum(fontName[index])) {
582 fontName[index] = '_';
583 }
584 }
585
586 SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
587 gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
588
589 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed());
590 dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs());
591 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
592 }
593
DumpMemoryStatistics(SkTraceMemoryDump * dump)594 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
595 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
596 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
597 SkGraphics::GetFontCacheLimit());
598 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
599 SkGraphics::GetFontCacheCountUsed());
600 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
601 SkGraphics::GetFontCacheCountLimit());
602
603 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
604 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
605 return;
606 }
607
608 SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump);
609 }
610
VisitAll(Visitor visitor,void * context)611 void SkGlyphCache::VisitAll(Visitor visitor, void* context) {
612 SkGlyphCache_Globals& globals = get_globals();
613 SkAutoExclusive ac(globals.fLock);
614 SkGlyphCache* cache;
615
616 globals.validate();
617
618 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
619 visitor(*cache, context);
620 }
621 }
622
623 ///////////////////////////////////////////////////////////////////////////////
624
attachCacheToHead(SkGlyphCache * cache)625 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
626 SkAutoExclusive ac(fLock);
627
628 this->validate();
629 cache->validate();
630
631 this->internalAttachCacheToHead(cache);
632 this->internalPurge();
633 }
634
internalGetTail() const635 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
636 SkGlyphCache* cache = fHead;
637 if (cache) {
638 while (cache->fNext) {
639 cache = cache->fNext;
640 }
641 }
642 return cache;
643 }
644
internalPurge(size_t minBytesNeeded)645 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
646 this->validate();
647
648 size_t bytesNeeded = 0;
649 if (fTotalMemoryUsed > fCacheSizeLimit) {
650 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
651 }
652 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
653 if (bytesNeeded) {
654 // no small purges!
655 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
656 }
657
658 int countNeeded = 0;
659 if (fCacheCount > fCacheCountLimit) {
660 countNeeded = fCacheCount - fCacheCountLimit;
661 // no small purges!
662 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
663 }
664
665 // early exit
666 if (!countNeeded && !bytesNeeded) {
667 return 0;
668 }
669
670 size_t bytesFreed = 0;
671 int countFreed = 0;
672
673 // we start at the tail and proceed backwards, as the linklist is in LRU
674 // order, with unimportant entries at the tail.
675 SkGlyphCache* cache = this->internalGetTail();
676 while (cache != nullptr &&
677 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
678 SkGlyphCache* prev = cache->fPrev;
679 bytesFreed += cache->fMemoryUsed;
680 countFreed += 1;
681
682 this->internalDetachCache(cache);
683 delete cache;
684 cache = prev;
685 }
686
687 this->validate();
688
689 #ifdef SPEW_PURGE_STATUS
690 if (countFreed) {
691 SkDebugf("purging %dK from font cache [%d entries]\n",
692 (int)(bytesFreed >> 10), countFreed);
693 }
694 #endif
695
696 return bytesFreed;
697 }
698
internalAttachCacheToHead(SkGlyphCache * cache)699 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
700 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext);
701 if (fHead) {
702 fHead->fPrev = cache;
703 cache->fNext = fHead;
704 }
705 fHead = cache;
706
707 fCacheCount += 1;
708 fTotalMemoryUsed += cache->fMemoryUsed;
709 }
710
internalDetachCache(SkGlyphCache * cache)711 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
712 SkASSERT(fCacheCount > 0);
713 fCacheCount -= 1;
714 fTotalMemoryUsed -= cache->fMemoryUsed;
715
716 if (cache->fPrev) {
717 cache->fPrev->fNext = cache->fNext;
718 } else {
719 fHead = cache->fNext;
720 }
721 if (cache->fNext) {
722 cache->fNext->fPrev = cache->fPrev;
723 }
724 cache->fPrev = cache->fNext = nullptr;
725 }
726
727 ///////////////////////////////////////////////////////////////////////////////
728
729 #ifdef SK_DEBUG
730
validate() const731 void SkGlyphCache::validate() const {
732 #ifdef SK_DEBUG_GLYPH_CACHE
733 int count = fGlyphArray.count();
734 for (int i = 0; i < count; i++) {
735 const SkGlyph* glyph = &fGlyphArray[i];
736 SkASSERT(glyph);
737 if (glyph->fImage) {
738 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
739 }
740 }
741 #endif
742 }
743
validate() const744 void SkGlyphCache_Globals::validate() const {
745 size_t computedBytes = 0;
746 int computedCount = 0;
747
748 const SkGlyphCache* head = fHead;
749 while (head != nullptr) {
750 computedBytes += head->fMemoryUsed;
751 computedCount += 1;
752 head = head->fNext;
753 }
754
755 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount,
756 computedCount);
757 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d",
758 fTotalMemoryUsed, computedBytes);
759 }
760
761 #endif
762
763 ///////////////////////////////////////////////////////////////////////////////
764 ///////////////////////////////////////////////////////////////////////////////
765
766 #include "SkTypefaceCache.h"
767
GetFontCacheLimit()768 size_t SkGraphics::GetFontCacheLimit() {
769 return get_globals().getCacheSizeLimit();
770 }
771
SetFontCacheLimit(size_t bytes)772 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
773 return get_globals().setCacheSizeLimit(bytes);
774 }
775
GetFontCacheUsed()776 size_t SkGraphics::GetFontCacheUsed() {
777 return get_globals().getTotalMemoryUsed();
778 }
779
GetFontCacheCountLimit()780 int SkGraphics::GetFontCacheCountLimit() {
781 return get_globals().getCacheCountLimit();
782 }
783
SetFontCacheCountLimit(int count)784 int SkGraphics::SetFontCacheCountLimit(int count) {
785 return get_globals().setCacheCountLimit(count);
786 }
787
GetFontCacheCountUsed()788 int SkGraphics::GetFontCacheCountUsed() {
789 return get_globals().getCacheCountUsed();
790 }
791
GetFontCachePointSizeLimit()792 int SkGraphics::GetFontCachePointSizeLimit() {
793 return get_globals().getCachePointSizeLimit();
794 }
795
SetFontCachePointSizeLimit(int limit)796 int SkGraphics::SetFontCachePointSizeLimit(int limit) {
797 return get_globals().setCachePointSizeLimit(limit);
798 }
799
PurgeFontCache()800 void SkGraphics::PurgeFontCache() {
801 get_globals().purgeAll();
802 SkTypefaceCache::PurgeAll();
803 }
804
805 // TODO(herb): clean up TLS apis.
GetTLSFontCacheLimit()806 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; }
SetTLSFontCacheLimit(size_t bytes)807 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { }
808
DetachCacheUsingPaint(const SkPaint & paint,const SkSurfaceProps * surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix * deviceMatrix)809 SkGlyphCache* SkGlyphCache::DetachCacheUsingPaint(const SkPaint& paint,
810 const SkSurfaceProps* surfaceProps,
811 SkScalerContextFlags scalerContextFlags,
812 const SkMatrix* deviceMatrix) {
813 SkAutoDescriptor ad;
814 SkScalerContextEffects effects;
815
816 auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
817 paint, surfaceProps, scalerContextFlags, deviceMatrix, &ad, &effects);
818
819 return SkGlyphCache::DetachCache(paint.getTypeface(), effects, desc);
820 }
821