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 9 #include "SkGlyphCache.h" 10 #include "SkGlyphCache_Globals.h" 11 #include "SkGraphics.h" 12 #include "SkOnce.h" 13 #include "SkPath.h" 14 #include "SkTemplates.h" 15 #include "SkTraceMemoryDump.h" 16 #include "SkTypeface.h" 17 18 #include <cctype> 19 20 //#define SPEW_PURGE_STATUS 21 22 namespace { 23 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache"; 24 } // namespace 25 26 // Returns the shared globals 27 static SkGlyphCache_Globals& get_globals() { 28 static SkOnce once; 29 static SkGlyphCache_Globals* globals; 30 31 once([]{ globals = new SkGlyphCache_Globals; }); 32 return *globals; 33 } 34 35 /////////////////////////////////////////////////////////////////////////////// 36 37 SkGlyphCache::SkGlyphCache(const SkDescriptor* desc, std::unique_ptr<SkScalerContext> ctx) 38 : fDesc(desc->copy()) 39 , fScalerContext(std::move(ctx)) { 40 SkASSERT(desc); 41 SkASSERT(fScalerContext); 42 43 fPrev = fNext = nullptr; 44 45 fScalerContext->getFontMetrics(&fFontMetrics); 46 47 fMemoryUsed = sizeof(*this); 48 } 49 50 SkGlyphCache::~SkGlyphCache() { 51 fGlyphMap.foreach([](SkGlyph* g) { 52 if (g->fPathData) { 53 delete g->fPathData->fPath; 54 } 55 }); 56 } 57 58 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(SkPackedUnicharID packedUnicharID) { 59 if (!fPackedUnicharIDToPackedGlyphID) { 60 fPackedUnicharIDToPackedGlyphID.reset(new CharGlyphRec[kHashCount]); 61 } 62 63 return &fPackedUnicharIDToPackedGlyphID[packedUnicharID.hash() & kHashMask]; 64 } 65 66 /////////////////////////////////////////////////////////////////////////////// 67 68 #ifdef SK_DEBUG 69 #define VALIDATE() AutoValidate av(this) 70 #else 71 #define VALIDATE() 72 #endif 73 74 SkGlyphID SkGlyphCache::unicharToGlyph(SkUnichar charCode) { 75 VALIDATE(); 76 SkPackedUnicharID packedUnicharID(charCode); 77 CharGlyphRec* rec = this->getCharGlyphRec(packedUnicharID); 78 79 if (rec->fPackedUnicharID == packedUnicharID) { 80 // The glyph exists in the unichar to glyph mapping cache. Return it. 81 return rec->fPackedGlyphID.code(); 82 } else { 83 // The glyph is not in the unichar to glyph mapping cache. Insert it. 84 rec->fPackedUnicharID = packedUnicharID; 85 SkGlyphID glyphID = fScalerContext->charToGlyphID(charCode); 86 rec->fPackedGlyphID = SkPackedGlyphID(glyphID); 87 return glyphID; 88 } 89 } 90 91 SkUnichar SkGlyphCache::glyphToUnichar(SkGlyphID glyphID) { 92 return fScalerContext->glyphIDToChar(glyphID); 93 } 94 95 unsigned SkGlyphCache::getGlyphCount() const { 96 return fScalerContext->getGlyphCount(); 97 } 98 99 int SkGlyphCache::countCachedGlyphs() const { 100 return fGlyphMap.count(); 101 } 102 103 /////////////////////////////////////////////////////////////////////////////// 104 105 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) { 106 VALIDATE(); 107 return *this->lookupByChar(charCode, kJustAdvance_MetricsType); 108 } 109 110 const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) { 111 VALIDATE(); 112 SkPackedGlyphID packedGlyphID(glyphID); 113 return *this->lookupByPackedGlyphID(packedGlyphID, kJustAdvance_MetricsType); 114 } 115 116 /////////////////////////////////////////////////////////////////////////////// 117 118 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) { 119 VALIDATE(); 120 return *this->lookupByChar(charCode, kFull_MetricsType); 121 } 122 123 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, SkFixed x, SkFixed y) { 124 VALIDATE(); 125 return *this->lookupByChar(charCode, kFull_MetricsType, x, y); 126 } 127 128 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) { 129 VALIDATE(); 130 SkPackedGlyphID packedGlyphID(glyphID); 131 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType); 132 } 133 134 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) { 135 VALIDATE(); 136 SkPackedGlyphID packedGlyphID(glyphID, x, y); 137 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType); 138 } 139 140 SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) { 141 SkPackedUnicharID id(charCode, x, y); 142 CharGlyphRec* rec = this->getCharGlyphRec(id); 143 if (rec->fPackedUnicharID != id) { 144 rec->fPackedUnicharID = id; 145 rec->fPackedGlyphID = SkPackedGlyphID(fScalerContext->charToGlyphID(charCode), x, y); 146 } 147 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type); 148 } 149 150 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(SkPackedGlyphID packedGlyphID, MetricsType type) { 151 SkGlyph* glyph = fGlyphMap.find(packedGlyphID); 152 153 if (nullptr == glyph) { 154 glyph = this->allocateNewGlyph(packedGlyphID, type); 155 } else { 156 if (type == kFull_MetricsType && glyph->isJustAdvance()) { 157 fScalerContext->getMetrics(glyph); 158 } 159 } 160 return glyph; 161 } 162 163 SkGlyph* SkGlyphCache::allocateNewGlyph(SkPackedGlyphID packedGlyphID, MetricsType mtype) { 164 fMemoryUsed += sizeof(SkGlyph); 165 166 SkGlyph* glyphPtr; 167 { 168 SkGlyph glyph; 169 glyph.initWithGlyphID(packedGlyphID); 170 glyphPtr = fGlyphMap.set(glyph); 171 } 172 173 if (kJustAdvance_MetricsType == mtype) { 174 fScalerContext->getAdvance(glyphPtr); 175 } else { 176 SkASSERT(kFull_MetricsType == mtype); 177 fScalerContext->getMetrics(glyphPtr); 178 } 179 180 SkASSERT(glyphPtr->fID != SkPackedGlyphID()); 181 return glyphPtr; 182 } 183 184 const void* SkGlyphCache::findImage(const SkGlyph& glyph) { 185 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { 186 if (nullptr == glyph.fImage) { 187 size_t size = const_cast<SkGlyph&>(glyph).allocImage(&fAlloc); 188 // check that alloc() actually succeeded 189 if (glyph.fImage) { 190 fScalerContext->getImage(glyph); 191 // TODO: the scaler may have changed the maskformat during 192 // getImage (e.g. from AA or LCD to BW) which means we may have 193 // overallocated the buffer. Check if the new computedImageSize 194 // is smaller, and if so, strink the alloc size in fImageAlloc. 195 fMemoryUsed += size; 196 } 197 } 198 } 199 return glyph.fImage; 200 } 201 202 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { 203 if (glyph.fWidth) { 204 if (glyph.fPathData == nullptr) { 205 SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>(); 206 const_cast<SkGlyph&>(glyph).fPathData = pathData; 207 pathData->fIntercept = nullptr; 208 SkPath* path = pathData->fPath = new SkPath; 209 fScalerContext->getPath(glyph.getPackedID(), path); 210 fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint); 211 } 212 } 213 return glyph.fPathData ? glyph.fPathData->fPath : nullptr; 214 } 215 216 #include "../pathops/SkPathOpsCubic.h" 217 #include "../pathops/SkPathOpsQuad.h" 218 219 static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { 220 SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]); 221 if (bounds[1] < min) { 222 return false; 223 } 224 SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]); 225 return bounds[0] < max; 226 } 227 228 static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { 229 SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]); 230 if (bounds[1] < min) { 231 return false; 232 } 233 SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]); 234 return bounds[0] < max; 235 } 236 237 void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, 238 SkScalar xPos, SkScalar* array, int* count) { 239 if (array) { 240 array += *count; 241 for (int index = 0; index < 2; index++) { 242 *array++ = intercept->fInterval[index] * scale + xPos; 243 } 244 } 245 *count += 2; 246 } 247 248 void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) { 249 intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val); 250 intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val); 251 } 252 253 void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], 254 bool yAxis, SkGlyph::Intercept* intercept) { 255 for (int i = 0; i < ptCount; ++i) { 256 SkScalar val = *(&pts[i].fY - yAxis); 257 if (bounds[0] < val && val < bounds[1]) { 258 AddInterval(*(&pts[i].fX + yAxis), intercept); 259 } 260 } 261 } 262 263 void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, 264 SkGlyph::Intercept* intercept) { 265 SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX) 266 : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY); 267 if (0 <= t && t < 1) { // this handles divide by zero above 268 AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY) 269 : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept); 270 } 271 } 272 273 void SkGlyphCache::AddQuad(const SkPoint pts[3], SkScalar axis, bool yAxis, 274 SkGlyph::Intercept* intercept) { 275 SkDQuad quad; 276 quad.set(pts); 277 double roots[2]; 278 int count = yAxis ? quad.verticalIntersect(axis, roots) 279 : quad.horizontalIntersect(axis, roots); 280 while (--count >= 0) { 281 SkPoint pt = quad.ptAtT(roots[count]).asSkPoint(); 282 AddInterval(*(&pt.fX + yAxis), intercept); 283 } 284 } 285 286 void SkGlyphCache::AddCubic(const SkPoint pts[4], SkScalar axis, bool yAxis, 287 SkGlyph::Intercept* intercept) { 288 SkDCubic cubic; 289 cubic.set(pts); 290 double roots[3]; 291 int count = yAxis ? cubic.verticalIntersect(axis, roots) 292 : cubic.horizontalIntersect(axis, roots); 293 while (--count >= 0) { 294 SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint(); 295 AddInterval(*(&pt.fX + yAxis), intercept); 296 } 297 } 298 299 const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph, 300 const SkScalar bounds[2]) { 301 if (!glyph->fPathData) { 302 return nullptr; 303 } 304 const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept; 305 while (intercept) { 306 if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) { 307 return intercept; 308 } 309 intercept = intercept->fNext; 310 } 311 return nullptr; 312 } 313 314 void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, 315 bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) { 316 const SkGlyph::Intercept* match = MatchBounds(glyph, bounds); 317 318 if (match) { 319 if (match->fInterval[0] < match->fInterval[1]) { 320 OffsetResults(match, scale, xPos, array, count); 321 } 322 return; 323 } 324 325 SkGlyph::Intercept* intercept = fAlloc.make<SkGlyph::Intercept>(); 326 intercept->fNext = glyph->fPathData->fIntercept; 327 intercept->fBounds[0] = bounds[0]; 328 intercept->fBounds[1] = bounds[1]; 329 intercept->fInterval[0] = SK_ScalarMax; 330 intercept->fInterval[1] = SK_ScalarMin; 331 glyph->fPathData->fIntercept = intercept; 332 const SkPath* path = glyph->fPathData->fPath; 333 const SkRect& pathBounds = path->getBounds(); 334 if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) { 335 return; 336 } 337 SkPath::Iter iter(*path, false); 338 SkPoint pts[4]; 339 SkPath::Verb verb; 340 while (SkPath::kDone_Verb != (verb = iter.next(pts))) { 341 switch (verb) { 342 case SkPath::kMove_Verb: 343 break; 344 case SkPath::kLine_Verb: 345 AddLine(pts, bounds[0], yAxis, intercept); 346 AddLine(pts, bounds[1], yAxis, intercept); 347 AddPoints(pts, 2, bounds, yAxis, intercept); 348 break; 349 case SkPath::kQuad_Verb: 350 if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) { 351 break; 352 } 353 AddQuad(pts, bounds[0], yAxis, intercept); 354 AddQuad(pts, bounds[1], yAxis, intercept); 355 AddPoints(pts, 3, bounds, yAxis, intercept); 356 break; 357 case SkPath::kConic_Verb: 358 SkASSERT(0); // no support for text composed of conics 359 break; 360 case SkPath::kCubic_Verb: 361 if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) { 362 break; 363 } 364 AddCubic(pts, bounds[0], yAxis, intercept); 365 AddCubic(pts, bounds[1], yAxis, intercept); 366 AddPoints(pts, 4, bounds, yAxis, intercept); 367 break; 368 case SkPath::kClose_Verb: 369 break; 370 default: 371 SkASSERT(0); 372 break; 373 } 374 } 375 if (intercept->fInterval[0] >= intercept->fInterval[1]) { 376 intercept->fInterval[0] = SK_ScalarMax; 377 intercept->fInterval[1] = SK_ScalarMin; 378 return; 379 } 380 OffsetResults(intercept, scale, xPos, array, count); 381 } 382 383 void SkGlyphCache::dump() const { 384 const SkTypeface* face = fScalerContext->getTypeface(); 385 const SkScalerContextRec& rec = fScalerContext->getRec(); 386 SkMatrix matrix; 387 rec.getSingleMatrix(&matrix); 388 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); 389 SkString name; 390 face->getFamilyName(&name); 391 392 SkString msg; 393 SkFontStyle style = face->fontStyle(); 394 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d", 395 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(), 396 rec.dump().c_str(), fGlyphMap.count()); 397 SkDebugf("%s\n", msg.c_str()); 398 } 399 400 /////////////////////////////////////////////////////////////////////////////// 401 /////////////////////////////////////////////////////////////////////////////// 402 403 size_t SkGlyphCache_Globals::getTotalMemoryUsed() const { 404 SkAutoExclusive ac(fLock); 405 return fTotalMemoryUsed; 406 } 407 408 int SkGlyphCache_Globals::getCacheCountUsed() const { 409 SkAutoExclusive ac(fLock); 410 return fCacheCount; 411 } 412 413 int SkGlyphCache_Globals::getCacheCountLimit() const { 414 SkAutoExclusive ac(fLock); 415 return fCacheCountLimit; 416 } 417 418 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) { 419 static const size_t minLimit = 256 * 1024; 420 if (newLimit < minLimit) { 421 newLimit = minLimit; 422 } 423 424 SkAutoExclusive ac(fLock); 425 426 size_t prevLimit = fCacheSizeLimit; 427 fCacheSizeLimit = newLimit; 428 this->internalPurge(); 429 return prevLimit; 430 } 431 432 size_t SkGlyphCache_Globals::getCacheSizeLimit() const { 433 SkAutoExclusive ac(fLock); 434 return fCacheSizeLimit; 435 } 436 437 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) { 438 if (newCount < 0) { 439 newCount = 0; 440 } 441 442 SkAutoExclusive ac(fLock); 443 444 int prevCount = fCacheCountLimit; 445 fCacheCountLimit = newCount; 446 this->internalPurge(); 447 return prevCount; 448 } 449 450 int SkGlyphCache_Globals::getCachePointSizeLimit() const { 451 SkAutoExclusive ac(fLock); 452 return fPointSizeLimit; 453 } 454 455 int SkGlyphCache_Globals::setCachePointSizeLimit(int newLimit) { 456 if (newLimit < 0) { 457 newLimit = 0; 458 } 459 460 SkAutoExclusive ac(fLock); 461 462 int prevLimit = fPointSizeLimit; 463 fPointSizeLimit = newLimit; 464 return prevLimit; 465 } 466 467 void SkGlyphCache_Globals::purgeAll() { 468 SkAutoExclusive ac(fLock); 469 this->internalPurge(fTotalMemoryUsed); 470 } 471 472 /* This guy calls the visitor from within the mutext lock, so the visitor 473 cannot: 474 - take too much time 475 - try to acquire the mutext again 476 - call a fontscaler (which might call into the cache) 477 */ 478 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface, 479 const SkScalerContextEffects& effects, 480 const SkDescriptor* desc, 481 bool (*proc)(const SkGlyphCache*, void*), 482 void* context) { 483 if (!typeface) { 484 typeface = SkTypeface::GetDefaultTypeface(); 485 } 486 SkASSERT(desc); 487 488 // Precondition: the typeface id must be the fFontID in the descriptor 489 SkDEBUGCODE( 490 uint32_t length = 0; 491 const SkScalerContextRec* rec = static_cast<const SkScalerContextRec*>( 492 desc->findEntry(kRec_SkDescriptorTag, &length)); 493 SkASSERT(rec); 494 SkASSERT(length == sizeof(*rec)); 495 SkASSERT(typeface->uniqueID() == rec->fFontID); 496 ) 497 498 SkGlyphCache_Globals& globals = get_globals(); 499 SkGlyphCache* cache; 500 501 { 502 SkAutoExclusive ac(globals.fLock); 503 504 globals.validate(); 505 506 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { 507 if (*cache->fDesc == *desc) { 508 globals.internalDetachCache(cache); 509 if (!proc(cache, context)) { 510 globals.internalAttachCacheToHead(cache); 511 cache = nullptr; 512 } 513 return cache; 514 } 515 } 516 } 517 518 // Check if we can create a scaler-context before creating the glyphcache. 519 // If not, we may have exhausted OS/font resources, so try purging the 520 // cache once and try again. 521 { 522 // pass true the first time, to notice if the scalercontext failed, 523 // so we can try the purge. 524 std::unique_ptr<SkScalerContext> ctx = typeface->createScalerContext(effects, desc, true); 525 if (!ctx) { 526 get_globals().purgeAll(); 527 ctx = typeface->createScalerContext(effects, desc, false); 528 SkASSERT(ctx); 529 } 530 cache = new SkGlyphCache(desc, std::move(ctx)); 531 } 532 533 AutoValidate av(cache); 534 535 if (!proc(cache, context)) { // need to reattach 536 globals.attachCacheToHead(cache); 537 cache = nullptr; 538 } 539 return cache; 540 } 541 542 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { 543 SkASSERT(cache); 544 SkASSERT(cache->fNext == nullptr); 545 546 get_globals().attachCacheToHead(cache); 547 } 548 549 static void dump_visitor(const SkGlyphCache& cache, void* context) { 550 int* counter = (int*)context; 551 int index = *counter; 552 *counter += 1; 553 554 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); 555 556 SkDebugf("index %d\n", index); 557 SkDebugf("%s", rec.dump().c_str()); 558 } 559 560 void SkGlyphCache::Dump() { 561 SkDebugf("GlyphCache [ used budget ]\n"); 562 SkDebugf(" bytes [ %8zu %8zu ]\n", 563 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit()); 564 SkDebugf(" count [ %8zu %8zu ]\n", 565 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit()); 566 567 int counter = 0; 568 SkGlyphCache::VisitAll(dump_visitor, &counter); 569 } 570 571 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) { 572 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context); 573 574 const SkTypeface* face = cache.getScalerContext()->getTypeface(); 575 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); 576 577 SkString fontName; 578 face->getFamilyName(&fontName); 579 // Replace all special characters with '_'. 580 for (size_t index = 0; index < fontName.size(); ++index) { 581 if (!std::isalnum(fontName[index])) { 582 fontName[index] = '_'; 583 } 584 } 585 586 SkString dumpName = SkStringPrintf("%s/%s_%d/%p", 587 gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache); 588 589 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed()); 590 dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs()); 591 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr); 592 } 593 594 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) { 595 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed()); 596 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes", 597 SkGraphics::GetFontCacheLimit()); 598 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects", 599 SkGraphics::GetFontCacheCountUsed()); 600 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects", 601 SkGraphics::GetFontCacheCountLimit()); 602 603 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) { 604 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr); 605 return; 606 } 607 608 SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump); 609 } 610 611 void SkGlyphCache::VisitAll(Visitor visitor, void* context) { 612 SkGlyphCache_Globals& globals = get_globals(); 613 SkAutoExclusive ac(globals.fLock); 614 SkGlyphCache* cache; 615 616 globals.validate(); 617 618 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) { 619 visitor(*cache, context); 620 } 621 } 622 623 /////////////////////////////////////////////////////////////////////////////// 624 625 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { 626 SkAutoExclusive ac(fLock); 627 628 this->validate(); 629 cache->validate(); 630 631 this->internalAttachCacheToHead(cache); 632 this->internalPurge(); 633 } 634 635 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const { 636 SkGlyphCache* cache = fHead; 637 if (cache) { 638 while (cache->fNext) { 639 cache = cache->fNext; 640 } 641 } 642 return cache; 643 } 644 645 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) { 646 this->validate(); 647 648 size_t bytesNeeded = 0; 649 if (fTotalMemoryUsed > fCacheSizeLimit) { 650 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit; 651 } 652 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded); 653 if (bytesNeeded) { 654 // no small purges! 655 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2); 656 } 657 658 int countNeeded = 0; 659 if (fCacheCount > fCacheCountLimit) { 660 countNeeded = fCacheCount - fCacheCountLimit; 661 // no small purges! 662 countNeeded = SkMax32(countNeeded, fCacheCount >> 2); 663 } 664 665 // early exit 666 if (!countNeeded && !bytesNeeded) { 667 return 0; 668 } 669 670 size_t bytesFreed = 0; 671 int countFreed = 0; 672 673 // we start at the tail and proceed backwards, as the linklist is in LRU 674 // order, with unimportant entries at the tail. 675 SkGlyphCache* cache = this->internalGetTail(); 676 while (cache != nullptr && 677 (bytesFreed < bytesNeeded || countFreed < countNeeded)) { 678 SkGlyphCache* prev = cache->fPrev; 679 bytesFreed += cache->fMemoryUsed; 680 countFreed += 1; 681 682 this->internalDetachCache(cache); 683 delete cache; 684 cache = prev; 685 } 686 687 this->validate(); 688 689 #ifdef SPEW_PURGE_STATUS 690 if (countFreed) { 691 SkDebugf("purging %dK from font cache [%d entries]\n", 692 (int)(bytesFreed >> 10), countFreed); 693 } 694 #endif 695 696 return bytesFreed; 697 } 698 699 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { 700 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext); 701 if (fHead) { 702 fHead->fPrev = cache; 703 cache->fNext = fHead; 704 } 705 fHead = cache; 706 707 fCacheCount += 1; 708 fTotalMemoryUsed += cache->fMemoryUsed; 709 } 710 711 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { 712 SkASSERT(fCacheCount > 0); 713 fCacheCount -= 1; 714 fTotalMemoryUsed -= cache->fMemoryUsed; 715 716 if (cache->fPrev) { 717 cache->fPrev->fNext = cache->fNext; 718 } else { 719 fHead = cache->fNext; 720 } 721 if (cache->fNext) { 722 cache->fNext->fPrev = cache->fPrev; 723 } 724 cache->fPrev = cache->fNext = nullptr; 725 } 726 727 /////////////////////////////////////////////////////////////////////////////// 728 729 #ifdef SK_DEBUG 730 731 void SkGlyphCache::validate() const { 732 #ifdef SK_DEBUG_GLYPH_CACHE 733 int count = fGlyphArray.count(); 734 for (int i = 0; i < count; i++) { 735 const SkGlyph* glyph = &fGlyphArray[i]; 736 SkASSERT(glyph); 737 if (glyph->fImage) { 738 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); 739 } 740 } 741 #endif 742 } 743 744 void SkGlyphCache_Globals::validate() const { 745 size_t computedBytes = 0; 746 int computedCount = 0; 747 748 const SkGlyphCache* head = fHead; 749 while (head != nullptr) { 750 computedBytes += head->fMemoryUsed; 751 computedCount += 1; 752 head = head->fNext; 753 } 754 755 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount, 756 computedCount); 757 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d", 758 fTotalMemoryUsed, computedBytes); 759 } 760 761 #endif 762 763 /////////////////////////////////////////////////////////////////////////////// 764 /////////////////////////////////////////////////////////////////////////////// 765 766 #include "SkTypefaceCache.h" 767 768 size_t SkGraphics::GetFontCacheLimit() { 769 return get_globals().getCacheSizeLimit(); 770 } 771 772 size_t SkGraphics::SetFontCacheLimit(size_t bytes) { 773 return get_globals().setCacheSizeLimit(bytes); 774 } 775 776 size_t SkGraphics::GetFontCacheUsed() { 777 return get_globals().getTotalMemoryUsed(); 778 } 779 780 int SkGraphics::GetFontCacheCountLimit() { 781 return get_globals().getCacheCountLimit(); 782 } 783 784 int SkGraphics::SetFontCacheCountLimit(int count) { 785 return get_globals().setCacheCountLimit(count); 786 } 787 788 int SkGraphics::GetFontCacheCountUsed() { 789 return get_globals().getCacheCountUsed(); 790 } 791 792 int SkGraphics::GetFontCachePointSizeLimit() { 793 return get_globals().getCachePointSizeLimit(); 794 } 795 796 int SkGraphics::SetFontCachePointSizeLimit(int limit) { 797 return get_globals().setCachePointSizeLimit(limit); 798 } 799 800 void SkGraphics::PurgeFontCache() { 801 get_globals().purgeAll(); 802 SkTypefaceCache::PurgeAll(); 803 } 804 805 // TODO(herb): clean up TLS apis. 806 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; } 807 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { } 808 809 SkGlyphCache* SkGlyphCache::DetachCacheUsingPaint(const SkPaint& paint, 810 const SkSurfaceProps* surfaceProps, 811 SkScalerContextFlags scalerContextFlags, 812 const SkMatrix* deviceMatrix) { 813 SkAutoDescriptor ad; 814 SkScalerContextEffects effects; 815 816 auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint( 817 paint, surfaceProps, scalerContextFlags, deviceMatrix, &ad, &effects); 818 819 return SkGlyphCache::DetachCache(paint.getTypeface(), effects, desc); 820 } 821