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