• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "SkGlyphCache.h"
11 #include "SkGlyphCache_Globals.h"
12 #include "SkDistanceFieldGen.h"
13 #include "SkGraphics.h"
14 #include "SkLazyPtr.h"
15 #include "SkPaint.h"
16 #include "SkPath.h"
17 #include "SkTemplates.h"
18 #include "SkTLS.h"
19 #include "SkTypeface.h"
20 
21 //#define SPEW_PURGE_STATUS
22 //#define RECORD_HASH_EFFICIENCY
23 
24 namespace {
25 
create_globals()26 SkGlyphCache_Globals* create_globals() {
27     return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
28 }
29 
30 }  // namespace
31 
32 // Returns the shared globals
getSharedGlobals()33 static SkGlyphCache_Globals& getSharedGlobals() {
34     SK_DECLARE_STATIC_LAZY_PTR(SkGlyphCache_Globals, globals, create_globals);
35     return *globals.get();
36 }
37 
38 // Returns the TLS globals (if set), or the shared globals
getGlobals()39 static SkGlyphCache_Globals& getGlobals() {
40     SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
41     return tls ? *tls : getSharedGlobals();
42 }
43 
44 ///////////////////////////////////////////////////////////////////////////////
45 
46 #ifdef RECORD_HASH_EFFICIENCY
47     static uint32_t gHashSuccess;
48     static uint32_t gHashCollision;
49 
RecordHashSuccess()50     static void RecordHashSuccess() {
51         gHashSuccess += 1;
52     }
53 
RecordHashCollisionIf(bool pred)54     static void RecordHashCollisionIf(bool pred) {
55         if (pred) {
56             gHashCollision += 1;
57 
58             uint32_t total = gHashSuccess + gHashCollision;
59             SkDebugf("Font Cache Hash success rate: %d%%\n",
60                      100 * gHashSuccess / total);
61         }
62     }
63 #else
64     #define RecordHashSuccess() (void)0
65     #define RecordHashCollisionIf(pred) (void)0
66 #endif
67 #define RecordHashCollision() RecordHashCollisionIf(true)
68 
69 ///////////////////////////////////////////////////////////////////////////////
70 
71 // so we don't grow our arrays a lot
72 #define kMinGlyphCount      16
73 #define kMinGlyphImageSize  (16*2)
74 #define kMinAllocAmount     ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
75 
SkGlyphCache(SkTypeface * typeface,const SkDescriptor * desc,SkScalerContext * ctx)76 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
77         : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
78     SkASSERT(typeface);
79     SkASSERT(desc);
80     SkASSERT(ctx);
81 
82     fPrev = fNext = NULL;
83 
84     fDesc = desc->copy();
85     fScalerContext->getFontMetrics(&fFontMetrics);
86 
87     // init to 0 so that all of the pointers will be null
88     memset(fGlyphHash, 0, sizeof(fGlyphHash));
89     // init with 0xFF so that the charCode field will be -1, which is invalid
90     memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
91 
92     fMemoryUsed = sizeof(*this);
93 
94     fGlyphArray.setReserve(kMinGlyphCount);
95 
96     fAuxProcList = NULL;
97 }
98 
~SkGlyphCache()99 SkGlyphCache::~SkGlyphCache() {
100 #if 0
101     {
102         size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
103         size_t glyphAlloc = fGlyphAlloc.totalCapacity();
104         size_t glyphHashUsed = 0;
105         size_t uniHashUsed = 0;
106         for (int i = 0; i < kHashCount; ++i) {
107             glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
108             uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
109         }
110         size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
111         size_t imageUsed = 0;
112         for (int i = 0; i < fGlyphArray.count(); ++i) {
113             const SkGlyph& g = *fGlyphArray[i];
114             if (g.fImage) {
115                 imageUsed += g.fHeight * g.rowBytes();
116             }
117         }
118 
119         printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
120                  ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
121 
122     }
123 #endif
124     SkGlyph**   gptr = fGlyphArray.begin();
125     SkGlyph**   stop = fGlyphArray.end();
126     while (gptr < stop) {
127         SkPath* path = (*gptr)->fPath;
128         if (path) {
129             SkDELETE(path);
130         }
131         gptr += 1;
132     }
133     SkDescriptor::Free(fDesc);
134     SkDELETE(fScalerContext);
135     this->invokeAndRemoveAuxProcs();
136 }
137 
138 ///////////////////////////////////////////////////////////////////////////////
139 
140 #ifdef SK_DEBUG
141 #define VALIDATE()  AutoValidate av(this)
142 #else
143 #define VALIDATE()
144 #endif
145 
unicharToGlyph(SkUnichar charCode)146 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
147     VALIDATE();
148     uint32_t id = SkGlyph::MakeID(charCode);
149     const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
150 
151     if (rec.fID == id) {
152         return rec.fGlyph->getGlyphID();
153     } else {
154         return fScalerContext->charToGlyphID(charCode);
155     }
156 }
157 
glyphToUnichar(uint16_t glyphID)158 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
159     return fScalerContext->glyphIDToChar(glyphID);
160 }
161 
getGlyphCount()162 unsigned SkGlyphCache::getGlyphCount() {
163     return fScalerContext->getGlyphCount();
164 }
165 
166 ///////////////////////////////////////////////////////////////////////////////
167 
getUnicharAdvance(SkUnichar charCode)168 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
169     VALIDATE();
170     uint32_t id = SkGlyph::MakeID(charCode);
171     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
172 
173     if (rec->fID != id) {
174         // this ID is based on the UniChar
175         rec->fID = id;
176         // this ID is based on the glyph index
177         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
178         rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
179     }
180     return *rec->fGlyph;
181 }
182 
getGlyphIDAdvance(uint16_t glyphID)183 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
184     VALIDATE();
185     uint32_t id = SkGlyph::MakeID(glyphID);
186     unsigned index = ID2HashIndex(id);
187     SkGlyph* glyph = fGlyphHash[index];
188 
189     if (NULL == glyph || glyph->fID != id) {
190         glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
191         fGlyphHash[index] = glyph;
192     }
193     return *glyph;
194 }
195 
196 ///////////////////////////////////////////////////////////////////////////////
197 
getUnicharMetrics(SkUnichar charCode)198 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
199     VALIDATE();
200     uint32_t id = SkGlyph::MakeID(charCode);
201     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
202 
203     if (rec->fID != id) {
204         RecordHashCollisionIf(rec->fGlyph != NULL);
205         // this ID is based on the UniChar
206         rec->fID = id;
207         // this ID is based on the glyph index
208         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
209         rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
210     } else {
211         RecordHashSuccess();
212         if (rec->fGlyph->isJustAdvance()) {
213             fScalerContext->getMetrics(rec->fGlyph);
214         }
215     }
216     SkASSERT(rec->fGlyph->isFullMetrics());
217     return *rec->fGlyph;
218 }
219 
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)220 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
221                                                SkFixed x, SkFixed y) {
222     VALIDATE();
223     uint32_t id = SkGlyph::MakeID(charCode, x, y);
224     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
225 
226     if (rec->fID != id) {
227         RecordHashCollisionIf(rec->fGlyph != NULL);
228         // this ID is based on the UniChar
229         rec->fID = id;
230         // this ID is based on the glyph index
231         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
232         rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
233     } else {
234         RecordHashSuccess();
235         if (rec->fGlyph->isJustAdvance()) {
236             fScalerContext->getMetrics(rec->fGlyph);
237         }
238     }
239     SkASSERT(rec->fGlyph->isFullMetrics());
240     return *rec->fGlyph;
241 }
242 
getGlyphIDMetrics(uint16_t glyphID)243 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
244     VALIDATE();
245     uint32_t id = SkGlyph::MakeID(glyphID);
246     unsigned index = ID2HashIndex(id);
247     SkGlyph* glyph = fGlyphHash[index];
248 
249     if (NULL == glyph || glyph->fID != id) {
250         RecordHashCollisionIf(glyph != NULL);
251         glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
252         fGlyphHash[index] = glyph;
253     } else {
254         RecordHashSuccess();
255         if (glyph->isJustAdvance()) {
256             fScalerContext->getMetrics(glyph);
257         }
258     }
259     SkASSERT(glyph->isFullMetrics());
260     return *glyph;
261 }
262 
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)263 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
264                                                SkFixed x, SkFixed y) {
265     VALIDATE();
266     uint32_t id = SkGlyph::MakeID(glyphID, x, y);
267     unsigned index = ID2HashIndex(id);
268     SkGlyph* glyph = fGlyphHash[index];
269 
270     if (NULL == glyph || glyph->fID != id) {
271         RecordHashCollisionIf(glyph != NULL);
272         glyph = this->lookupMetrics(id, kFull_MetricsType);
273         fGlyphHash[index] = glyph;
274     } else {
275         RecordHashSuccess();
276         if (glyph->isJustAdvance()) {
277             fScalerContext->getMetrics(glyph);
278         }
279     }
280     SkASSERT(glyph->isFullMetrics());
281     return *glyph;
282 }
283 
lookupMetrics(uint32_t id,MetricsType mtype)284 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
285     SkGlyph* glyph;
286 
287     int     hi = 0;
288     int     count = fGlyphArray.count();
289 
290     if (count) {
291         SkGlyph**   gptr = fGlyphArray.begin();
292         int     lo = 0;
293 
294         hi = count - 1;
295         while (lo < hi) {
296             int mid = (hi + lo) >> 1;
297             if (gptr[mid]->fID < id) {
298                 lo = mid + 1;
299             } else {
300                 hi = mid;
301             }
302         }
303         glyph = gptr[hi];
304         if (glyph->fID == id) {
305             if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
306                 fScalerContext->getMetrics(glyph);
307             }
308             return glyph;
309         }
310 
311         // check if we need to bump hi before falling though to the allocator
312         if (glyph->fID < id) {
313             hi += 1;
314         }
315     }
316 
317     // not found, but hi tells us where to inser the new glyph
318     fMemoryUsed += sizeof(SkGlyph);
319 
320     glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
321                                         SkChunkAlloc::kThrow_AllocFailType);
322     glyph->init(id);
323     *fGlyphArray.insert(hi) = glyph;
324 
325     if (kJustAdvance_MetricsType == mtype) {
326         fScalerContext->getAdvance(glyph);
327     } else {
328         SkASSERT(kFull_MetricsType == mtype);
329         fScalerContext->getMetrics(glyph);
330     }
331 
332     return glyph;
333 }
334 
findImage(const SkGlyph & glyph)335 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
336     if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
337         if (NULL == glyph.fImage) {
338             size_t  size = glyph.computeImageSize();
339             const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
340                                         SkChunkAlloc::kReturnNil_AllocFailType);
341             // check that alloc() actually succeeded
342             if (NULL != glyph.fImage) {
343                 fScalerContext->getImage(glyph);
344                 // TODO: the scaler may have changed the maskformat during
345                 // getImage (e.g. from AA or LCD to BW) which means we may have
346                 // overallocated the buffer. Check if the new computedImageSize
347                 // is smaller, and if so, strink the alloc size in fImageAlloc.
348                 fMemoryUsed += size;
349             }
350         }
351     }
352     return glyph.fImage;
353 }
354 
findPath(const SkGlyph & glyph)355 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
356     if (glyph.fWidth) {
357         if (glyph.fPath == NULL) {
358             const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
359             fScalerContext->getPath(glyph, glyph.fPath);
360             fMemoryUsed += sizeof(SkPath) +
361                     glyph.fPath->countPoints() * sizeof(SkPoint);
362         }
363     }
364     return glyph.fPath;
365 }
366 
findDistanceField(const SkGlyph & glyph)367 const void* SkGlyphCache::findDistanceField(const SkGlyph& glyph) {
368     if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
369         if (NULL == glyph.fDistanceField) {
370             size_t  size = SkComputeDistanceFieldSize(glyph.fWidth, glyph.fHeight);
371             if (size == 0) {
372                 return NULL;
373             }
374             const void* image = this->findImage(glyph);
375             // now generate the distance field
376             if (NULL != image) {
377                 const_cast<SkGlyph&>(glyph).fDistanceField = fGlyphAlloc.alloc(size,
378                                             SkChunkAlloc::kReturnNil_AllocFailType);
379                 if (NULL != glyph.fDistanceField) {
380                     SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
381                     if (SkMask::kA8_Format == maskFormat) {
382                         // make the distance field from the image
383                         SkGenerateDistanceFieldFromA8Image((unsigned char*)glyph.fDistanceField,
384                                                            (unsigned char*)glyph.fImage,
385                                                            glyph.fWidth, glyph.fHeight,
386                                                            glyph.rowBytes());
387                         fMemoryUsed += size;
388                     } else if (SkMask::kBW_Format == maskFormat) {
389                         // make the distance field from the image
390                         SkGenerateDistanceFieldFromBWImage((unsigned char*)glyph.fDistanceField,
391                                                            (unsigned char*)glyph.fImage,
392                                                            glyph.fWidth, glyph.fHeight,
393                                                            glyph.rowBytes());
394                         fMemoryUsed += size;
395                     } else {
396                         fGlyphAlloc.unalloc(glyph.fDistanceField);
397                         const_cast<SkGlyph&>(glyph).fDistanceField = NULL;
398                     }
399                 }
400             }
401         }
402     }
403     return glyph.fDistanceField;
404 }
405 
406 ///////////////////////////////////////////////////////////////////////////////
407 
getAuxProcData(void (* proc)(void *),void ** dataPtr) const408 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
409     const AuxProcRec* rec = fAuxProcList;
410     while (rec) {
411         if (rec->fProc == proc) {
412             if (dataPtr) {
413                 *dataPtr = rec->fData;
414             }
415             return true;
416         }
417         rec = rec->fNext;
418     }
419     return false;
420 }
421 
setAuxProc(void (* proc)(void *),void * data)422 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
423     if (proc == NULL) {
424         return;
425     }
426 
427     AuxProcRec* rec = fAuxProcList;
428     while (rec) {
429         if (rec->fProc == proc) {
430             rec->fData = data;
431             return;
432         }
433         rec = rec->fNext;
434     }
435     // not found, create a new rec
436     rec = SkNEW(AuxProcRec);
437     rec->fProc = proc;
438     rec->fData = data;
439     rec->fNext = fAuxProcList;
440     fAuxProcList = rec;
441 }
442 
invokeAndRemoveAuxProcs()443 void SkGlyphCache::invokeAndRemoveAuxProcs() {
444     AuxProcRec* rec = fAuxProcList;
445     while (rec) {
446         rec->fProc(rec->fData);
447         AuxProcRec* next = rec->fNext;
448         SkDELETE(rec);
449         rec = next;
450     }
451 }
452 
453 ///////////////////////////////////////////////////////////////////////////////
454 ///////////////////////////////////////////////////////////////////////////////
455 
456 #include "SkThread.h"
457 
setCacheSizeLimit(size_t newLimit)458 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
459     static const size_t minLimit = 256 * 1024;
460     if (newLimit < minLimit) {
461         newLimit = minLimit;
462     }
463 
464     SkAutoMutexAcquire    ac(fMutex);
465 
466     size_t prevLimit = fCacheSizeLimit;
467     fCacheSizeLimit = newLimit;
468     this->internalPurge();
469     return prevLimit;
470 }
471 
setCacheCountLimit(int newCount)472 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
473     if (newCount < 0) {
474         newCount = 0;
475     }
476 
477     SkAutoMutexAcquire    ac(fMutex);
478 
479     int prevCount = fCacheCountLimit;
480     fCacheCountLimit = newCount;
481     this->internalPurge();
482     return prevCount;
483 }
484 
purgeAll()485 void SkGlyphCache_Globals::purgeAll() {
486     SkAutoMutexAcquire    ac(fMutex);
487     this->internalPurge(fTotalMemoryUsed);
488 }
489 
VisitAllCaches(bool (* proc)(SkGlyphCache *,void *),void * context)490 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
491                                   void* context) {
492     SkGlyphCache_Globals& globals = getGlobals();
493     SkAutoMutexAcquire    ac(globals.fMutex);
494     SkGlyphCache*         cache;
495 
496     globals.validate();
497 
498     for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
499         if (proc(cache, context)) {
500             break;
501         }
502     }
503 
504     globals.validate();
505 }
506 
507 /*  This guy calls the visitor from within the mutext lock, so the visitor
508     cannot:
509     - take too much time
510     - try to acquire the mutext again
511     - call a fontscaler (which might call into the cache)
512 */
VisitCache(SkTypeface * typeface,const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)513 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
514                               const SkDescriptor* desc,
515                               bool (*proc)(const SkGlyphCache*, void*),
516                               void* context) {
517     if (!typeface) {
518         typeface = SkTypeface::GetDefaultTypeface();
519     }
520     SkASSERT(desc);
521 
522     SkGlyphCache_Globals& globals = getGlobals();
523     SkAutoMutexAcquire    ac(globals.fMutex);
524     SkGlyphCache*         cache;
525     bool                  insideMutex = true;
526 
527     globals.validate();
528 
529     for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
530         if (cache->fDesc->equals(*desc)) {
531             globals.internalDetachCache(cache);
532             goto FOUND_IT;
533         }
534     }
535 
536     /* Release the mutex now, before we create a new entry (which might have
537         side-effects like trying to access the cache/mutex (yikes!)
538     */
539     ac.release();           // release the mutex now
540     insideMutex = false;    // can't use globals anymore
541 
542     // Check if we can create a scaler-context before creating the glyphcache.
543     // If not, we may have exhausted OS/font resources, so try purging the
544     // cache once and try again.
545     {
546         // pass true the first time, to notice if the scalercontext failed,
547         // so we can try the purge.
548         SkScalerContext* ctx = typeface->createScalerContext(desc, true);
549         if (!ctx) {
550             getSharedGlobals().purgeAll();
551             ctx = typeface->createScalerContext(desc, false);
552             SkASSERT(ctx);
553         }
554         cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
555     }
556 
557 FOUND_IT:
558 
559     AutoValidate av(cache);
560 
561     if (!proc(cache, context)) {   // need to reattach
562         if (insideMutex) {
563             globals.internalAttachCacheToHead(cache);
564         } else {
565             globals.attachCacheToHead(cache);
566         }
567         cache = NULL;
568     }
569     return cache;
570 }
571 
AttachCache(SkGlyphCache * cache)572 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
573     SkASSERT(cache);
574     SkASSERT(cache->fNext == NULL);
575 
576     getGlobals().attachCacheToHead(cache);
577 }
578 
579 ///////////////////////////////////////////////////////////////////////////////
580 
attachCacheToHead(SkGlyphCache * cache)581 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
582     SkAutoMutexAcquire    ac(fMutex);
583 
584     this->validate();
585     cache->validate();
586 
587     this->internalAttachCacheToHead(cache);
588     this->internalPurge();
589 }
590 
internalGetTail() const591 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
592     SkGlyphCache* cache = fHead;
593     if (cache) {
594         while (cache->fNext) {
595             cache = cache->fNext;
596         }
597     }
598     return cache;
599 }
600 
internalPurge(size_t minBytesNeeded)601 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
602     this->validate();
603 
604     size_t bytesNeeded = 0;
605     if (fTotalMemoryUsed > fCacheSizeLimit) {
606         bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
607     }
608     bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
609     if (bytesNeeded) {
610         // no small purges!
611         bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
612     }
613 
614     int countNeeded = 0;
615     if (fCacheCount > fCacheCountLimit) {
616         countNeeded = fCacheCount - fCacheCountLimit;
617         // no small purges!
618         countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
619     }
620 
621     // early exit
622     if (!countNeeded && !bytesNeeded) {
623         return 0;
624     }
625 
626     size_t  bytesFreed = 0;
627     int     countFreed = 0;
628 
629     // we start at the tail and proceed backwards, as the linklist is in LRU
630     // order, with unimportant entries at the tail.
631     SkGlyphCache* cache = this->internalGetTail();
632     while (cache != NULL &&
633            (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
634         SkGlyphCache* prev = cache->fPrev;
635         bytesFreed += cache->fMemoryUsed;
636         countFreed += 1;
637 
638         this->internalDetachCache(cache);
639         SkDELETE(cache);
640         cache = prev;
641     }
642 
643     this->validate();
644 
645 #ifdef SPEW_PURGE_STATUS
646     if (countFreed) {
647         SkDebugf("purging %dK from font cache [%d entries]\n",
648                  (int)(bytesFreed >> 10), countFreed);
649     }
650 #endif
651 
652     return bytesFreed;
653 }
654 
internalAttachCacheToHead(SkGlyphCache * cache)655 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
656     SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
657     if (fHead) {
658         fHead->fPrev = cache;
659         cache->fNext = fHead;
660     }
661     fHead = cache;
662 
663     fCacheCount += 1;
664     fTotalMemoryUsed += cache->fMemoryUsed;
665 }
666 
internalDetachCache(SkGlyphCache * cache)667 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
668     SkASSERT(fCacheCount > 0);
669     fCacheCount -= 1;
670     fTotalMemoryUsed -= cache->fMemoryUsed;
671 
672     if (cache->fPrev) {
673         cache->fPrev->fNext = cache->fNext;
674     } else {
675         fHead = cache->fNext;
676     }
677     if (cache->fNext) {
678         cache->fNext->fPrev = cache->fPrev;
679     }
680     cache->fPrev = cache->fNext = NULL;
681 }
682 
683 ///////////////////////////////////////////////////////////////////////////////
684 
685 #ifdef SK_DEBUG
686 
validate() const687 void SkGlyphCache::validate() const {
688 #ifdef SK_DEBUG_GLYPH_CACHE
689     int count = fGlyphArray.count();
690     for (int i = 0; i < count; i++) {
691         const SkGlyph* glyph = fGlyphArray[i];
692         SkASSERT(glyph);
693         SkASSERT(fGlyphAlloc.contains(glyph));
694         if (glyph->fImage) {
695             SkASSERT(fGlyphAlloc.contains(glyph->fImage));
696         }
697         if (glyph->fDistanceField) {
698             SkASSERT(fGlyphAlloc.contains(glyph->fDistanceField));
699         }
700     }
701 #endif
702 }
703 
validate() const704 void SkGlyphCache_Globals::validate() const {
705     size_t computedBytes = 0;
706     int computedCount = 0;
707 
708     const SkGlyphCache* head = fHead;
709     while (head != NULL) {
710         computedBytes += head->fMemoryUsed;
711         computedCount += 1;
712         head = head->fNext;
713     }
714 
715     SkASSERT(fTotalMemoryUsed == computedBytes);
716     SkASSERT(fCacheCount == computedCount);
717 }
718 
719 #endif
720 
721 ///////////////////////////////////////////////////////////////////////////////
722 ///////////////////////////////////////////////////////////////////////////////
723 
724 #include "SkTypefaceCache.h"
725 
GetFontCacheLimit()726 size_t SkGraphics::GetFontCacheLimit() {
727     return getSharedGlobals().getCacheSizeLimit();
728 }
729 
SetFontCacheLimit(size_t bytes)730 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
731     return getSharedGlobals().setCacheSizeLimit(bytes);
732 }
733 
GetFontCacheUsed()734 size_t SkGraphics::GetFontCacheUsed() {
735     return getSharedGlobals().getTotalMemoryUsed();
736 }
737 
GetFontCacheCountLimit()738 int SkGraphics::GetFontCacheCountLimit() {
739     return getSharedGlobals().getCacheCountLimit();
740 }
741 
SetFontCacheCountLimit(int count)742 int SkGraphics::SetFontCacheCountLimit(int count) {
743     return getSharedGlobals().setCacheCountLimit(count);
744 }
745 
GetFontCacheCountUsed()746 int SkGraphics::GetFontCacheCountUsed() {
747     return getSharedGlobals().getCacheCountUsed();
748 }
749 
PurgeFontCache()750 void SkGraphics::PurgeFontCache() {
751     getSharedGlobals().purgeAll();
752     SkTypefaceCache::PurgeAll();
753 }
754 
GetTLSFontCacheLimit()755 size_t SkGraphics::GetTLSFontCacheLimit() {
756     const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
757     return tls ? tls->getCacheSizeLimit() : 0;
758 }
759 
SetTLSFontCacheLimit(size_t bytes)760 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
761     if (0 == bytes) {
762         SkGlyphCache_Globals::DeleteTLS();
763     } else {
764         SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
765     }
766 }
767