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