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