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
21 namespace {
22 const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
23 } // namespace
24
25 // Returns the shared globals
26 SK_DECLARE_STATIC_ONCE_PTR(SkGlyphCache_Globals, globals);
get_globals()27 static 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
SkGlyphCache(SkTypeface * typeface,const SkDescriptor * desc,SkScalerContext * ctx)38 SkGlyphCache::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
~SkGlyphCache()55 SkGlyphCache::~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
getCharGlyphRec(PackedUnicharID packedUnicharID)65 SkGlyphCache::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
unicharToGlyph(SkUnichar charCode)88 uint16_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
glyphToUnichar(uint16_t glyphID)100 SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
101 return fScalerContext->glyphIDToChar(glyphID);
102 }
103
getGlyphCount() const104 unsigned SkGlyphCache::getGlyphCount() const {
105 return fScalerContext->getGlyphCount();
106 }
107
countCachedGlyphs() const108 int SkGlyphCache::countCachedGlyphs() const {
109 return fGlyphMap.count();
110 }
111
112 ///////////////////////////////////////////////////////////////////////////////
113
getUnicharAdvance(SkUnichar charCode)114 const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
115 VALIDATE();
116 return *this->lookupByChar(charCode, kJustAdvance_MetricsType);
117 }
118
getGlyphIDAdvance(uint16_t glyphID)119 const 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
getUnicharMetrics(SkUnichar charCode)127 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
128 VALIDATE();
129 return *this->lookupByChar(charCode, kFull_MetricsType);
130 }
131
getUnicharMetrics(SkUnichar charCode,SkFixed x,SkFixed y)132 const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode, SkFixed x, SkFixed y) {
133 VALIDATE();
134 return *this->lookupByChar(charCode, kFull_MetricsType, x, y);
135 }
136
getGlyphIDMetrics(uint16_t glyphID)137 const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
138 VALIDATE();
139 PackedGlyphID packedGlyphID = SkGlyph::MakeID(glyphID);
140 return *this->lookupByPackedGlyphID(packedGlyphID, kFull_MetricsType);
141 }
142
getGlyphIDMetrics(uint16_t glyphID,SkFixed x,SkFixed y)143 const 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
lookupByChar(SkUnichar charCode,MetricsType type,SkFixed x,SkFixed y)149 SkGlyph* 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
lookupByPackedGlyphID(PackedGlyphID packedGlyphID,MetricsType type)164 SkGlyph* 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
allocateNewGlyph(PackedGlyphID packedGlyphID,MetricsType mtype)177 SkGlyph* 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
findImage(const SkGlyph & glyph)198 const 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
findPath(const SkGlyph & glyph)218 const 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
quad_in_bounds(const SkScalar * pts,const SkScalar bounds[2])236 static 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
cubic_in_bounds(const SkScalar * pts,const SkScalar bounds[2])245 static 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
OffsetResults(const SkGlyph::Intercept * intercept,SkScalar scale,SkScalar xPos,SkScalar * array,int * count)254 void 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
AddInterval(SkScalar val,SkGlyph::Intercept * intercept)265 void 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
AddPoints(const SkPoint * pts,int ptCount,const SkScalar bounds[2],bool yAxis,SkGlyph::Intercept * intercept)270 void 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
AddLine(const SkPoint pts[2],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)280 void 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
AddQuad(const SkPoint pts[2],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)290 void 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
AddCubic(const SkPoint pts[3],SkScalar axis,bool yAxis,SkGlyph::Intercept * intercept)303 void 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
MatchBounds(const SkGlyph * glyph,const SkScalar bounds[2])316 const 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
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,bool yAxis,SkGlyph * glyph,SkScalar * array,int * count)331 void 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
dump() const401 void 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
getAuxProcData(void (* proc)(void *),void ** dataPtr) const422 bool 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
setAuxProc(void (* proc)(void *),void * data)436 void 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
invokeAndRemoveAuxProcs()457 void 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
470 typedef SkAutoTExclusive<SkSpinlock> Exclusive;
471
setCacheSizeLimit(size_t newLimit)472 size_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
setCacheCountLimit(int newCount)486 int 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
purgeAll()499 void 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 */
VisitCache(SkTypeface * typeface,const SkDescriptor * desc,bool (* proc)(const SkGlyphCache *,void *),void * context)510 SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
511 const SkDescriptor* desc,
512 bool (*proc)(const SkGlyphCache*, void*),
513 void* context) {
514 if (!typeface) {
515 typeface = SkTypeface::GetDefaultTypeface();
516 }
517 SkASSERT(desc);
518
519 SkGlyphCache_Globals& globals = get_globals();
520 SkGlyphCache* cache;
521
522 {
523 Exclusive ac(globals.fLock);
524
525 globals.validate();
526
527 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
528 if (cache->fDesc->equals(*desc)) {
529 globals.internalDetachCache(cache);
530 if (!proc(cache, context)) {
531 globals.internalAttachCacheToHead(cache);
532 cache = nullptr;
533 }
534 return cache;
535 }
536 }
537 }
538
539 // Check if we can create a scaler-context before creating the glyphcache.
540 // If not, we may have exhausted OS/font resources, so try purging the
541 // cache once and try again.
542 {
543 // pass true the first time, to notice if the scalercontext failed,
544 // so we can try the purge.
545 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
546 if (!ctx) {
547 get_globals().purgeAll();
548 ctx = typeface->createScalerContext(desc, false);
549 SkASSERT(ctx);
550 }
551 cache = new SkGlyphCache(typeface, desc, ctx);
552 }
553
554 AutoValidate av(cache);
555
556 if (!proc(cache, context)) { // need to reattach
557 globals.attachCacheToHead(cache);
558 cache = nullptr;
559 }
560 return cache;
561 }
562
AttachCache(SkGlyphCache * cache)563 void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
564 SkASSERT(cache);
565 SkASSERT(cache->fNext == nullptr);
566
567 get_globals().attachCacheToHead(cache);
568 }
569
dump_visitor(const SkGlyphCache & cache,void * context)570 static void dump_visitor(const SkGlyphCache& cache, void* context) {
571 int* counter = (int*)context;
572 int index = *counter;
573 *counter += 1;
574
575 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
576
577 SkDebugf("[%3d] ID %3d, glyphs %3d, size %g, scale %g, skew %g, [%g %g %g %g]\n",
578 index, rec.fFontID, cache.countCachedGlyphs(),
579 rec.fTextSize, rec.fPreScaleX, rec.fPreSkewX,
580 rec.fPost2x2[0][0], rec.fPost2x2[0][1], rec.fPost2x2[1][0], rec.fPost2x2[1][1]);
581 }
582
Dump()583 void SkGlyphCache::Dump() {
584 SkDebugf("GlyphCache [ used budget ]\n");
585 SkDebugf(" bytes [ %8zu %8zu ]\n",
586 SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
587 SkDebugf(" count [ %8zu %8zu ]\n",
588 SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());
589
590 int counter = 0;
591 SkGlyphCache::VisitAll(dump_visitor, &counter);
592 }
593
sk_trace_dump_visitor(const SkGlyphCache & cache,void * context)594 static void sk_trace_dump_visitor(const SkGlyphCache& cache, void* context) {
595 SkTraceMemoryDump* dump = static_cast<SkTraceMemoryDump*>(context);
596
597 const SkTypeface* face = cache.getScalerContext()->getTypeface();
598 const SkScalerContextRec& rec = cache.getScalerContext()->getRec();
599
600 SkString fontName;
601 face->getFamilyName(&fontName);
602 // Replace all special characters with '_'.
603 for (size_t index = 0; index < fontName.size(); ++index) {
604 if (!std::isalnum(fontName[index])) {
605 fontName[index] = '_';
606 }
607 }
608
609 SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
610 gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &cache);
611
612 dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", cache.getMemoryUsed());
613 dump->dumpNumericValue(dumpName.c_str(), "glyph_count", "objects", cache.countCachedGlyphs());
614 dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
615 }
616
DumpMemoryStatistics(SkTraceMemoryDump * dump)617 void SkGlyphCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
618 dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
619 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
620 SkGraphics::GetFontCacheLimit());
621 dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
622 SkGraphics::GetFontCacheCountUsed());
623 dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
624 SkGraphics::GetFontCacheCountLimit());
625
626 if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
627 dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
628 return;
629 }
630
631 SkGlyphCache::VisitAll(sk_trace_dump_visitor, dump);
632 }
633
VisitAll(Visitor visitor,void * context)634 void SkGlyphCache::VisitAll(Visitor visitor, void* context) {
635 SkGlyphCache_Globals& globals = get_globals();
636 Exclusive ac(globals.fLock);
637 SkGlyphCache* cache;
638
639 globals.validate();
640
641 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNext) {
642 visitor(*cache, context);
643 }
644 }
645
646 ///////////////////////////////////////////////////////////////////////////////
647
attachCacheToHead(SkGlyphCache * cache)648 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
649 Exclusive ac(fLock);
650
651 this->validate();
652 cache->validate();
653
654 this->internalAttachCacheToHead(cache);
655 this->internalPurge();
656 }
657
internalGetTail() const658 SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
659 SkGlyphCache* cache = fHead;
660 if (cache) {
661 while (cache->fNext) {
662 cache = cache->fNext;
663 }
664 }
665 return cache;
666 }
667
internalPurge(size_t minBytesNeeded)668 size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
669 this->validate();
670
671 size_t bytesNeeded = 0;
672 if (fTotalMemoryUsed > fCacheSizeLimit) {
673 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
674 }
675 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
676 if (bytesNeeded) {
677 // no small purges!
678 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
679 }
680
681 int countNeeded = 0;
682 if (fCacheCount > fCacheCountLimit) {
683 countNeeded = fCacheCount - fCacheCountLimit;
684 // no small purges!
685 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
686 }
687
688 // early exit
689 if (!countNeeded && !bytesNeeded) {
690 return 0;
691 }
692
693 size_t bytesFreed = 0;
694 int countFreed = 0;
695
696 // we start at the tail and proceed backwards, as the linklist is in LRU
697 // order, with unimportant entries at the tail.
698 SkGlyphCache* cache = this->internalGetTail();
699 while (cache != nullptr &&
700 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
701 SkGlyphCache* prev = cache->fPrev;
702 bytesFreed += cache->fMemoryUsed;
703 countFreed += 1;
704
705 this->internalDetachCache(cache);
706 delete cache;
707 cache = prev;
708 }
709
710 this->validate();
711
712 #ifdef SPEW_PURGE_STATUS
713 if (countFreed) {
714 SkDebugf("purging %dK from font cache [%d entries]\n",
715 (int)(bytesFreed >> 10), countFreed);
716 }
717 #endif
718
719 return bytesFreed;
720 }
721
internalAttachCacheToHead(SkGlyphCache * cache)722 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
723 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext);
724 if (fHead) {
725 fHead->fPrev = cache;
726 cache->fNext = fHead;
727 }
728 fHead = cache;
729
730 fCacheCount += 1;
731 fTotalMemoryUsed += cache->fMemoryUsed;
732 }
733
internalDetachCache(SkGlyphCache * cache)734 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
735 SkASSERT(fCacheCount > 0);
736 fCacheCount -= 1;
737 fTotalMemoryUsed -= cache->fMemoryUsed;
738
739 if (cache->fPrev) {
740 cache->fPrev->fNext = cache->fNext;
741 } else {
742 fHead = cache->fNext;
743 }
744 if (cache->fNext) {
745 cache->fNext->fPrev = cache->fPrev;
746 }
747 cache->fPrev = cache->fNext = nullptr;
748 }
749
750 ///////////////////////////////////////////////////////////////////////////////
751
752 #ifdef SK_DEBUG
753
validate() const754 void SkGlyphCache::validate() const {
755 #ifdef SK_DEBUG_GLYPH_CACHE
756 int count = fGlyphArray.count();
757 for (int i = 0; i < count; i++) {
758 const SkGlyph* glyph = &fGlyphArray[i];
759 SkASSERT(glyph);
760 if (glyph->fImage) {
761 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
762 }
763 }
764 #endif
765 }
766
validate() const767 void SkGlyphCache_Globals::validate() const {
768 size_t computedBytes = 0;
769 int computedCount = 0;
770
771 const SkGlyphCache* head = fHead;
772 while (head != nullptr) {
773 computedBytes += head->fMemoryUsed;
774 computedCount += 1;
775 head = head->fNext;
776 }
777
778 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d", fCacheCount,
779 computedCount);
780 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computedBytes: %d",
781 fTotalMemoryUsed, computedBytes);
782 }
783
784 #endif
785
786 ///////////////////////////////////////////////////////////////////////////////
787 ///////////////////////////////////////////////////////////////////////////////
788
789 #include "SkTypefaceCache.h"
790
GetFontCacheLimit()791 size_t SkGraphics::GetFontCacheLimit() {
792 return get_globals().getCacheSizeLimit();
793 }
794
SetFontCacheLimit(size_t bytes)795 size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
796 return get_globals().setCacheSizeLimit(bytes);
797 }
798
GetFontCacheUsed()799 size_t SkGraphics::GetFontCacheUsed() {
800 return get_globals().getTotalMemoryUsed();
801 }
802
GetFontCacheCountLimit()803 int SkGraphics::GetFontCacheCountLimit() {
804 return get_globals().getCacheCountLimit();
805 }
806
SetFontCacheCountLimit(int count)807 int SkGraphics::SetFontCacheCountLimit(int count) {
808 return get_globals().setCacheCountLimit(count);
809 }
810
GetFontCacheCountUsed()811 int SkGraphics::GetFontCacheCountUsed() {
812 return get_globals().getCacheCountUsed();
813 }
814
PurgeFontCache()815 void SkGraphics::PurgeFontCache() {
816 get_globals().purgeAll();
817 SkTypefaceCache::PurgeAll();
818 }
819
820 // TODO(herb): clean up TLS apis.
GetTLSFontCacheLimit()821 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; }
SetTLSFontCacheLimit(size_t bytes)822 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { }
823