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