Home | History | Annotate | Download | only in core
      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) size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d",
    395                face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
    396                rec.fTextSize,
    397                matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX],
    398                matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY],
    399                rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast,
    400                fGlyphMap.count());
    401     SkDebugf("%s\n", msg.c_str());
    402 }
    403 
    404 ///////////////////////////////////////////////////////////////////////////////
    405 ///////////////////////////////////////////////////////////////////////////////
    406 
    407 size_t SkGlyphCache_Globals::getTotalMemoryUsed() const {
    408     SkAutoExclusive ac(fLock);
    409     return fTotalMemoryUsed;
    410 }
    411 
    412 int SkGlyphCache_Globals::getCacheCountUsed() const {
    413     SkAutoExclusive ac(fLock);
    414     return fCacheCount;
    415 }
    416 
    417 int SkGlyphCache_Globals::getCacheCountLimit() const {
    418     SkAutoExclusive ac(fLock);
    419     return fCacheCountLimit;
    420 }
    421 
    422 size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
    423     static const size_t minLimit = 256 * 1024;
    424     if (newLimit < minLimit) {
    425         newLimit = minLimit;
    426     }
    427 
    428     SkAutoExclusive ac(fLock);
    429 
    430     size_t prevLimit = fCacheSizeLimit;
    431     fCacheSizeLimit = newLimit;
    432     this->internalPurge();
    433     return prevLimit;
    434 }
    435 
    436 size_t  SkGlyphCache_Globals::getCacheSizeLimit() const {
    437     SkAutoExclusive ac(fLock);
    438     return fCacheSizeLimit;
    439 }
    440 
    441 int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
    442     if (newCount < 0) {
    443         newCount = 0;
    444     }
    445 
    446     SkAutoExclusive ac(fLock);
    447 
    448     int prevCount = fCacheCountLimit;
    449     fCacheCountLimit = newCount;
    450     this->internalPurge();
    451     return prevCount;
    452 }
    453 
    454 int SkGlyphCache_Globals::getCachePointSizeLimit() const {
    455     SkAutoExclusive ac(fLock);
    456     return fPointSizeLimit;
    457 }
    458 
    459 int SkGlyphCache_Globals::setCachePointSizeLimit(int newLimit) {
    460     if (newLimit < 0) {
    461         newLimit = 0;
    462     }
    463 
    464     SkAutoExclusive ac(fLock);
    465 
    466     int prevLimit = fPointSizeLimit;
    467     fPointSizeLimit = newLimit;
    468     return prevLimit;
    469 }
    470 
    471 void SkGlyphCache_Globals::purgeAll() {
    472     SkAutoExclusive ac(fLock);
    473     this->internalPurge(fTotalMemoryUsed);
    474 }
    475 
    476 /*  This guy calls the visitor from within the mutext lock, so the visitor
    477     cannot:
    478     - take too much time
    479     - try to acquire the mutext again
    480     - call a fontscaler (which might call into the cache)
    481 */
    482 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
    483                                        const SkScalerContextEffects& effects,
    484                                        const SkDescriptor* desc,
    485                                        bool (*proc)(const SkGlyphCache*, void*),
    486                                        void* context) {
    487     if (!typeface) {
    488         typeface = SkTypeface::GetDefaultTypeface();
    489     }
    490     SkASSERT(desc);
    491 
    492     // Precondition: the typeface id must be the fFontID in the descriptor
    493     SkDEBUGCODE(
    494         uint32_t length = 0;
    495         const SkScalerContextRec* rec = static_cast<const SkScalerContextRec*>(
    496             desc->findEntry(kRec_SkDescriptorTag, &length));
    497         SkASSERT(rec);
    498         SkASSERT(length == sizeof(*rec));
    499         SkASSERT(typeface->uniqueID() == rec->fFontID);
    500     )
    501 
    502     SkGlyphCache_Globals& globals = get_globals();
    503     SkGlyphCache*         cache;
    504 
    505     {
    506         SkAutoExclusive ac(globals.fLock);
    507 
    508         globals.validate();
    509 
    510         for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
    511             if (*cache->fDesc == *desc) {
    512                 globals.internalDetachCache(cache);
    513                 if (!proc(cache, context)) {
    514                     globals.internalAttachCacheToHead(cache);
    515                     cache = nullptr;
    516                 }
    517                 return cache;
    518             }
    519         }
    520     }
    521 
    522     // Check if we can create a scaler-context before creating the glyphcache.
    523     // If not, we may have exhausted OS/font resources, so try purging the
    524     // cache once and try again.
    525     {
    526         // pass true the first time, to notice if the scalercontext failed,
    527         // so we can try the purge.
    528         std::unique_ptr<SkScalerContext> ctx = typeface->createScalerContext(effects, desc, true);
    529         if (!ctx) {
    530             get_globals().purgeAll();
    531             ctx = typeface->createScalerContext(effects, desc, false);
    532             SkASSERT(ctx);
    533         }
    534         cache = new SkGlyphCache(desc, std::move(ctx));
    535     }
    536 
    537     AutoValidate av(cache);
    538 
    539     if (!proc(cache, context)) {   // need to reattach
    540         globals.attachCacheToHead(cache);
    541         cache = nullptr;
    542     }
    543     return cache;
    544 }
    545 
    546 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
    547     SkASSERT(cache);
    548     SkASSERT(cache->fNext == nullptr);
    549 
    550     get_globals().attachCacheToHead(cache);
    551 }
    552 
    553 static void dump_visitor(const SkGlyphCache& cache, void* context) {
    554     int* counter = (int*)context;
    555     int index = *counter;
    556     *counter += 1;
    557 
    558     const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
    559 
    560     SkDebugf("[%3d] ID %3d, glyphs %3d, size %g, scale %g, skew %g, [%g %g %g %g]\n",
    561              index, rec.fFontID, cache.countCachedGlyphs(),
    562              rec.fTextSize, rec.fPreScaleX, rec.fPreSkewX,
    563              rec.fPost2x2[0][0], rec.fPost2x2[0][1], rec.fPost2x2[1][0], rec.fPost2x2[1][1]);
    564 }
    565 
    566 void SkGlyphCache::Dump() {
    567     SkDebugf("GlyphCache [     used    budget ]\n");
    568     SkDebugf("    bytes  [ %8zu  %8zu ]\n",
    569              SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
    570     SkDebugf("    count  [ %8zu  %8zu ]\n",
    571              SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
    572 
    573     int counter = 0;
    574     SkGlyphCache::VisitAll(dump_visitor, &counter);
    575 }
    576 
    577 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) {
    578     SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
    579 
    580     const SkTypeface* face = cache.getScalerContext()->getTypeface();
    581     const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
    582 
    583     SkString fontName;
    584     face->getFamilyName(&fontName);
    585     // Replace all special characters with '_'.
    586     for (size_t index = 0; index < fontName.size(); ++index) {
    587         if (!std::isalnum(fontName[index])) {
    588             fontName[index] = '_';
    589         }
    590     }
    591 
    592     SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
    593                                        gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
    594 
    595     dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed());
    596     dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs());
    597     dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
    598 }
    599 
    600 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
    601     dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
    602     dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
    603                            SkGraphics::GetFontCacheLimit());
    604     dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
    605                            SkGraphics::GetFontCacheCountUsed());
    606     dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
    607                            SkGraphics::GetFontCacheCountLimit());
    608 
    609     if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
    610         dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
    611         return;
    612     }
    613 
    614     SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump);
    615 }
    616 
    617 void SkGlyphCache::VisitAll(Visitor visitor, void* context) {
    618     SkGlyphCache_Globals& globals = get_globals();
    619     SkAutoExclusive ac(globals.fLock);
    620     SkGlyphCache*         cache;
    621 
    622     globals.validate();
    623 
    624     for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
    625         visitor(*cache, context);
    626     }
    627 }
    628 
    629 ///////////////////////////////////////////////////////////////////////////////
    630 
    631 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
    632     SkAutoExclusive ac(fLock);
    633 
    634     this->validate();
    635     cache->validate();
    636 
    637     this->internalAttachCacheToHead(cache);
    638     this->internalPurge();
    639 }
    640 
    641 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
    642     SkGlyphCache* cache = fHead;
    643     if (cache) {
    644         while (cache->fNext) {
    645             cache = cache->fNext;
    646         }
    647     }
    648     return cache;
    649 }
    650 
    651 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
    652     this->validate();
    653 
    654     size_t bytesNeeded = 0;
    655     if (fTotalMemoryUsed > fCacheSizeLimit) {
    656         bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    657     }
    658     bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
    659     if (bytesNeeded) {
    660         // no small purges!
    661         bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
    662     }
    663 
    664     int countNeeded = 0;
    665     if (fCacheCount > fCacheCountLimit) {
    666         countNeeded = fCacheCount - fCacheCountLimit;
    667         // no small purges!
    668         countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
    669     }
    670 
    671     // early exit
    672     if (!countNeeded && !bytesNeeded) {
    673         return 0;
    674     }
    675 
    676     size_t  bytesFreed = 0;
    677     int     countFreed = 0;
    678 
    679     // we start at the tail and proceed backwards, as the linklist is in LRU
    680     // order, with unimportant entries at the tail.
    681     SkGlyphCache* cache = this->internalGetTail();
    682     while (cache != nullptr &&
    683            (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
    684         SkGlyphCache* prev = cache->fPrev;
    685         bytesFreed += cache->fMemoryUsed;
    686         countFreed += 1;
    687 
    688         this->internalDetachCache(cache);
    689         delete cache;
    690         cache = prev;
    691     }
    692 
    693     this->validate();
    694 
    695 #ifdef SPEW_PURGE_STATUS
    696     if (countFreed) {
    697         SkDebugf("purging %dK from font cache [%d entries]\n",
    698                  (int)(bytesFreed >> 10), countFreed);
    699     }
    700 #endif
    701 
    702     return bytesFreed;
    703 }
    704 
    705 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
    706     SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext);
    707     if (fHead) {
    708         fHead->fPrev = cache;
    709         cache->fNext = fHead;
    710     }
    711     fHead = cache;
    712 
    713     fCacheCount += 1;
    714     fTotalMemoryUsed += cache->fMemoryUsed;
    715 }
    716 
    717 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
    718     SkASSERT(fCacheCount > 0);
    719     fCacheCount -= 1;
    720     fTotalMemoryUsed -= cache->fMemoryUsed;
    721 
    722     if (cache->fPrev) {
    723         cache->fPrev->fNext = cache->fNext;
    724     } else {
    725         fHead = cache->fNext;
    726     }
    727     if (cache->fNext) {
    728         cache->fNext->fPrev = cache->fPrev;
    729     }
    730     cache->fPrev = cache->fNext = nullptr;
    731 }
    732 
    733 ///////////////////////////////////////////////////////////////////////////////
    734 
    735 #ifdef SK_DEBUG
    736 
    737 void SkGlyphCache::validate() const {
    738 #ifdef SK_DEBUG_GLYPH_CACHE
    739     int count = fGlyphArray.count();
    740     for (int i = 0; i < count; i++) {
    741         const SkGlyph* glyph = &fGlyphArray[i];
    742         SkASSERT(glyph);
    743         if (glyph->fImage) {
    744             SkASSERT(fGlyphAlloc.contains(glyph->fImage));
    745         }
    746     }
    747 #endif
    748 }
    749 
    750 void SkGlyphCache_Globals::validate() const {
    751     size_t computedBytes = 0;
    752     int computedCount = 0;
    753 
    754     const SkGlyphCache* head = fHead;
    755     while (head != nullptr) {
    756         computedBytes += head->fMemoryUsed;
    757         computedCount += 1;
    758         head = head->fNext;
    759     }
    760 
    761     SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount,
    762               computedCount);
    763     SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d",
    764               fTotalMemoryUsed, computedBytes);
    765 }
    766 
    767 #endif
    768 
    769 ///////////////////////////////////////////////////////////////////////////////
    770 ///////////////////////////////////////////////////////////////////////////////
    771 
    772 #include "SkTypefaceCache.h"
    773 
    774 size_t SkGraphics::GetFontCacheLimit() {
    775     return get_globals().getCacheSizeLimit();
    776 }
    777 
    778 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
    779     return get_globals().setCacheSizeLimit(bytes);
    780 }
    781 
    782 size_t SkGraphics::GetFontCacheUsed() {
    783     return get_globals().getTotalMemoryUsed();
    784 }
    785 
    786 int SkGraphics::GetFontCacheCountLimit() {
    787     return get_globals().getCacheCountLimit();
    788 }
    789 
    790 int SkGraphics::SetFontCacheCountLimit(int count) {
    791     return get_globals().setCacheCountLimit(count);
    792 }
    793 
    794 int SkGraphics::GetFontCacheCountUsed() {
    795     return get_globals().getCacheCountUsed();
    796 }
    797 
    798 int SkGraphics::GetFontCachePointSizeLimit() {
    799     return get_globals().getCachePointSizeLimit();
    800 }
    801 
    802 int SkGraphics::SetFontCachePointSizeLimit(int limit) {
    803     return get_globals().setCachePointSizeLimit(limit);
    804 }
    805 
    806 void SkGraphics::PurgeFontCache() {
    807     get_globals().purgeAll();
    808     SkTypefaceCache::PurgeAll();
    809 }
    810 
    811 // TODO(herb): clean up TLS apis.
    812 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; }
    813 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { }
    814 
    815 SkGlyphCache* SkGlyphCache::DetachCacheUsingPaint(const SkPaint& paint,
    816                                                   const SkSurfaceProps* surfaceProps,
    817                                                   SkScalerContextFlags scalerContextFlags,
    818                                                   const SkMatrix* deviceMatrix) {
    819     SkAutoDescriptor ad;
    820     SkScalerContextEffects effects;
    821 
    822     auto desc = SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
    823         paint, surfaceProps, scalerContextFlags, deviceMatrix, &ad, &effects);
    824 
    825     return SkGlyphCache::DetachCache(paint.getTypeface(), effects, desc);
    826 }
    827