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