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