Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 */
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"
19 //#define SPEW_PURGE_STATUS
20 //#define RECORD_HASH_EFFICIENCY
22 bool gSkSuppressFontCachePurgeSpew;
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 }
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 }
38 ///////////////////////////////////////////////////////////////////////////////
40 #ifdef RECORD_HASH_EFFICIENCY
41 static uint32_t gHashSuccess;
42 static uint32_t gHashCollision;
44 static void RecordHashSuccess() {
45 gHashSuccess += 1;
46 }
48 static void RecordHashCollisionIf(bool pred) {
49 if (pred) {
50 gHashCollision += 1;
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)
63 ///////////////////////////////////////////////////////////////////////////////
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)
70 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
71 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
72 SkASSERT(typeface);
73 SkASSERT(desc);
74 SkASSERT(ctx);
76 fPrev = fNext = NULL;
78 fDesc = desc->copy();
79 fScalerContext->getFontMetrics(&fFontMetrics);
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));
86 fMemoryUsed = sizeof(*this);
88 fGlyphArray.setReserve(kMinGlyphCount);
90 fAuxProcList = NULL;
91 }
93 SkGlyphCache::~SkGlyphCache() {
94 #if 0
95 {
96 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
97 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
98 size_t glyphHashUsed = 0;
99 size_t uniHashUsed = 0;
100 for (int i = 0; i < kHashCount; ++i) {
101 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
102 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
103 }
104 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
105 size_t imageUsed = 0;
106 for (int i = 0; i < fGlyphArray.count(); ++i) {
107 const SkGlyph& g = *fGlyphArray[i];
108 if (g.fImage) {
109 imageUsed += g.fHeight * g.rowBytes();
110 }
111 }
113 printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
114 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
116 }
117 #endif
118 SkGlyph** gptr = fGlyphArray.begin();
119 SkGlyph** stop = fGlyphArray.end();
120 while (gptr < stop) {
121 SkPath* path = (*gptr)->fPath;
122 if (path) {
123 SkDELETE(path);
124 }
125 gptr += 1;
126 }
127 SkDescriptor::Free(fDesc);
128 SkDELETE(fScalerContext);
129 this->invokeAndRemoveAuxProcs();
130 }
132 ///////////////////////////////////////////////////////////////////////////////
134 #ifdef SK_DEBUG
135 #define VALIDATE() AutoValidate av(this)
136 #else
137 #define VALIDATE()
138 #endif
140 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
141 VALIDATE();
142 uint32_t id = SkGlyph::MakeID(charCode);
143 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
145 if (rec.fID == id) {
146 return rec.fGlyph->getGlyphID();
147 } else {
148 return fScalerContext->charToGlyphID(charCode);
149 }
150 }
152 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
153 return fScalerContext->glyphIDToChar(glyphID);
154 }
156 unsigned SkGlyphCache::getGlyphCount() {
157 return fScalerContext->getGlyphCount();
158 }
160 ///////////////////////////////////////////////////////////////////////////////
162 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
163 VALIDATE();
164 uint32_t id = SkGlyph::MakeID(charCode);
165 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
167 if (rec->fID != id) {
168 // this ID is based on the UniChar
169 rec->fID = id;
170 // this ID is based on the glyph index
171 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
172 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
173 }
174 return *rec->fGlyph;
175 }
177 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
178 VALIDATE();
179 uint32_t id = SkGlyph::MakeID(glyphID);
180 unsigned index = ID2HashIndex(id);
181 SkGlyph* glyph = fGlyphHash[index];
183 if (NULL == glyph || glyph->fID != id) {
184 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
185 fGlyphHash[index] = glyph;
186 }
187 return *glyph;
188 }
190 ///////////////////////////////////////////////////////////////////////////////
192 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
193 VALIDATE();
194 uint32_t id = SkGlyph::MakeID(charCode);
195 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
197 if (rec->fID != id) {
198 RecordHashCollisionIf(rec->fGlyph != NULL);
199 // this ID is based on the UniChar
200 rec->fID = id;
201 // this ID is based on the glyph index
202 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
203 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
204 } else {
205 RecordHashSuccess();
206 if (rec->fGlyph->isJustAdvance()) {
207 fScalerContext->getMetrics(rec->fGlyph);
208 }
209 }
210 SkASSERT(rec->fGlyph->isFullMetrics());
211 return *rec->fGlyph;
212 }
214 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
215 SkFixed x, SkFixed y) {
216 VALIDATE();
217 uint32_t id = SkGlyph::MakeID(charCode, x, y);
218 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
220 if (rec->fID != id) {
221 RecordHashCollisionIf(rec->fGlyph != NULL);
222 // this ID is based on the UniChar
223 rec->fID = id;
224 // this ID is based on the glyph index
225 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
226 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
227 } else {
228 RecordHashSuccess();
229 if (rec->fGlyph->isJustAdvance()) {
230 fScalerContext->getMetrics(rec->fGlyph);
231 }
232 }
233 SkASSERT(rec->fGlyph->isFullMetrics());
234 return *rec->fGlyph;
235 }
237 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
238 VALIDATE();
239 uint32_t id = SkGlyph::MakeID(glyphID);
240 unsigned index = ID2HashIndex(id);
241 SkGlyph* glyph = fGlyphHash[index];
243 if (NULL == glyph || glyph->fID != id) {
244 RecordHashCollisionIf(glyph != NULL);
245 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
246 fGlyphHash[index] = glyph;
247 } else {
248 RecordHashSuccess();
249 if (glyph->isJustAdvance()) {
250 fScalerContext->getMetrics(glyph);
251 }
252 }
253 SkASSERT(glyph->isFullMetrics());
254 return *glyph;
255 }
257 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
258 SkFixed x, SkFixed y) {
259 VALIDATE();
260 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
261 unsigned index = ID2HashIndex(id);
262 SkGlyph* glyph = fGlyphHash[index];
264 if (NULL == glyph || glyph->fID != id) {
265 RecordHashCollisionIf(glyph != NULL);
266 glyph = this->lookupMetrics(id, kFull_MetricsType);
267 fGlyphHash[index] = glyph;
268 } else {
269 RecordHashSuccess();
270 if (glyph->isJustAdvance()) {
271 fScalerContext->getMetrics(glyph);
272 }
273 }
274 SkASSERT(glyph->isFullMetrics());
275 return *glyph;
276 }
278 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
279 SkGlyph* glyph;
281 int hi = 0;
282 int count = fGlyphArray.count();
284 if (count) {
285 SkGlyph** gptr = fGlyphArray.begin();
286 int lo = 0;
288 hi = count - 1;
289 while (lo < hi) {
290 int mid = (hi + lo) >> 1;
291 if (gptr[mid]->fID < id) {
292 lo = mid + 1;
293 } else {
294 hi = mid;
295 }
296 }
297 glyph = gptr[hi];
298 if (glyph->fID == id) {
299 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
300 fScalerContext->getMetrics(glyph);
301 }
302 return glyph;
303 }
305 // check if we need to bump hi before falling though to the allocator
306 if (glyph->fID < id) {
307 hi += 1;
308 }
309 }
311 // not found, but hi tells us where to inser the new glyph
312 fMemoryUsed += sizeof(SkGlyph);
314 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
315 SkChunkAlloc::kThrow_AllocFailType);
316 glyph->init(id);
317 *fGlyphArray.insert(hi) = glyph;
319 if (kJustAdvance_MetricsType == mtype) {
320 fScalerContext->getAdvance(glyph);
321 } else {
322 SkASSERT(kFull_MetricsType == mtype);
323 fScalerContext->getMetrics(glyph);
324 }
326 return glyph;
327 }
329 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
330 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
331 if (glyph.fImage == NULL) {
332 size_t size = glyph.computeImageSize();
333 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
334 SkChunkAlloc::kReturnNil_AllocFailType);
335 // check that alloc() actually succeeded
336 if (glyph.fImage) {
337 fScalerContext->getImage(glyph);
338 // TODO: the scaler may have changed the maskformat during
339 // getImage (e.g. from AA or LCD to BW) which means we may have
340 // overallocated the buffer. Check if the new computedImageSize
341 // is smaller, and if so, strink the alloc size in fImageAlloc.
342 fMemoryUsed += size;
343 }
344 }
345 }
346 return glyph.fImage;
347 }
349 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
350 if (glyph.fWidth) {
351 if (glyph.fPath == NULL) {
352 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
353 fScalerContext->getPath(glyph, glyph.fPath);
354 fMemoryUsed += sizeof(SkPath) +
355 glyph.fPath->countPoints() * sizeof(SkPoint);
356 }
357 }
358 return glyph.fPath;
359 }
361 ///////////////////////////////////////////////////////////////////////////////
363 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
364 const AuxProcRec* rec = fAuxProcList;
365 while (rec) {
366 if (rec->fProc == proc) {
367 if (dataPtr) {
368 *dataPtr = rec->fData;
369 }
370 return true;
371 }
372 rec = rec->fNext;
373 }
374 return false;
375 }
377 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
378 if (proc == NULL) {
379 return;
380 }
382 AuxProcRec* rec = fAuxProcList;
383 while (rec) {
384 if (rec->fProc == proc) {
385 rec->fData = data;
386 return;
387 }
388 rec = rec->fNext;
389 }
390 // not found, create a new rec
391 rec = SkNEW(AuxProcRec);
392 rec->fProc = proc;
393 rec->fData = data;
394 rec->fNext = fAuxProcList;
395 fAuxProcList = rec;
396 }
398 void SkGlyphCache::invokeAndRemoveAuxProcs() {
399 AuxProcRec* rec = fAuxProcList;
400 while (rec) {
401 rec->fProc(rec->fData);
402 AuxProcRec* next = rec->fNext;
403 SkDELETE(rec);
404 rec = next;
405 }
406 }
408 ///////////////////////////////////////////////////////////////////////////////
409 ///////////////////////////////////////////////////////////////////////////////
411 #include "SkThread.h"
413 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
414 static const size_t minLimit = 256 * 1024;
415 if (newLimit < minLimit) {
416 newLimit = minLimit;
417 }
419 SkAutoMutexAcquire ac(fMutex);
421 size_t prevLimit = fCacheSizeLimit;
422 fCacheSizeLimit = newLimit;
423 this->internalPurge();
424 return prevLimit;
425 }
427 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
428 if (newCount < 0) {
429 newCount = 0;
430 }
432 SkAutoMutexAcquire ac(fMutex);
434 int prevCount = fCacheCountLimit;
435 fCacheCountLimit = newCount;
436 this->internalPurge();
437 return prevCount;
438 }
440 void SkGlyphCache_Globals::purgeAll() {
441 SkAutoMutexAcquire ac(fMutex);
442 this->internalPurge(fTotalMemoryUsed);
443 }
445 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
446 void* context) {
447 SkGlyphCache_Globals& globals = getGlobals();
448 SkAutoMutexAcquire ac(globals.fMutex);
449 SkGlyphCache* cache;
451 globals.validate();
453 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
454 if (proc(cache, context)) {
455 break;
456 }
457 }
459 globals.validate();
460 }
462 /* This guy calls the visitor from within the mutext lock, so the visitor
463 cannot:
464 - take too much time
465 - try to acquire the mutext again
466 - call a fontscaler (which might call into the cache)
467 */
468 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
469 const SkDescriptor* desc,
470 bool (*proc)(const SkGlyphCache*, void*),
471 void* context) {
472 if (!typeface) {
473 typeface = SkTypeface::GetDefaultTypeface();
474 }
475 SkASSERT(desc);
477 SkGlyphCache_Globals& globals = getGlobals();
478 SkAutoMutexAcquire ac(globals.fMutex);
479 SkGlyphCache* cache;
480 bool insideMutex = true;
482 globals.validate();
484 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
485 if (cache->fDesc->equals(*desc)) {
486 globals.internalDetachCache(cache);
487 goto FOUND_IT;
488 }
489 }
491 /* Release the mutex now, before we create a new entry (which might have
492 side-effects like trying to access the cache/mutex (yikes!)
493 */
494 ac.release(); // release the mutex now
495 insideMutex = false; // can't use globals anymore
497 // Check if we can create a scaler-context before creating the glyphcache.
498 // If not, we may have exhausted OS/font resources, so try purging the
499 // cache once and try again.
500 {
501 // pass true the first time, to notice if the scalercontext failed,
502 // so we can try the purge.
503 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
504 if (!ctx) {
505 getSharedGlobals().purgeAll();
506 ctx = typeface->createScalerContext(desc, false);
507 SkASSERT(ctx);
508 }
509 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
510 }
512 FOUND_IT:
514 AutoValidate av(cache);
516 if (!proc(cache, context)) { // need to reattach
517 if (insideMutex) {
518 globals.internalAttachCacheToHead(cache);
519 } else {
520 globals.attachCacheToHead(cache);
521 }
522 cache = NULL;
523 }
524 return cache;
525 }
527 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
528 SkASSERT(cache);
529 SkASSERT(cache->fNext == NULL);
531 getGlobals().attachCacheToHead(cache);
532 }
534 ///////////////////////////////////////////////////////////////////////////////
536 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
537 SkAutoMutexAcquire ac(fMutex);
539 this->validate();
540 cache->validate();
542 this->internalAttachCacheToHead(cache);
543 this->internalPurge();
544 }
546 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
547 SkGlyphCache* cache = fHead;
548 if (cache) {
549 while (cache->fNext) {
550 cache = cache->fNext;
551 }
552 }
553 return cache;
554 }
556 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
557 this->validate();
559 size_t bytesNeeded = 0;
560 if (fTotalMemoryUsed > fCacheSizeLimit) {
561 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
562 }
563 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
564 if (bytesNeeded) {
565 // no small purges!
566 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
567 }
569 int countNeeded = 0;
570 if (fCacheCount > fCacheCountLimit) {
571 countNeeded = fCacheCount - fCacheCountLimit;
572 // no small purges!
573 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
574 }
576 // early exit
577 if (!countNeeded && !bytesNeeded) {
578 return 0;
579 }
581 size_t bytesFreed = 0;
582 int countFreed = 0;
584 // we start at the tail and proceed backwards, as the linklist is in LRU
585 // order, with unimportant entries at the tail.
586 SkGlyphCache* cache = this->internalGetTail();
587 while (cache != NULL &&
588 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
589 SkGlyphCache* prev = cache->fPrev;
590 bytesFreed += cache->fMemoryUsed;
591 countFreed += 1;
593 this->internalDetachCache(cache);
594 SkDELETE(cache);
595 cache = prev;
596 }
598 this->validate();
600 #ifdef SPEW_PURGE_STATUS
601 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
602 SkDebugf("purging %dK from font cache [%d entries]\n",
603 (int)(bytesFreed >> 10), countFreed);
604 }
605 #endif
607 return bytesFreed;
608 }
610 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
611 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
612 if (fHead) {
613 fHead->fPrev = cache;
614 cache->fNext = fHead;
615 }
616 fHead = cache;
618 fCacheCount += 1;
619 fTotalMemoryUsed += cache->fMemoryUsed;
620 }
622 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
623 SkASSERT(fCacheCount > 0);
624 fCacheCount -= 1;
625 fTotalMemoryUsed -= cache->fMemoryUsed;
627 if (cache->fPrev) {
628 cache->fPrev->fNext = cache->fNext;
629 } else {
630 fHead = cache->fNext;
631 }
632 if (cache->fNext) {
633 cache->fNext->fPrev = cache->fPrev;
634 }
635 cache->fPrev = cache->fNext = NULL;
636 }
638 ///////////////////////////////////////////////////////////////////////////////
640 #ifdef SK_DEBUG
642 void SkGlyphCache::validate() const {
643 #ifdef SK_DEBUG_GLYPH_CACHE
644 int count = fGlyphArray.count();
645 for (int i = 0; i < count; i++) {
646 const SkGlyph* glyph = fGlyphArray[i];
647 SkASSERT(glyph);
648 SkASSERT(fGlyphAlloc.contains(glyph));
649 if (glyph->fImage) {
650 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
651 }
652 }
653 #endif
654 }
656 void SkGlyphCache_Globals::validate() const {
657 size_t computedBytes = 0;
658 int computedCount = 0;
660 const SkGlyphCache* head = fHead;
661 while (head != NULL) {
662 computedBytes += head->fMemoryUsed;
663 computedCount += 1;
664 head = head->fNext;
665 }
667 SkASSERT(fTotalMemoryUsed == computedBytes);
668 SkASSERT(fCacheCount == computedCount);
669 }
671 #endif
673 ///////////////////////////////////////////////////////////////////////////////
674 ///////////////////////////////////////////////////////////////////////////////
676 #include "SkTypefaceCache.h"
678 size_t SkGraphics::GetFontCacheLimit() {
679 return getSharedGlobals().getCacheSizeLimit();
680 }
682 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
683 return getSharedGlobals().setCacheSizeLimit(bytes);
684 }
686 size_t SkGraphics::GetFontCacheUsed() {
687 return getSharedGlobals().getTotalMemoryUsed();
688 }
690 int SkGraphics::GetFontCacheCountLimit() {
691 return getSharedGlobals().getCacheCountLimit();
692 }
694 int SkGraphics::SetFontCacheCountLimit(int count) {
695 return getSharedGlobals().setCacheCountLimit(count);
696 }
698 int SkGraphics::GetFontCacheCountUsed() {
699 return getSharedGlobals().getCacheCountUsed();
700 }
702 void SkGraphics::PurgeFontCache() {
703 getSharedGlobals().purgeAll();
704 SkTypefaceCache::PurgeAll();
705 }
707 size_t SkGraphics::GetTLSFontCacheLimit() {
708 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
709 return tls ? tls->getCacheSizeLimit() : 0;
710 }
712 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
713 if (0 == bytes) {
714 SkGlyphCache_Globals::DeleteTLS();
715 } else {
716 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
717 }
718 }