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