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) { }
|