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