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