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
24 namespace {
25
create_globals()26 SkGlyphCache_Globals* create_globals() {
27 return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
28 }
29
30 } // namespace
31
32 // Returns the shared globals
getSharedGlobals()33 static 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
getGlobals()39 static 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
RecordHashSuccess()50 static void RecordHashSuccess() {
51 gHashSuccess += 1;
52 }
53
RecordHashCollisionIf(bool pred)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
SkGlyphCache(SkTypeface * typeface,const SkDescriptor * desc,SkScalerContext * ctx)76 SkGlyphCache::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
~SkGlyphCache()99 SkGlyphCache::~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
unicharToGlyph(SkUnichar charCode)146 uint16_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
glyphToUnichar(uint16_t glyphID)158 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
159 return fScalerContext->glyphIDToChar(glyphID);
160 }
161
getGlyphCount()162 unsigned SkGlyphCache::getGlyphCount() {
163 return fScalerContext->getGlyphCount();
164 }
165
166 ///////////////////////////////////////////////////////////////////////////////
167
getUnicharAdvance(SkUnichar charCode)168 const 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
getGlyphIDAdvance(uint16_t glyphID)183 const 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
getUnicharMetrics(SkUnichar charCode)198 const 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
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)220 const 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
getGlyphIDMetrics(uint16_t glyphID)243 const 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
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)263 const 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
lookupMetrics(uint32_t id,MetricsType mtype)284 SkGlyph* 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
findImage(const SkGlyph & glyph)335 const 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
findPath(const SkGlyph & glyph)355 const 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
findDistanceField(const SkGlyph & glyph)367 const 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
getAuxProcData(void (* proc)(void *),void ** dataPtr) const408 bool 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
setAuxProc(void (* proc)(void *),void * data)422 void 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
invokeAndRemoveAuxProcs()443 void 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
setCacheSizeLimit(size_t newLimit)458 size_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
setCacheCountLimit(int newCount)472 int 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
purgeAll()485 void SkGlyphCache_Globals::purgeAll() {
486 SkAutoMutexAcquire ac(fMutex);
487 this->internalPurge(fTotalMemoryUsed);
488 }
489
VisitAllCaches(bool (* proc)(SkGlyphCache *,void *),void * context)490 void 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 */
VisitCache(SkTypeface * typeface,const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)513 SkGlyphCache* 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
557 FOUND_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
AttachCache(SkGlyphCache * cache)572 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
573 SkASSERT(cache);
574 SkASSERT(cache->fNext == NULL);
575
576 getGlobals().attachCacheToHead(cache);
577 }
578
579 ///////////////////////////////////////////////////////////////////////////////
580
attachCacheToHead(SkGlyphCache * cache)581 void 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
internalGetTail() const591 SkGlyphCache* 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
internalPurge(size_t minBytesNeeded)601 size_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
internalAttachCacheToHead(SkGlyphCache * cache)655 void 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
internalDetachCache(SkGlyphCache * cache)667 void 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
validate() const687 void 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
validate() const704 void 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
GetFontCacheLimit()726 size_t SkGraphics::GetFontCacheLimit() {
727 return getSharedGlobals().getCacheSizeLimit();
728 }
729
SetFontCacheLimit(size_t bytes)730 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
731 return getSharedGlobals().setCacheSizeLimit(bytes);
732 }
733
GetFontCacheUsed()734 size_t SkGraphics::GetFontCacheUsed() {
735 return getSharedGlobals().getTotalMemoryUsed();
736 }
737
GetFontCacheCountLimit()738 int SkGraphics::GetFontCacheCountLimit() {
739 return getSharedGlobals().getCacheCountLimit();
740 }
741
SetFontCacheCountLimit(int count)742 int SkGraphics::SetFontCacheCountLimit(int count) {
743 return getSharedGlobals().setCacheCountLimit(count);
744 }
745
GetFontCacheCountUsed()746 int SkGraphics::GetFontCacheCountUsed() {
747 return getSharedGlobals().getCacheCountUsed();
748 }
749
PurgeFontCache()750 void SkGraphics::PurgeFontCache() {
751 getSharedGlobals().purgeAll();
752 SkTypefaceCache::PurgeAll();
753 }
754
GetTLSFontCacheLimit()755 size_t SkGraphics::GetTLSFontCacheLimit() {
756 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
757 return tls ? tls->getCacheSizeLimit() : 0;
758 }
759
SetTLSFontCacheLimit(size_t bytes)760 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
761 if (0 == bytes) {
762 SkGlyphCache_Globals::DeleteTLS();
763 } else {
764 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
765 }
766 }
767