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