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