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