• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libs/graphics/sgl/SkGlyphCache.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include "SkGlyphCache.h"
19 #include "SkFontHost.h"
20 #include "SkPaint.h"
21 #include "SkTemplates.h"
22 
23 //#define SPEW_PURGE_STATUS
24 //#define USE_CACHE_HASH
25 //#define RECORD_HASH_EFFICIENCY
26 
27 ///////////////////////////////////////////////////////////////////////////////
28 
29 #ifdef RECORD_HASH_EFFICIENCY
30     static uint32_t gHashSuccess;
31     static uint32_t gHashCollision;
32 
RecordHashSuccess()33     static void RecordHashSuccess() {
34         gHashSuccess += 1;
35     }
36 
RecordHashCollisionIf(bool pred)37     static void RecordHashCollisionIf(bool pred) {
38         if (pred) {
39             gHashCollision += 1;
40 
41             uint32_t total = gHashSuccess + gHashCollision;
42             SkDebugf("Font Cache Hash success rate: %d%%\n",
43                      100 * gHashSuccess / total);
44         }
45     }
46 #else
47     #define RecordHashSuccess() (void)0
48     #define RecordHashCollisionIf(pred) (void)0
49 #endif
50 #define RecordHashCollision() RecordHashCollisionIf(true)
51 
52 ///////////////////////////////////////////////////////////////////////////////
53 
54 #define kMinGlphAlloc       (sizeof(SkGlyph) * 64)
55 #define kMinImageAlloc      (24 * 64)   // should be pointsize-dependent
56 
57 #define METRICS_RESERVE_COUNT  128  // so we don't grow this array a lot
58 
SkGlyphCache(const SkDescriptor * desc)59 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc)
60         : fGlyphAlloc(kMinGlphAlloc), fImageAlloc(kMinImageAlloc) {
61     fPrev = fNext = NULL;
62 
63     fDesc = desc->copy();
64     fScalerContext = SkScalerContext::Create(desc);
65     fScalerContext->getFontMetrics(NULL, &fFontMetricsY);
66 
67     // init to 0 so that all of the pointers will be null
68     memset(fGlyphHash, 0, sizeof(fGlyphHash));
69     // init with 0xFF so that the charCode field will be -1, which is invalid
70     memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
71 
72     fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
73 
74     fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
75 
76     fMetricsCount = 0;
77     fAdvanceCount = 0;
78     fAuxProcList = NULL;
79 }
80 
~SkGlyphCache()81 SkGlyphCache::~SkGlyphCache() {
82     SkGlyph**   gptr = fGlyphArray.begin();
83     SkGlyph**   stop = fGlyphArray.end();
84     while (gptr < stop) {
85         SkPath* path = (*gptr)->fPath;
86         if (path) {
87             SkDELETE(path);
88         }
89         gptr += 1;
90     }
91     SkDescriptor::Free(fDesc);
92     SkDELETE(fScalerContext);
93     this->invokeAndRemoveAuxProcs();
94 }
95 
96 ///////////////////////////////////////////////////////////////////////////////
97 
98 #ifdef SK_DEBUG
99 #define VALIDATE()  AutoValidate av(this)
100 #else
101 #define VALIDATE()
102 #endif
103 
unicharToGlyph(SkUnichar charCode)104 uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
105     VALIDATE();
106     uint32_t id = SkGlyph::MakeID(charCode);
107     const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
108 
109     if (rec.fID == id) {
110         return rec.fGlyph->getGlyphID();
111     } else {
112         return fScalerContext->charToGlyphID(charCode);
113     }
114 }
115 
glyphToUnichar(uint16_t glyphID)116 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
117     return fScalerContext->glyphIDToChar(glyphID);
118 }
119 
getGlyphCount()120 unsigned SkGlyphCache::getGlyphCount() {
121     return fScalerContext->getGlyphCount();
122 }
123 
124 ///////////////////////////////////////////////////////////////////////////////
125 
getUnicharAdvance(SkUnichar charCode)126 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
127     VALIDATE();
128     uint32_t id = SkGlyph::MakeID(charCode);
129     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
130 
131     if (rec->fID != id) {
132         // this ID is based on the UniChar
133         rec->fID = id;
134         // this ID is based on the glyph index
135         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
136         rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
137     }
138     return *rec->fGlyph;
139 }
140 
getGlyphIDAdvance(uint16_t glyphID)141 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
142     VALIDATE();
143     uint32_t id = SkGlyph::MakeID(glyphID);
144     unsigned index = ID2HashIndex(id);
145     SkGlyph* glyph = fGlyphHash[index];
146 
147     if (NULL == glyph || glyph->fID != id) {
148         glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
149         fGlyphHash[index] = glyph;
150     }
151     return *glyph;
152 }
153 
154 ///////////////////////////////////////////////////////////////////////////////
155 
getUnicharMetrics(SkUnichar charCode)156 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
157     VALIDATE();
158     uint32_t id = SkGlyph::MakeID(charCode);
159     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
160 
161     if (rec->fID != id) {
162         RecordHashCollisionIf(rec->fGlyph != NULL);
163         // this ID is based on the UniChar
164         rec->fID = id;
165         // this ID is based on the glyph index
166         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
167         rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
168     } else {
169         RecordHashSuccess();
170         if (rec->fGlyph->isJustAdvance()) {
171             fScalerContext->getMetrics(rec->fGlyph);
172         }
173     }
174     SkASSERT(rec->fGlyph->isFullMetrics());
175     return *rec->fGlyph;
176 }
177 
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)178 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
179                                                SkFixed x, SkFixed y) {
180     VALIDATE();
181     uint32_t id = SkGlyph::MakeID(charCode, x, y);
182     CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
183 
184     if (rec->fID != id) {
185         RecordHashCollisionIf(rec->fGlyph != NULL);
186         // this ID is based on the UniChar
187         rec->fID = id;
188         // this ID is based on the glyph index
189         id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
190         rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
191     } else {
192         RecordHashSuccess();
193         if (rec->fGlyph->isJustAdvance()) {
194             fScalerContext->getMetrics(rec->fGlyph);
195         }
196     }
197     SkASSERT(rec->fGlyph->isFullMetrics());
198     return *rec->fGlyph;
199 }
200 
getGlyphIDMetrics(uint16_t glyphID)201 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
202     VALIDATE();
203     uint32_t id = SkGlyph::MakeID(glyphID);
204     unsigned index = ID2HashIndex(id);
205     SkGlyph* glyph = fGlyphHash[index];
206 
207     if (NULL == glyph || glyph->fID != id) {
208         RecordHashCollisionIf(glyph != NULL);
209         glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
210         fGlyphHash[index] = glyph;
211     } else {
212         RecordHashSuccess();
213         if (glyph->isJustAdvance()) {
214             fScalerContext->getMetrics(glyph);
215         }
216     }
217     SkASSERT(glyph->isFullMetrics());
218     return *glyph;
219 }
220 
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)221 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
222                                                SkFixed x, SkFixed y) {
223     VALIDATE();
224     uint32_t id = SkGlyph::MakeID(glyphID, x, y);
225     unsigned index = ID2HashIndex(id);
226     SkGlyph* glyph = fGlyphHash[index];
227 
228     if (NULL == glyph || glyph->fID != id) {
229         RecordHashCollisionIf(glyph != NULL);
230         glyph = this->lookupMetrics(id, kFull_MetricsType);
231         fGlyphHash[index] = glyph;
232     } else {
233         RecordHashSuccess();
234         if (glyph->isJustAdvance()) {
235             fScalerContext->getMetrics(glyph);
236         }
237     }
238     SkASSERT(glyph->isFullMetrics());
239     return *glyph;
240 }
241 
lookupMetrics(uint32_t id,MetricsType mtype)242 SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
243     SkGlyph* glyph;
244 
245     int     hi = 0;
246     int     count = fGlyphArray.count();
247 
248     if (count) {
249         SkGlyph**   gptr = fGlyphArray.begin();
250         int     lo = 0;
251 
252         hi = count - 1;
253         while (lo < hi) {
254             int mid = (hi + lo) >> 1;
255             if (gptr[mid]->fID < id) {
256                 lo = mid + 1;
257             } else {
258                 hi = mid;
259             }
260         }
261         glyph = gptr[hi];
262         if (glyph->fID == id) {
263             if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
264                 fScalerContext->getMetrics(glyph);
265             }
266             return glyph;
267         }
268 
269         // check if we need to bump hi before falling though to the allocator
270         if (glyph->fID < id) {
271             hi += 1;
272         }
273     }
274 
275     // not found, but hi tells us where to inser the new glyph
276     fMemoryUsed += sizeof(SkGlyph);
277 
278     glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
279                                         SkChunkAlloc::kThrow_AllocFailType);
280     glyph->init(id);
281     *fGlyphArray.insert(hi) = glyph;
282 
283     if (kJustAdvance_MetricsType == mtype) {
284         fScalerContext->getAdvance(glyph);
285         fAdvanceCount += 1;
286     } else {
287         SkASSERT(kFull_MetricsType == mtype);
288         fScalerContext->getMetrics(glyph);
289         fMetricsCount += 1;
290     }
291 
292     return glyph;
293 }
294 
findImage(const SkGlyph & glyph)295 const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
296     if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
297         if (glyph.fImage == NULL) {
298             size_t  size = glyph.computeImageSize();
299             const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
300                                         SkChunkAlloc::kReturnNil_AllocFailType);
301             // check that alloc() actually succeeded
302             if (glyph.fImage) {
303                 fScalerContext->getImage(glyph);
304                 fMemoryUsed += size;
305             }
306         }
307     }
308     return glyph.fImage;
309 }
310 
findPath(const SkGlyph & glyph)311 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
312     if (glyph.fWidth) {
313         if (glyph.fPath == NULL) {
314             const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
315             fScalerContext->getPath(glyph, glyph.fPath);
316             fMemoryUsed += sizeof(SkPath) +
317                     glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint);
318         }
319     }
320     return glyph.fPath;
321 }
322 
323 ///////////////////////////////////////////////////////////////////////////////
324 
getAuxProcData(void (* proc)(void *),void ** dataPtr) const325 bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
326     const AuxProcRec* rec = fAuxProcList;
327     while (rec) {
328         if (rec->fProc == proc) {
329             if (dataPtr) {
330                 *dataPtr = rec->fData;
331             }
332             return true;
333         }
334         rec = rec->fNext;
335     }
336     return false;
337 }
338 
setAuxProc(void (* proc)(void *),void * data)339 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
340     if (proc == NULL) {
341         return;
342     }
343 
344     AuxProcRec* rec = fAuxProcList;
345     while (rec) {
346         if (rec->fProc == proc) {
347             rec->fData = data;
348             return;
349         }
350         rec = rec->fNext;
351     }
352     // not found, create a new rec
353     rec = SkNEW(AuxProcRec);
354     rec->fProc = proc;
355     rec->fData = data;
356     rec->fNext = fAuxProcList;
357     fAuxProcList = rec;
358 }
359 
removeAuxProc(void (* proc)(void *))360 void SkGlyphCache::removeAuxProc(void (*proc)(void*)) {
361     AuxProcRec* rec = fAuxProcList;
362     AuxProcRec* prev = NULL;
363     while (rec) {
364         AuxProcRec* next = rec->fNext;
365         if (rec->fProc == proc) {
366             if (prev) {
367                 prev->fNext = next;
368             } else {
369                 fAuxProcList = next;
370             }
371             SkDELETE(rec);
372             return;
373         }
374         prev = rec;
375         rec = next;
376     }
377 }
378 
invokeAndRemoveAuxProcs()379 void SkGlyphCache::invokeAndRemoveAuxProcs() {
380     AuxProcRec* rec = fAuxProcList;
381     while (rec) {
382         rec->fProc(rec->fData);
383         AuxProcRec* next = rec->fNext;
384         SkDELETE(rec);
385         rec = next;
386     }
387 }
388 
389 ///////////////////////////////////////////////////////////////////////////////
390 ///////////////////////////////////////////////////////////////////////////////
391 
392 #include "SkGlobals.h"
393 #include "SkThread.h"
394 
395 #define SkGlyphCache_GlobalsTag     SkSetFourByteTag('g', 'l', 'f', 'c')
396 
397 #ifdef USE_CACHE_HASH
398     #define HASH_BITCOUNT   6
399     #define HASH_COUNT      (1 << HASH_BITCOUNT)
400     #define HASH_MASK       (HASH_COUNT - 1)
401 
desc_to_hashindex(const SkDescriptor * desc)402     static unsigned desc_to_hashindex(const SkDescriptor* desc)
403     {
404         SkASSERT(HASH_MASK < 256);  // since our munging reduces to 8 bits
405 
406         uint32_t n = *(const uint32_t*)desc;    //desc->getChecksum();
407         SkASSERT(n == desc->getChecksum());
408 
409         // don't trust that the low bits of checksum vary enough, so...
410         n ^= (n >> 24) ^ (n >> 16) ^ (n >> 8) ^ (n >> 30);
411 
412         return n & HASH_MASK;
413     }
414 #endif
415 
416 class SkGlyphCache_Globals : public SkGlobals::Rec {
417 public:
418     SkMutex         fMutex;
419     SkGlyphCache*   fHead;
420     size_t          fTotalMemoryUsed;
421 #ifdef USE_CACHE_HASH
422     SkGlyphCache*   fHash[HASH_COUNT];
423 #endif
424 
425 #ifdef SK_DEBUG
426     void validate() const;
427 #else
validate() const428     void validate() const {}
429 #endif
430 };
431 
432 #ifdef SK_USE_RUNTIME_GLOBALS
create_globals()433     static SkGlobals::Rec* create_globals() {
434         SkGlyphCache_Globals* rec = SkNEW(SkGlyphCache_Globals);
435         rec->fHead = NULL;
436         rec->fTotalMemoryUsed = 0;
437 #ifdef USE_CACHE_HASH
438         memset(rec->fHash, 0, sizeof(rec->fHash));
439 #endif
440         return rec;
441     }
442 
443     #define FIND_GC_GLOBALS()   *(SkGlyphCache_Globals*)SkGlobals::Find(SkGlyphCache_GlobalsTag, create_globals)
444     #define GET_GC_GLOBALS()    *(SkGlyphCache_Globals*)SkGlobals::Get(SkGlyphCache_GlobalsTag)
445 #else
446     static SkGlyphCache_Globals gGCGlobals;
447     #define FIND_GC_GLOBALS()   gGCGlobals
448     #define GET_GC_GLOBALS()    gGCGlobals
449 #endif
450 
VisitAllCaches(bool (* proc)(SkGlyphCache *,void *),void * context)451 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
452                                   void* context) {
453     SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
454     SkAutoMutexAcquire    ac(globals.fMutex);
455     SkGlyphCache*         cache;
456 
457     globals.validate();
458 
459     for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
460         if (proc(cache, context)) {
461             break;
462         }
463     }
464 
465     globals.validate();
466 }
467 
468 /*  This guy calls the visitor from within the mutext lock, so the visitor
469     cannot:
470     - take too much time
471     - try to acquire the mutext again
472     - call a fontscaler (which might call into the cache)
473 */
VisitCache(const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)474 SkGlyphCache* SkGlyphCache::VisitCache(const SkDescriptor* desc,
475                               bool (*proc)(const SkGlyphCache*, void*),
476                               void* context) {
477     SkASSERT(desc);
478 
479     SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
480     SkAutoMutexAcquire    ac(globals.fMutex);
481     SkGlyphCache*         cache;
482     bool                  insideMutex = true;
483 
484     globals.validate();
485 
486 #ifdef USE_CACHE_HASH
487     SkGlyphCache** hash = globals.fHash;
488     unsigned index = desc_to_hashindex(desc);
489     cache = hash[index];
490     if (cache && *cache->fDesc == *desc) {
491         cache->detach(&globals.fHead);
492         goto FOUND_IT;
493     }
494 #endif
495 
496     for (cache = globals.fHead; cache != NULL; cache = cache->fNext) {
497         if (cache->fDesc->equals(*desc)) {
498             cache->detach(&globals.fHead);
499             goto FOUND_IT;
500         }
501     }
502 
503     /* Release the mutex now, before we create a new entry (which might have
504         side-effects like trying to access the cache/mutex (yikes!)
505     */
506     ac.release();           // release the mutex now
507     insideMutex = false;    // can't use globals anymore
508 
509     cache = SkNEW_ARGS(SkGlyphCache, (desc));
510 
511 FOUND_IT:
512 
513     AutoValidate av(cache);
514 
515     if (proc(cache, context)) {   // stay detached
516         if (insideMutex) {
517             SkASSERT(globals.fTotalMemoryUsed >= cache->fMemoryUsed);
518             globals.fTotalMemoryUsed -= cache->fMemoryUsed;
519 #ifdef USE_CACHE_HASH
520             hash[index] = NULL;
521 #endif
522         }
523     } else {                        // reattach
524         if (insideMutex) {
525             cache->attachToHead(&globals.fHead);
526 #ifdef USE_CACHE_HASH
527             hash[index] = cache;
528 #endif
529         } else {
530             AttachCache(cache);
531         }
532         cache = NULL;
533     }
534     return cache;
535 }
536 
AttachCache(SkGlyphCache * cache)537 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
538     SkASSERT(cache);
539     SkASSERT(cache->fNext == NULL);
540 
541     SkGlyphCache_Globals& globals = GET_GC_GLOBALS();
542     SkAutoMutexAcquire    ac(globals.fMutex);
543 
544     globals.validate();
545     cache->validate();
546 
547     // if we have a fixed budget for our cache, do a purge here
548     {
549         size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
550         size_t amountToFree = SkFontHost::ShouldPurgeFontCache(allocated);
551         if (amountToFree)
552             (void)InternalFreeCache(&globals, amountToFree);
553     }
554 
555     cache->attachToHead(&globals.fHead);
556     globals.fTotalMemoryUsed += cache->fMemoryUsed;
557 
558 #ifdef USE_CACHE_HASH
559     unsigned index = desc_to_hashindex(cache->fDesc);
560     SkASSERT(globals.fHash[index] != cache);
561     globals.fHash[index] = cache;
562 #endif
563 
564     globals.validate();
565 }
566 
GetCacheUsed()567 size_t SkGlyphCache::GetCacheUsed() {
568     SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
569     SkAutoMutexAcquire  ac(globals.fMutex);
570 
571     return SkGlyphCache::ComputeMemoryUsed(globals.fHead);
572 }
573 
SetCacheUsed(size_t bytesUsed)574 bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) {
575     size_t curr = SkGlyphCache::GetCacheUsed();
576 
577     if (curr > bytesUsed) {
578         SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
579         SkAutoMutexAcquire  ac(globals.fMutex);
580 
581         return InternalFreeCache(&globals, curr - bytesUsed) > 0;
582     }
583     return false;
584 }
585 
586 ///////////////////////////////////////////////////////////////////////////////
587 
FindTail(SkGlyphCache * cache)588 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
589     if (cache) {
590         while (cache->fNext) {
591             cache = cache->fNext;
592         }
593     }
594     return cache;
595 }
596 
ComputeMemoryUsed(const SkGlyphCache * head)597 size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) {
598     size_t size = 0;
599 
600     while (head != NULL) {
601         size += head->fMemoryUsed;
602         head = head->fNext;
603     }
604     return size;
605 }
606 
607 #ifdef SK_DEBUG
validate() const608 void SkGlyphCache_Globals::validate() const {
609     size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead);
610     if (fTotalMemoryUsed != computed) {
611         printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
612     }
613     SkASSERT(fTotalMemoryUsed == computed);
614 }
615 #endif
616 
InternalFreeCache(SkGlyphCache_Globals * globals,size_t bytesNeeded)617 size_t SkGlyphCache::InternalFreeCache(SkGlyphCache_Globals* globals,
618                                        size_t bytesNeeded) {
619     globals->validate();
620 
621     size_t  bytesFreed = 0;
622     int     count = 0;
623 
624     // don't do any "small" purges
625     size_t minToPurge = globals->fTotalMemoryUsed >> 2;
626     if (bytesNeeded < minToPurge)
627         bytesNeeded = minToPurge;
628 
629     SkGlyphCache* cache = FindTail(globals->fHead);
630     while (cache != NULL && bytesFreed < bytesNeeded) {
631         SkGlyphCache* prev = cache->fPrev;
632         bytesFreed += cache->fMemoryUsed;
633 
634 #ifdef USE_CACHE_HASH
635         unsigned index = desc_to_hashindex(cache->fDesc);
636         if (cache == globals->fHash[index]) {
637             globals->fHash[index] = NULL;
638         }
639 #endif
640 
641         cache->detach(&globals->fHead);
642         SkDELETE(cache);
643         cache = prev;
644         count += 1;
645     }
646 
647     SkASSERT(bytesFreed <= globals->fTotalMemoryUsed);
648     globals->fTotalMemoryUsed -= bytesFreed;
649     globals->validate();
650 
651 #ifdef SPEW_PURGE_STATUS
652     if (count) {
653         SkDebugf("purging %dK from font cache [%d entries]\n",
654                  (int)(bytesFreed >> 10), count);
655     }
656 #endif
657 
658     return bytesFreed;
659 }
660 
661 ///////////////////////////////////////////////////////////////////////////////
662 #ifdef SK_DEBUG
663 
validate() const664 void SkGlyphCache::validate() const {
665     int count = fGlyphArray.count();
666     for (int i = 0; i < count; i++) {
667         const SkGlyph* glyph = fGlyphArray[i];
668         SkASSERT(glyph);
669         SkASSERT(fGlyphAlloc.contains(glyph));
670         if (glyph->fImage) {
671             SkASSERT(fImageAlloc.contains(glyph->fImage));
672         }
673     }
674 }
675 
676 #endif
677