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