SkGlyphCache.cpp revision 8a986721926f77aba3772acb5d1c575072acd05c
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
24namespace {
25
26SkGlyphCache_Globals* create_globals() {
27    return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
28}
29
30}  // namespace
31
32// Returns the shared globals
33static 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
39static 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
76SkGlyphCache::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
99SkGlyphCache::~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
146uint16_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
158SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
159    return fScalerContext->glyphIDToChar(glyphID);
160}
161
162unsigned SkGlyphCache::getGlyphCount() {
163    return fScalerContext->getGlyphCount();
164}
165
166///////////////////////////////////////////////////////////////////////////////
167
168const 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
183const 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
198const 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
220const 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
243const 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
263const 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
284SkGlyph* 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
335const 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
355const 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
367const 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
408bool 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
422void 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
443void 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
458size_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
472int 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
485void 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*/
496SkGlyphCache* 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
540FOUND_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
555void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
556    SkASSERT(cache);
557    SkASSERT(cache->fNext == NULL);
558
559    getGlobals().attachCacheToHead(cache);
560}
561
562///////////////////////////////////////////////////////////////////////////////
563
564void 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
574SkGlyphCache* 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
584size_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
638void 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
650void 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
670void 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
687void 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
709size_t SkGraphics::GetFontCacheLimit() {
710    return getSharedGlobals().getCacheSizeLimit();
711}
712
713size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
714    return getSharedGlobals().setCacheSizeLimit(bytes);
715}
716
717size_t SkGraphics::GetFontCacheUsed() {
718    return getSharedGlobals().getTotalMemoryUsed();
719}
720
721int SkGraphics::GetFontCacheCountLimit() {
722    return getSharedGlobals().getCacheCountLimit();
723}
724
725int SkGraphics::SetFontCacheCountLimit(int count) {
726    return getSharedGlobals().setCacheCountLimit(count);
727}
728
729int SkGraphics::GetFontCacheCountUsed() {
730    return getSharedGlobals().getCacheCountUsed();
731}
732
733void SkGraphics::PurgeFontCache() {
734    getSharedGlobals().purgeAll();
735    SkTypefaceCache::PurgeAll();
736}
737
738size_t SkGraphics::GetTLSFontCacheLimit() {
739    const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
740    return tls ? tls->getCacheSizeLimit() : 0;
741}
742
743void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
744    if (0 == bytes) {
745        SkGlyphCache_Globals::DeleteTLS();
746    } else {
747        SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
748    }
749}
750