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