LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkGlyphCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 125 431 29.0 %
Date: 2017-07-14 16:53:18 Functions: 19 62 30.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13