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