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