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
20namespace {
21
22SkGlyphCache_Globals* create_globals() {
23    return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
24}
25
26}  // namespace
27
28SK_DECLARE_STATIC_LAZY_PTR(SkGlyphCache_Globals, globals, create_globals);
29
30// Returns the shared globals
31static SkGlyphCache_Globals& getSharedGlobals() {
32    return *globals.get();
33}
34
35// Returns the TLS globals (if set), or the shared globals
36static 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
59SkGlyphCache::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
88SkGlyphCache::~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
127SkGlyphCache::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
142void 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
165uint16_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
177SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
178    return fScalerContext->glyphIDToChar(glyphID);
179}
180
181unsigned SkGlyphCache::getGlyphCount() {
182    return fScalerContext->getGlyphCount();
183}
184
185///////////////////////////////////////////////////////////////////////////////
186
187const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
188    VALIDATE();
189    return *this->lookupByChar(charCode, kJustAdvance_MetricsType);
190}
191
192const 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
200const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
201    VALIDATE();
202    return *this->lookupByChar(charCode, kFull_MetricsType);
203}
204
205const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
206                                               SkFixed x, SkFixed y) {
207    VALIDATE();
208    return *this->lookupByChar(charCode, kFull_MetricsType, x, y);
209}
210
211const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
212    VALIDATE();
213    uint32_t id = SkGlyph::MakeID(glyphID);
214    return *this->lookupByCombinedID(id, kFull_MetricsType);
215}
216
217const 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
223SkGlyph* 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
245SkGlyph* 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
264uint16_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
315const 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
335const 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
347void 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
372bool 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
386void 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
407void 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
422size_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
436int 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
449void 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*/
460SkGlyphCache* 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
504FOUND_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
519void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
520    SkASSERT(cache);
521    SkASSERT(cache->fNext == NULL);
522
523    getGlobals().attachCacheToHead(cache);
524}
525
526void 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
555void 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
565SkGlyphCache* 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
575size_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
629void 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
641void 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
661void 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
674void 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
696size_t SkGraphics::GetFontCacheLimit() {
697    return getSharedGlobals().getCacheSizeLimit();
698}
699
700size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
701    return getSharedGlobals().setCacheSizeLimit(bytes);
702}
703
704size_t SkGraphics::GetFontCacheUsed() {
705    return getSharedGlobals().getTotalMemoryUsed();
706}
707
708int SkGraphics::GetFontCacheCountLimit() {
709    return getSharedGlobals().getCacheCountLimit();
710}
711
712int SkGraphics::SetFontCacheCountLimit(int count) {
713    return getSharedGlobals().setCacheCountLimit(count);
714}
715
716int SkGraphics::GetFontCacheCountUsed() {
717    return getSharedGlobals().getCacheCountUsed();
718}
719
720void SkGraphics::PurgeFontCache() {
721    getSharedGlobals().purgeAll();
722    SkTypefaceCache::PurgeAll();
723}
724
725size_t SkGraphics::GetTLSFontCacheLimit() {
726    const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
727    return tls ? tls->getCacheSizeLimit() : 0;
728}
729
730void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
731    if (0 == bytes) {
732        SkGlyphCache_Globals::DeleteTLS();
733    } else {
734        SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
735    }
736}
737