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