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