blob: 57c54bfc1ec3f7f35d3c499ba14c26abe99bcdcf [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkGlyphCache.h"
reed@google.combaed71f2013-09-26 19:28:27 +00009#include "SkGlyphCache_Globals.h"
reed@google.com77407ca2011-11-08 13:48:32 +000010#include "SkGraphics.h"
mtklein78358bf2014-06-02 08:44:27 -070011#include "SkLazyPtr.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkPaint.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000013#include "SkPath.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkTemplates.h"
reed@google.com6172d672012-05-17 13:38:03 +000015#include "SkTLS.h"
reed@google.com90808e82013-03-19 14:44:54 +000016#include "SkTypeface.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
reed@google.com8887be02012-01-25 16:04:18 +000018//#define SPEW_PURGE_STATUS
reed@android.com8a1c16f2008-12-17 15:59:43 +000019
mtklein78358bf2014-06-02 08:44:27 -070020namespace {
21
22SkGlyphCache_Globals* create_globals() {
23 return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
bungeman@google.comb6ad00b2014-04-28 13:58:35 +000024}
25
mtklein78358bf2014-06-02 08:44:27 -070026} // namespace
27
mtklein148ec592014-10-13 13:17:56 -070028SK_DECLARE_STATIC_LAZY_PTR(SkGlyphCache_Globals, globals, create_globals);
29
reed@google.combaed71f2013-09-26 19:28:27 +000030// Returns the shared globals
31static SkGlyphCache_Globals& getSharedGlobals() {
mtklein78358bf2014-06-02 08:44:27 -070032 return *globals.get();
reed@google.combaed71f2013-09-26 19:28:27 +000033}
34
35// Returns the TLS globals (if set), or the shared globals
36static SkGlyphCache_Globals& getGlobals() {
37 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
38 return tls ? *tls : getSharedGlobals();
39}
40
reed@android.com8a1c16f2008-12-17 15:59:43 +000041///////////////////////////////////////////////////////////////////////////////
42
reed40dab982015-01-28 13:28:53 -080043#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
44 #define RecordHashSuccess() fHashHitCount += 1
45 #define RecordHashCollisionIf(pred) do { if (pred) fHashMissCount += 1; } while (0)
reed@android.com8a1c16f2008-12-17 15:59:43 +000046#else
reed40dab982015-01-28 13:28:53 -080047 #define RecordHashSuccess() (void)0
48 #define RecordHashCollisionIf(pred) (void)0
reed@android.com8a1c16f2008-12-17 15:59:43 +000049#endif
50#define RecordHashCollision() RecordHashCollisionIf(true)
51
52///////////////////////////////////////////////////////////////////////////////
53
reed@google.com6757a3c2013-06-19 19:25:36 +000054// so we don't grow our arrays a lot
55#define kMinGlyphCount 16
56#define kMinGlyphImageSize (16*2)
57#define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
reed@android.com8a1c16f2008-12-17 15:59:43 +000058
reed@google.com4b60dbe2013-07-09 15:29:32 +000059SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
60 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
reed@google.com90808e82013-03-19 14:44:54 +000061 SkASSERT(typeface);
reed@google.com4b60dbe2013-07-09 15:29:32 +000062 SkASSERT(desc);
63 SkASSERT(ctx);
reed@google.com90808e82013-03-19 14:44:54 +000064
reed@android.com8a1c16f2008-12-17 15:59:43 +000065 fPrev = fNext = NULL;
66
67 fDesc = desc->copy();
reed@google.com0a01f5a2013-05-08 14:19:08 +000068 fScalerContext->getFontMetrics(&fFontMetrics);
herb4c08f162015-02-02 17:47:31 -080069
bsalomon9bf4e5b2015-02-02 21:06:23 -080070 // init to 0 so that all of the pointers will be null
71 memset(fGlyphHash, 0, sizeof(fGlyphHash));
72
reed@google.com6757a3c2013-06-19 19:25:36 +000073 fMemoryUsed = sizeof(*this);
reed@google.com22a02212011-03-01 21:33:48 +000074
reed@google.com6757a3c2013-06-19 19:25:36 +000075 fGlyphArray.setReserve(kMinGlyphCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +000076
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 fAuxProcList = NULL;
reed40dab982015-01-28 13:28:53 -080078
79#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
80 fHashHitCount = fHashMissCount = 0;
81#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000082}
83
84SkGlyphCache::~SkGlyphCache() {
reed@google.com6757a3c2013-06-19 19:25:36 +000085#if 0
86 {
87 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
88 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
89 size_t glyphHashUsed = 0;
90 size_t uniHashUsed = 0;
91 for (int i = 0; i < kHashCount; ++i) {
92 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
93 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
94 }
95 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
96 size_t imageUsed = 0;
97 for (int i = 0; i < fGlyphArray.count(); ++i) {
98 const SkGlyph& g = *fGlyphArray[i];
99 if (g.fImage) {
100 imageUsed += g.fHeight * g.rowBytes();
101 }
102 }
103
herbf8dd0762015-01-28 14:12:12 -0800104 SkDebugf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
105 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(CharGlyphRec) * kHashCount, uniHashUsed);
skia.committer@gmail.com4d494f02013-06-20 07:00:59 +0000106
reed@google.com6757a3c2013-06-19 19:25:36 +0000107 }
108#endif
bsalomon9bf4e5b2015-02-02 21:06:23 -0800109 SkGlyph** gptr = fGlyphArray.begin();
110 SkGlyph** stop = fGlyphArray.end();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111 while (gptr < stop) {
bsalomon9bf4e5b2015-02-02 21:06:23 -0800112 SkPath* path = (*gptr)->fPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 if (path) {
114 SkDELETE(path);
115 }
116 gptr += 1;
117 }
118 SkDescriptor::Free(fDesc);
119 SkDELETE(fScalerContext);
120 this->invokeAndRemoveAuxProcs();
121}
122
herbf8dd0762015-01-28 14:12:12 -0800123SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(uint32_t id) {
124 if (NULL == fCharToGlyphHash.get()) {
125 fCharToGlyphHash.reset(kHashCount);
bsalomon9bf4e5b2015-02-02 21:06:23 -0800126 // init with 0xFF so that the charCode field will be -1, which is invalid
127 memset(fCharToGlyphHash.get(), 0xFF,
herbf8dd0762015-01-28 14:12:12 -0800128 sizeof(CharGlyphRec) * kHashCount);
129 }
130
131 return &fCharToGlyphHash[ID2HashIndex(id)];
132}
133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134///////////////////////////////////////////////////////////////////////////////
135
136#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +0000137#define VALIDATE() AutoValidate av(this)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138#else
139#define VALIDATE()
140#endif
141
142uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
143 VALIDATE();
144 uint32_t id = SkGlyph::MakeID(charCode);
herbf8dd0762015-01-28 14:12:12 -0800145 const CharGlyphRec& rec = *this->getCharGlyphRec(id);
reed@google.com22a02212011-03-01 21:33:48 +0000146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 if (rec.fID == id) {
bsalomon9bf4e5b2015-02-02 21:06:23 -0800148 return rec.fGlyph->getGlyphID();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 } else {
150 return fScalerContext->charToGlyphID(charCode);
151 }
152}
153
reed@android.com9d3a9852010-01-08 14:07:42 +0000154SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
155 return fScalerContext->glyphIDToChar(glyphID);
156}
157
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000158unsigned SkGlyphCache::getGlyphCount() {
159 return fScalerContext->getGlyphCount();
160}
161
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162///////////////////////////////////////////////////////////////////////////////
163
164const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
165 VALIDATE();
bsalomon9bf4e5b2015-02-02 21:06:23 -0800166 uint32_t id = SkGlyph::MakeID(charCode);
167 CharGlyphRec* rec = this->getCharGlyphRec(id);
168
169 if (rec->fID != id) {
170 // this ID is based on the UniChar
171 rec->fID = id;
172 // this ID is based on the glyph index
173 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
174 rec->fGlyph = this->lookupMetrics(id, kJustAdvance_MetricsType);
175 }
176 return *rec->fGlyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177}
178
179const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
180 VALIDATE();
181 uint32_t id = SkGlyph::MakeID(glyphID);
bsalomon9bf4e5b2015-02-02 21:06:23 -0800182 unsigned index = ID2HashIndex(id);
183 SkGlyph* glyph = fGlyphHash[index];
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184
bsalomon9bf4e5b2015-02-02 21:06:23 -0800185 if (NULL == glyph || glyph->fID != id) {
186 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
187 fGlyphHash[index] = glyph;
188 }
189 return *glyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
195 VALIDATE();
bsalomon9bf4e5b2015-02-02 21:06:23 -0800196 uint32_t id = SkGlyph::MakeID(charCode);
197 CharGlyphRec* rec = this->getCharGlyphRec(id);
198
199 if (rec->fID != id) {
200 RecordHashCollisionIf(rec->fGlyph != NULL);
201 // this ID is based on the UniChar
202 rec->fID = id;
203 // this ID is based on the glyph index
204 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode));
205 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
206 } else {
207 RecordHashSuccess();
208 if (rec->fGlyph->isJustAdvance()) {
209 fScalerContext->getMetrics(rec->fGlyph);
210 }
211 }
212 SkASSERT(rec->fGlyph->isFullMetrics());
213 return *rec->fGlyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214}
215
216const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
217 SkFixed x, SkFixed y) {
218 VALIDATE();
bsalomon9bf4e5b2015-02-02 21:06:23 -0800219 uint32_t id = SkGlyph::MakeID(charCode, x, y);
220 CharGlyphRec* rec = this->getCharGlyphRec(id);
221
222 if (rec->fID != id) {
223 RecordHashCollisionIf(rec->fGlyph != NULL);
224 // this ID is based on the UniChar
225 rec->fID = id;
226 // this ID is based on the glyph index
227 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
228 rec->fGlyph = this->lookupMetrics(id, kFull_MetricsType);
229 } else {
230 RecordHashSuccess();
231 if (rec->fGlyph->isJustAdvance()) {
232 fScalerContext->getMetrics(rec->fGlyph);
233 }
234 }
235 SkASSERT(rec->fGlyph->isFullMetrics());
236 return *rec->fGlyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237}
238
239const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
240 VALIDATE();
241 uint32_t id = SkGlyph::MakeID(glyphID);
bsalomon9bf4e5b2015-02-02 21:06:23 -0800242 unsigned index = ID2HashIndex(id);
243 SkGlyph* glyph = fGlyphHash[index];
244
245 if (NULL == glyph || glyph->fID != id) {
246 RecordHashCollisionIf(glyph != NULL);
247 glyph = this->lookupMetrics(glyphID, kFull_MetricsType);
248 fGlyphHash[index] = glyph;
249 } else {
250 RecordHashSuccess();
251 if (glyph->isJustAdvance()) {
252 fScalerContext->getMetrics(glyph);
253 }
254 }
255 SkASSERT(glyph->isFullMetrics());
256 return *glyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257}
258
reed40dab982015-01-28 13:28:53 -0800259const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 VALIDATE();
261 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
bsalomon9bf4e5b2015-02-02 21:06:23 -0800262 unsigned index = ID2HashIndex(id);
263 SkGlyph* glyph = fGlyphHash[index];
264
265 if (NULL == glyph || glyph->fID != id) {
266 RecordHashCollisionIf(glyph != NULL);
267 glyph = this->lookupMetrics(id, kFull_MetricsType);
268 fGlyphHash[index] = glyph;
269 } else {
270 RecordHashSuccess();
271 if (glyph->isJustAdvance()) {
272 fScalerContext->getMetrics(glyph);
273 }
274 }
275 SkASSERT(glyph->isFullMetrics());
276 return *glyph;
herb4c08f162015-02-02 17:47:31 -0800277}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278
bsalomon9bf4e5b2015-02-02 21:06:23 -0800279SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
herb4c08f162015-02-02 17:47:31 -0800280 SkGlyph* glyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281
bsalomon9bf4e5b2015-02-02 21:06:23 -0800282 int hi = 0;
283 int count = fGlyphArray.count();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
bsalomon9bf4e5b2015-02-02 21:06:23 -0800285 if (count) {
286 SkGlyph** gptr = fGlyphArray.begin();
287 int lo = 0;
288
289 hi = count - 1;
290 while (lo < hi) {
291 int mid = (hi + lo) >> 1;
292 if (gptr[mid]->fID < id) {
293 lo = mid + 1;
294 } else {
295 hi = mid;
296 }
297 }
298 glyph = gptr[hi];
299 if (glyph->fID == id) {
300 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
301 fScalerContext->getMetrics(glyph);
302 }
303 return glyph;
304 }
305
306 // check if we need to bump hi before falling though to the allocator
307 if (glyph->fID < id) {
308 hi += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 }
310 }
311
312 // not found, but hi tells us where to inser the new glyph
313 fMemoryUsed += sizeof(SkGlyph);
314
bsalomon9bf4e5b2015-02-02 21:06:23 -0800315 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
316 SkChunkAlloc::kThrow_AllocFailType);
reed@android.comf2b98d62010-12-20 18:26:13 +0000317 glyph->init(id);
bsalomon9bf4e5b2015-02-02 21:06:23 -0800318 *fGlyphArray.insert(hi) = glyph;
reed@google.com22a02212011-03-01 21:33:48 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 if (kJustAdvance_MetricsType == mtype) {
321 fScalerContext->getAdvance(glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 } else {
323 SkASSERT(kFull_MetricsType == mtype);
324 fScalerContext->getMetrics(glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 }
326
bsalomon9bf4e5b2015-02-02 21:06:23 -0800327 return glyph;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328}
329
330const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000331 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000332 if (NULL == glyph.fImage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 size_t size = glyph.computeImageSize();
reed@google.com6757a3c2013-06-19 19:25:36 +0000334 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000336 // check that alloc() actually succeeded
bsalomon49f085d2014-09-05 13:34:00 -0700337 if (glyph.fImage) {
reed@google.com22a02212011-03-01 21:33:48 +0000338 fScalerContext->getImage(glyph);
reed@google.com5e2df642011-09-21 18:42:09 +0000339 // TODO: the scaler may have changed the maskformat during
340 // getImage (e.g. from AA or LCD to BW) which means we may have
341 // overallocated the buffer. Check if the new computedImageSize
342 // is smaller, and if so, strink the alloc size in fImageAlloc.
reed@google.com22a02212011-03-01 21:33:48 +0000343 fMemoryUsed += size;
344 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 }
346 }
347 return glyph.fImage;
348}
349
350const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
351 if (glyph.fWidth) {
352 if (glyph.fPath == NULL) {
353 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
354 fScalerContext->getPath(glyph, glyph.fPath);
355 fMemoryUsed += sizeof(SkPath) +
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000356 glyph.fPath->countPoints() * sizeof(SkPoint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 }
358 }
359 return glyph.fPath;
360}
361
reed40dab982015-01-28 13:28:53 -0800362void SkGlyphCache::dump() const {
363 const SkTypeface* face = fScalerContext->getTypeface();
364 const SkScalerContextRec& rec = fScalerContext->getRec();
365 SkMatrix matrix;
366 rec.getSingleMatrix(&matrix);
367 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
368 SkString name;
369 face->getFamilyName(&name);
370
371 SkString msg;
372 msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d",
373 face->uniqueID(), name.c_str(), face->style(), rec.fTextSize,
374 matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX],
375 matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY],
376 rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast,
377 fGlyphArray.count());
378#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
379 const int sum = SkTMax(fHashHitCount + fHashMissCount, 1); // avoid divide-by-zero
380 msg.appendf(" hash:%2d\n", 100 * fHashHitCount / sum);
381#endif
382 SkDebugf("%s\n", msg.c_str());
383}
384
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385///////////////////////////////////////////////////////////////////////////////
386
387bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
388 const AuxProcRec* rec = fAuxProcList;
389 while (rec) {
390 if (rec->fProc == proc) {
391 if (dataPtr) {
392 *dataPtr = rec->fData;
393 }
394 return true;
395 }
396 rec = rec->fNext;
397 }
398 return false;
399}
400
401void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
402 if (proc == NULL) {
403 return;
404 }
405
406 AuxProcRec* rec = fAuxProcList;
407 while (rec) {
408 if (rec->fProc == proc) {
409 rec->fData = data;
410 return;
411 }
412 rec = rec->fNext;
413 }
414 // not found, create a new rec
415 rec = SkNEW(AuxProcRec);
416 rec->fProc = proc;
417 rec->fData = data;
418 rec->fNext = fAuxProcList;
419 fAuxProcList = rec;
420}
421
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422void SkGlyphCache::invokeAndRemoveAuxProcs() {
423 AuxProcRec* rec = fAuxProcList;
424 while (rec) {
425 rec->fProc(rec->fData);
426 AuxProcRec* next = rec->fNext;
427 SkDELETE(rec);
428 rec = next;
429 }
430}
431
432///////////////////////////////////////////////////////////////////////////////
433///////////////////////////////////////////////////////////////////////////////
434
reed@google.com5d248bc2011-11-17 21:49:02 +0000435#include "SkThread.h"
436
reed@google.combaed71f2013-09-26 19:28:27 +0000437size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
reed@google.com09837012012-04-23 15:04:44 +0000438 static const size_t minLimit = 256 * 1024;
439 if (newLimit < minLimit) {
440 newLimit = minLimit;
441 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000442
reed@google.combaed71f2013-09-26 19:28:27 +0000443 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000444
reed@google.combaed71f2013-09-26 19:28:27 +0000445 size_t prevLimit = fCacheSizeLimit;
446 fCacheSizeLimit = newLimit;
447 this->internalPurge();
reed@google.com09837012012-04-23 15:04:44 +0000448 return prevLimit;
449}
450
reed@google.combaed71f2013-09-26 19:28:27 +0000451int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
452 if (newCount < 0) {
453 newCount = 0;
454 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000455
reed@google.combaed71f2013-09-26 19:28:27 +0000456 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000457
reed@google.combaed71f2013-09-26 19:28:27 +0000458 int prevCount = fCacheCountLimit;
459 fCacheCountLimit = newCount;
460 this->internalPurge();
461 return prevCount;
462}
463
reed@google.com26344cf2012-06-27 18:23:01 +0000464void SkGlyphCache_Globals::purgeAll() {
465 SkAutoMutexAcquire ac(fMutex);
reed@google.combaed71f2013-09-26 19:28:27 +0000466 this->internalPurge(fTotalMemoryUsed);
reed@google.com09837012012-04-23 15:04:44 +0000467}
468
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469/* This guy calls the visitor from within the mutext lock, so the visitor
470 cannot:
471 - take too much time
472 - try to acquire the mutext again
473 - call a fontscaler (which might call into the cache)
474*/
reed@google.com90808e82013-03-19 14:44:54 +0000475SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
476 const SkDescriptor* desc,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 bool (*proc)(const SkGlyphCache*, void*),
478 void* context) {
reed@google.com90808e82013-03-19 14:44:54 +0000479 if (!typeface) {
480 typeface = SkTypeface::GetDefaultTypeface();
481 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkASSERT(desc);
483
reed@google.com5d248bc2011-11-17 21:49:02 +0000484 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 SkAutoMutexAcquire ac(globals.fMutex);
486 SkGlyphCache* cache;
487 bool insideMutex = true;
488
489 globals.validate();
490
reed@google.combaed71f2013-09-26 19:28:27 +0000491 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 if (cache->fDesc->equals(*desc)) {
reed@google.combaed71f2013-09-26 19:28:27 +0000493 globals.internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 goto FOUND_IT;
495 }
496 }
497
498 /* Release the mutex now, before we create a new entry (which might have
499 side-effects like trying to access the cache/mutex (yikes!)
500 */
501 ac.release(); // release the mutex now
502 insideMutex = false; // can't use globals anymore
503
reed@google.com4b60dbe2013-07-09 15:29:32 +0000504 // Check if we can create a scaler-context before creating the glyphcache.
505 // If not, we may have exhausted OS/font resources, so try purging the
506 // cache once and try again.
507 {
reed@google.com84e22d82013-07-10 15:38:20 +0000508 // pass true the first time, to notice if the scalercontext failed,
509 // so we can try the purge.
510 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000511 if (!ctx) {
512 getSharedGlobals().purgeAll();
reed@google.com84e22d82013-07-10 15:38:20 +0000513 ctx = typeface->createScalerContext(desc, false);
514 SkASSERT(ctx);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000515 }
516 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
517 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518
519FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000520
521 AutoValidate av(cache);
522
reed@google.combaed71f2013-09-26 19:28:27 +0000523 if (!proc(cache, context)) { // need to reattach
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 if (insideMutex) {
reed@google.combaed71f2013-09-26 19:28:27 +0000525 globals.internalAttachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000527 globals.attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 }
529 cache = NULL;
530 }
531 return cache;
532}
533
534void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
535 SkASSERT(cache);
536 SkASSERT(cache->fNext == NULL);
537
reed@google.combaed71f2013-09-26 19:28:27 +0000538 getGlobals().attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539}
540
reed40dab982015-01-28 13:28:53 -0800541void SkGlyphCache::Dump() {
542 SkGlyphCache_Globals& globals = getGlobals();
543 SkAutoMutexAcquire ac(globals.fMutex);
544 SkGlyphCache* cache;
545
546 globals.validate();
547
548 SkDebugf("SkGlyphCache strikes:%d memory:%d\n",
549 globals.getCacheCountUsed(), (int)globals.getTotalMemoryUsed());
550
551#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
552 int hitCount = 0;
553 int missCount = 0;
554#endif
555
556 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
557#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
558 hitCount += cache->fHashHitCount;
559 missCount += cache->fHashMissCount;
560#endif
561 cache->dump();
562 }
563#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
564 SkDebugf("Hash hit percent:%2d\n", 100 * hitCount / (hitCount + missCount));
565#endif
566}
567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568///////////////////////////////////////////////////////////////////////////////
569
reed@google.combaed71f2013-09-26 19:28:27 +0000570void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
571 SkAutoMutexAcquire ac(fMutex);
572
573 this->validate();
574 cache->validate();
575
576 this->internalAttachCacheToHead(cache);
577 this->internalPurge();
578}
579
580SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
581 SkGlyphCache* cache = fHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 if (cache) {
583 while (cache->fNext) {
584 cache = cache->fNext;
585 }
586 }
587 return cache;
588}
589
reed@google.combaed71f2013-09-26 19:28:27 +0000590size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
591 this->validate();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000592
reed@google.combaed71f2013-09-26 19:28:27 +0000593 size_t bytesNeeded = 0;
594 if (fTotalMemoryUsed > fCacheSizeLimit) {
595 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
596 }
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000597 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
reed@google.combaed71f2013-09-26 19:28:27 +0000598 if (bytesNeeded) {
599 // no small purges!
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000600 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000601 }
602
reed@google.combaed71f2013-09-26 19:28:27 +0000603 int countNeeded = 0;
604 if (fCacheCount > fCacheCountLimit) {
605 countNeeded = fCacheCount - fCacheCountLimit;
606 // no small purges!
607 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609
reed@google.combaed71f2013-09-26 19:28:27 +0000610 // early exit
611 if (!countNeeded && !bytesNeeded) {
612 return 0;
613 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614
615 size_t bytesFreed = 0;
reed@google.combaed71f2013-09-26 19:28:27 +0000616 int countFreed = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617
reed@google.combaed71f2013-09-26 19:28:27 +0000618 // we start at the tail and proceed backwards, as the linklist is in LRU
619 // order, with unimportant entries at the tail.
620 SkGlyphCache* cache = this->internalGetTail();
621 while (cache != NULL &&
622 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 SkGlyphCache* prev = cache->fPrev;
624 bytesFreed += cache->fMemoryUsed;
reed@google.combaed71f2013-09-26 19:28:27 +0000625 countFreed += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626
reed@google.combaed71f2013-09-26 19:28:27 +0000627 this->internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 SkDELETE(cache);
629 cache = prev;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 }
631
reed@google.combaed71f2013-09-26 19:28:27 +0000632 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633
634#ifdef SPEW_PURGE_STATUS
commit-bot@chromium.org261d0152014-05-12 15:48:38 +0000635 if (countFreed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 SkDebugf("purging %dK from font cache [%d entries]\n",
reed@google.combaed71f2013-09-26 19:28:27 +0000637 (int)(bytesFreed >> 10), countFreed);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 }
639#endif
640
641 return bytesFreed;
642}
643
reed@google.combaed71f2013-09-26 19:28:27 +0000644void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
645 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
646 if (fHead) {
647 fHead->fPrev = cache;
648 cache->fNext = fHead;
649 }
650 fHead = cache;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000651
reed@google.combaed71f2013-09-26 19:28:27 +0000652 fCacheCount += 1;
653 fTotalMemoryUsed += cache->fMemoryUsed;
654}
655
656void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
657 SkASSERT(fCacheCount > 0);
658 fCacheCount -= 1;
659 fTotalMemoryUsed -= cache->fMemoryUsed;
660
661 if (cache->fPrev) {
662 cache->fPrev->fNext = cache->fNext;
663 } else {
664 fHead = cache->fNext;
665 }
666 if (cache->fNext) {
667 cache->fNext->fPrev = cache->fPrev;
668 }
669 cache->fPrev = cache->fNext = NULL;
670}
671
reed@android.comf2b98d62010-12-20 18:26:13 +0000672///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000673
reed@google.com09837012012-04-23 15:04:44 +0000674#ifdef SK_DEBUG
reed@google.combaed71f2013-09-26 19:28:27 +0000675
reed@android.comf2b98d62010-12-20 18:26:13 +0000676void SkGlyphCache::validate() const {
djsollen@google.com000dea72012-11-30 16:19:32 +0000677#ifdef SK_DEBUG_GLYPH_CACHE
reed@android.comf2b98d62010-12-20 18:26:13 +0000678 int count = fGlyphArray.count();
679 for (int i = 0; i < count; i++) {
680 const SkGlyph* glyph = fGlyphArray[i];
681 SkASSERT(glyph);
682 SkASSERT(fGlyphAlloc.contains(glyph));
683 if (glyph->fImage) {
reed@google.com6757a3c2013-06-19 19:25:36 +0000684 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
reed@android.comf2b98d62010-12-20 18:26:13 +0000685 }
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000686 if (glyph->fDistanceField) {
687 SkASSERT(fGlyphAlloc.contains(glyph->fDistanceField));
688 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000689 }
djsollen@google.com000dea72012-11-30 16:19:32 +0000690#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000691}
reed@google.combaed71f2013-09-26 19:28:27 +0000692
693void SkGlyphCache_Globals::validate() const {
694 size_t computedBytes = 0;
695 int computedCount = 0;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000696
reed@google.combaed71f2013-09-26 19:28:27 +0000697 const SkGlyphCache* head = fHead;
698 while (head != NULL) {
699 computedBytes += head->fMemoryUsed;
700 computedCount += 1;
701 head = head->fNext;
702 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000703
reed@google.combaed71f2013-09-26 19:28:27 +0000704 SkASSERT(fTotalMemoryUsed == computedBytes);
705 SkASSERT(fCacheCount == computedCount);
706}
707
reed@android.comf2b98d62010-12-20 18:26:13 +0000708#endif
reed@google.com09837012012-04-23 15:04:44 +0000709
710///////////////////////////////////////////////////////////////////////////////
711///////////////////////////////////////////////////////////////////////////////
712
713#include "SkTypefaceCache.h"
714
715size_t SkGraphics::GetFontCacheLimit() {
reed@google.combaed71f2013-09-26 19:28:27 +0000716 return getSharedGlobals().getCacheSizeLimit();
reed@google.com09837012012-04-23 15:04:44 +0000717}
718
719size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
reed@google.combaed71f2013-09-26 19:28:27 +0000720 return getSharedGlobals().setCacheSizeLimit(bytes);
reed@google.com09837012012-04-23 15:04:44 +0000721}
722
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000723size_t SkGraphics::GetFontCacheUsed() {
reed@google.combaed71f2013-09-26 19:28:27 +0000724 return getSharedGlobals().getTotalMemoryUsed();
725}
726
727int SkGraphics::GetFontCacheCountLimit() {
728 return getSharedGlobals().getCacheCountLimit();
729}
730
731int SkGraphics::SetFontCacheCountLimit(int count) {
732 return getSharedGlobals().setCacheCountLimit(count);
733}
734
735int SkGraphics::GetFontCacheCountUsed() {
736 return getSharedGlobals().getCacheCountUsed();
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000737}
738
reed@google.com09837012012-04-23 15:04:44 +0000739void SkGraphics::PurgeFontCache() {
reed@google.com26344cf2012-06-27 18:23:01 +0000740 getSharedGlobals().purgeAll();
reed@google.com09837012012-04-23 15:04:44 +0000741 SkTypefaceCache::PurgeAll();
742}
743
reed@google.com6172d672012-05-17 13:38:03 +0000744size_t SkGraphics::GetTLSFontCacheLimit() {
745 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
reed@google.combaed71f2013-09-26 19:28:27 +0000746 return tls ? tls->getCacheSizeLimit() : 0;
reed@google.com6172d672012-05-17 13:38:03 +0000747}
748
reed@google.com803c67d2012-05-17 13:50:36 +0000749void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
reed@google.com6172d672012-05-17 13:38:03 +0000750 if (0 == bytes) {
751 SkGlyphCache_Globals::DeleteTLS();
752 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000753 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
reed@google.com6172d672012-05-17 13:38:03 +0000754 }
755}