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