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 "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 
     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
     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
     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 
     50     static void RecordHashSuccess() {
     51         gHashSuccess += 1;
     52     }
     53 
     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 
     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 
     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 
    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 
    158 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
    159     return fScalerContext->glyphIDToChar(glyphID);
    160 }
    161 
    162 unsigned SkGlyphCache::getGlyphCount() {
    163     return fScalerContext->getGlyphCount();
    164 }
    165 
    166 ///////////////////////////////////////////////////////////////////////////////
    167 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 (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 
    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 
    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 (image) {
    377                 const_cast<SkGlyph&>(glyph).fDistanceField = fGlyphAlloc.alloc(size,
    378                                             SkChunkAlloc::kReturnNil_AllocFailType);
    379                 if (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 
    408 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 
    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 
    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 
    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 
    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 
    485 void SkGlyphCache_Globals::purgeAll() {
    486     SkAutoMutexAcquire    ac(fMutex);
    487     this->internalPurge(fTotalMemoryUsed);
    488 }
    489 
    490 /*  This guy calls the visitor from within the mutext lock, so the visitor
    491     cannot:
    492     - take too much time
    493     - try to acquire the mutext again
    494     - call a fontscaler (which might call into the cache)
    495 */
    496 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
    497                               const SkDescriptor* desc,
    498                               bool (*proc)(const SkGlyphCache*, void*),
    499                               void* context) {
    500     if (!typeface) {
    501         typeface = SkTypeface::GetDefaultTypeface();
    502     }
    503     SkASSERT(desc);
    504 
    505     SkGlyphCache_Globals& globals = getGlobals();
    506     SkAutoMutexAcquire    ac(globals.fMutex);
    507     SkGlyphCache*         cache;
    508     bool                  insideMutex = true;
    509 
    510     globals.validate();
    511 
    512     for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
    513         if (cache->fDesc->equals(*desc)) {
    514             globals.internalDetachCache(cache);
    515             goto FOUND_IT;
    516         }
    517     }
    518 
    519     /* Release the mutex now, before we create a new entry (which might have
    520         side-effects like trying to access the cache/mutex (yikes!)
    521     */
    522     ac.release();           // release the mutex now
    523     insideMutex = false;    // can't use globals anymore
    524 
    525     // Check if we can create a scaler-context before creating the glyphcache.
    526     // If not, we may have exhausted OS/font resources, so try purging the
    527     // cache once and try again.
    528     {
    529         // pass true the first time, to notice if the scalercontext failed,
    530         // so we can try the purge.
    531         SkScalerContext* ctx = typeface->createScalerContext(desc, true);
    532         if (!ctx) {
    533             getSharedGlobals().purgeAll();
    534             ctx = typeface->createScalerContext(desc, false);
    535             SkASSERT(ctx);
    536         }
    537         cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
    538     }
    539 
    540 FOUND_IT:
    541 
    542     AutoValidate av(cache);
    543 
    544     if (!proc(cache, context)) {   // need to reattach
    545         if (insideMutex) {
    546             globals.internalAttachCacheToHead(cache);
    547         } else {
    548             globals.attachCacheToHead(cache);
    549         }
    550         cache = NULL;
    551     }
    552     return cache;
    553 }
    554 
    555 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
    556     SkASSERT(cache);
    557     SkASSERT(cache->fNext == NULL);
    558 
    559     getGlobals().attachCacheToHead(cache);
    560 }
    561 
    562 ///////////////////////////////////////////////////////////////////////////////
    563 
    564 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
    565     SkAutoMutexAcquire    ac(fMutex);
    566 
    567     this->validate();
    568     cache->validate();
    569 
    570     this->internalAttachCacheToHead(cache);
    571     this->internalPurge();
    572 }
    573 
    574 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
    575     SkGlyphCache* cache = fHead;
    576     if (cache) {
    577         while (cache->fNext) {
    578             cache = cache->fNext;
    579         }
    580     }
    581     return cache;
    582 }
    583 
    584 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
    585     this->validate();
    586 
    587     size_t bytesNeeded = 0;
    588     if (fTotalMemoryUsed > fCacheSizeLimit) {
    589         bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    590     }
    591     bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
    592     if (bytesNeeded) {
    593         // no small purges!
    594         bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
    595     }
    596 
    597     int countNeeded = 0;
    598     if (fCacheCount > fCacheCountLimit) {
    599         countNeeded = fCacheCount - fCacheCountLimit;
    600         // no small purges!
    601         countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
    602     }
    603 
    604     // early exit
    605     if (!countNeeded && !bytesNeeded) {
    606         return 0;
    607     }
    608 
    609     size_t  bytesFreed = 0;
    610     int     countFreed = 0;
    611 
    612     // we start at the tail and proceed backwards, as the linklist is in LRU
    613     // order, with unimportant entries at the tail.
    614     SkGlyphCache* cache = this->internalGetTail();
    615     while (cache != NULL &&
    616            (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
    617         SkGlyphCache* prev = cache->fPrev;
    618         bytesFreed += cache->fMemoryUsed;
    619         countFreed += 1;
    620 
    621         this->internalDetachCache(cache);
    622         SkDELETE(cache);
    623         cache = prev;
    624     }
    625 
    626     this->validate();
    627 
    628 #ifdef SPEW_PURGE_STATUS
    629     if (countFreed) {
    630         SkDebugf("purging %dK from font cache [%d entries]\n",
    631                  (int)(bytesFreed >> 10), countFreed);
    632     }
    633 #endif
    634 
    635     return bytesFreed;
    636 }
    637 
    638 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
    639     SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
    640     if (fHead) {
    641         fHead->fPrev = cache;
    642         cache->fNext = fHead;
    643     }
    644     fHead = cache;
    645 
    646     fCacheCount += 1;
    647     fTotalMemoryUsed += cache->fMemoryUsed;
    648 }
    649 
    650 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
    651     SkASSERT(fCacheCount > 0);
    652     fCacheCount -= 1;
    653     fTotalMemoryUsed -= cache->fMemoryUsed;
    654 
    655     if (cache->fPrev) {
    656         cache->fPrev->fNext = cache->fNext;
    657     } else {
    658         fHead = cache->fNext;
    659     }
    660     if (cache->fNext) {
    661         cache->fNext->fPrev = cache->fPrev;
    662     }
    663     cache->fPrev = cache->fNext = NULL;
    664 }
    665 
    666 ///////////////////////////////////////////////////////////////////////////////
    667 
    668 #ifdef SK_DEBUG
    669 
    670 void SkGlyphCache::validate() const {
    671 #ifdef SK_DEBUG_GLYPH_CACHE
    672     int count = fGlyphArray.count();
    673     for (int i = 0; i < count; i++) {
    674         const SkGlyph* glyph = fGlyphArray[i];
    675         SkASSERT(glyph);
    676         SkASSERT(fGlyphAlloc.contains(glyph));
    677         if (glyph->fImage) {
    678             SkASSERT(fGlyphAlloc.contains(glyph->fImage));
    679         }
    680         if (glyph->fDistanceField) {
    681             SkASSERT(fGlyphAlloc.contains(glyph->fDistanceField));
    682         }
    683     }
    684 #endif
    685 }
    686 
    687 void SkGlyphCache_Globals::validate() const {
    688     size_t computedBytes = 0;
    689     int computedCount = 0;
    690 
    691     const SkGlyphCache* head = fHead;
    692     while (head != NULL) {
    693         computedBytes += head->fMemoryUsed;
    694         computedCount += 1;
    695         head = head->fNext;
    696     }
    697 
    698     SkASSERT(fTotalMemoryUsed == computedBytes);
    699     SkASSERT(fCacheCount == computedCount);
    700 }
    701 
    702 #endif
    703 
    704 ///////////////////////////////////////////////////////////////////////////////
    705 ///////////////////////////////////////////////////////////////////////////////
    706 
    707 #include "SkTypefaceCache.h"
    708 
    709 size_t SkGraphics::GetFontCacheLimit() {
    710     return getSharedGlobals().getCacheSizeLimit();
    711 }
    712 
    713 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
    714     return getSharedGlobals().setCacheSizeLimit(bytes);
    715 }
    716 
    717 size_t SkGraphics::GetFontCacheUsed() {
    718     return getSharedGlobals().getTotalMemoryUsed();
    719 }
    720 
    721 int SkGraphics::GetFontCacheCountLimit() {
    722     return getSharedGlobals().getCacheCountLimit();
    723 }
    724 
    725 int SkGraphics::SetFontCacheCountLimit(int count) {
    726     return getSharedGlobals().setCacheCountLimit(count);
    727 }
    728 
    729 int SkGraphics::GetFontCacheCountUsed() {
    730     return getSharedGlobals().getCacheCountUsed();
    731 }
    732 
    733 void SkGraphics::PurgeFontCache() {
    734     getSharedGlobals().purgeAll();
    735     SkTypefaceCache::PurgeAll();
    736 }
    737 
    738 size_t SkGraphics::GetTLSFontCacheLimit() {
    739     const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
    740     return tls ? tls->getCacheSizeLimit() : 0;
    741 }
    742 
    743 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
    744     if (0 == bytes) {
    745         SkGlyphCache_Globals::DeleteTLS();
    746     } else {
    747         SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
    748     }
    749 }
    750