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