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