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