blob: b97699c2ddd11e8f2b16d585b5fc1261fe381358 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/sgl/SkGlyphCache.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.com22a02212011-03-01 21:33:48 +00005** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.com22a02212011-03-01 21:33:48 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.com22a02212011-03-01 21:33:48 +000011** 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
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** 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;
reed@google.com22a02212011-03-01 21:33:48 +000040
reed@android.com8a1c16f2008-12-17 15:59:43 +000041 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
59SkGlyphCache::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));
reed@google.com22a02212011-03-01 21:33:48 +000071
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 fMemoryUsed = sizeof(*this) + kMinGlphAlloc + kMinImageAlloc;
reed@google.com22a02212011-03-01 21:33:48 +000073
reed@android.com8a1c16f2008-12-17 15:59:43 +000074 fGlyphArray.setReserve(METRICS_RESERVE_COUNT);
75
76 fMetricsCount = 0;
77 fAdvanceCount = 0;
78 fAuxProcList = NULL;
79}
80
81SkGlyphCache::~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
reed@android.comf2b98d62010-12-20 18:26:13 +000099#define VALIDATE() AutoValidate av(this)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100#else
101#define VALIDATE()
102#endif
103
104uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
105 VALIDATE();
106 uint32_t id = SkGlyph::MakeID(charCode);
107 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 if (rec.fID == id) {
110 return rec.fGlyph->getGlyphID();
111 } else {
112 return fScalerContext->charToGlyphID(charCode);
113 }
114}
115
reed@android.com9d3a9852010-01-08 14:07:42 +0000116SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
117 return fScalerContext->glyphIDToChar(glyphID);
118}
119
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000120unsigned SkGlyphCache::getGlyphCount() {
121 return fScalerContext->getGlyphCount();
122}
123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124///////////////////////////////////////////////////////////////////////////////
125
126const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
127 VALIDATE();
128 uint32_t id = SkGlyph::MakeID(charCode);
129 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000130
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 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
141const 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
156const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
157 VALIDATE();
158 uint32_t id = SkGlyph::MakeID(charCode);
159 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 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
178const 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)];
reed@google.com22a02212011-03-01 21:33:48 +0000183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 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
201const 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];
reed@google.com22a02212011-03-01 21:33:48 +0000206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 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
221const 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
242SkGlyph* 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);
reed@android.comf2b98d62010-12-20 18:26:13 +0000280 glyph->init(id);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 *fGlyphArray.insert(hi) = glyph;
reed@google.com22a02212011-03-01 21:33:48 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 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
295const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000296 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 if (glyph.fImage == NULL) {
298 size_t size = glyph.computeImageSize();
299 const_cast<SkGlyph&>(glyph).fImage = fImageAlloc.alloc(size,
300 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000301 // check that alloc() actually succeeded
302 if (glyph.fImage) {
303 fScalerContext->getImage(glyph);
304 fMemoryUsed += size;
305 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 }
307 }
308 return glyph.fImage;
309}
310
311const 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
325bool 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
339void 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
360void 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
379void 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)
reed@google.com22a02212011-03-01 21:33:48 +0000401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 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
416class SkGlyphCache_Globals : public SkGlobals::Rec {
417public:
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
428 void validate() const {}
429#endif
430};
431
432#ifdef SK_USE_RUNTIME_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
451void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
452 void* context) {
453 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
454 SkAutoMutexAcquire ac(globals.fMutex);
455 SkGlyphCache* cache;
reed@google.com22a02212011-03-01 21:33:48 +0000456
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 globals.validate();
reed@google.com22a02212011-03-01 21:33:48 +0000458
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 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*/
474SkGlyphCache* 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
511FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000512
513 AutoValidate av(cache);
514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 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
537void 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();
reed@android.comf2b98d62010-12-20 18:26:13 +0000545 cache->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546
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
567size_t SkGlyphCache::GetCacheUsed() {
568 SkGlyphCache_Globals& globals = FIND_GC_GLOBALS();
569 SkAutoMutexAcquire ac(globals.fMutex);
reed@google.com22a02212011-03-01 21:33:48 +0000570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 return SkGlyphCache::ComputeMemoryUsed(globals.fHead);
572}
573
574bool 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);
reed@google.com22a02212011-03-01 21:33:48 +0000580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581 return InternalFreeCache(&globals, curr - bytesUsed) > 0;
582 }
583 return false;
584}
585
586///////////////////////////////////////////////////////////////////////////////
587
588SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
589 if (cache) {
590 while (cache->fNext) {
591 cache = cache->fNext;
592 }
593 }
594 return cache;
595}
596
597size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) {
598 size_t size = 0;
reed@google.com22a02212011-03-01 21:33:48 +0000599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 while (head != NULL) {
601 size += head->fMemoryUsed;
602 head = head->fNext;
603 }
604 return size;
605}
606
607#ifdef SK_DEBUG
608void 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
617size_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
reed@android.comf2b98d62010-12-20 18:26:13 +0000661///////////////////////////////////////////////////////////////////////////////
662#ifdef SK_DEBUG
663
664void 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