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