blob: faa3f89049f1b5360e1f95881cb25286e98e1966 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkGlyphCache.h"
reed@google.combaed71f2013-09-26 19:28:27 +000011#include "SkGlyphCache_Globals.h"
reed@google.com77407ca2011-11-08 13:48:32 +000012#include "SkGraphics.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkPaint.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000014#include "SkPath.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkTemplates.h"
reed@google.com6172d672012-05-17 13:38:03 +000016#include "SkTLS.h"
reed@google.com90808e82013-03-19 14:44:54 +000017#include "SkTypeface.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018
reed@google.com8887be02012-01-25 16:04:18 +000019//#define SPEW_PURGE_STATUS
reed@android.com8a1c16f2008-12-17 15:59:43 +000020//#define RECORD_HASH_EFFICIENCY
21
reed@google.com11b4d8a2011-11-02 21:02:36 +000022bool gSkSuppressFontCachePurgeSpew;
23
reed@google.combaed71f2013-09-26 19:28:27 +000024// Returns the shared globals
25static SkGlyphCache_Globals& getSharedGlobals() {
26 // we leak this, so we don't incur any shutdown cost of the destructor
27 static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
28 (SkGlyphCache_Globals::kYes_UseMutex));
29 return *gGlobals;
30}
31
32// Returns the TLS globals (if set), or the shared globals
33static SkGlyphCache_Globals& getGlobals() {
34 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
35 return tls ? *tls : getSharedGlobals();
36}
37
reed@android.com8a1c16f2008-12-17 15:59:43 +000038///////////////////////////////////////////////////////////////////////////////
39
40#ifdef RECORD_HASH_EFFICIENCY
41 static uint32_t gHashSuccess;
42 static uint32_t gHashCollision;
43
44 static void RecordHashSuccess() {
45 gHashSuccess += 1;
46 }
47
48 static void RecordHashCollisionIf(bool pred) {
49 if (pred) {
50 gHashCollision += 1;
reed@google.com22a02212011-03-01 21:33:48 +000051
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 uint32_t total = gHashSuccess + gHashCollision;
53 SkDebugf("Font Cache Hash success rate: %d%%\n",
54 100 * gHashSuccess / total);
55 }
56 }
57#else
58 #define RecordHashSuccess() (void)0
59 #define RecordHashCollisionIf(pred) (void)0
60#endif
61#define RecordHashCollision() RecordHashCollisionIf(true)
62
63///////////////////////////////////////////////////////////////////////////////
64
reed@google.com6757a3c2013-06-19 19:25:36 +000065// so we don't grow our arrays a lot
66#define kMinGlyphCount 16
67#define kMinGlyphImageSize (16*2)
68#define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
reed@android.com8a1c16f2008-12-17 15:59:43 +000069
reed@google.com4b60dbe2013-07-09 15:29:32 +000070SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
71 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
reed@google.com90808e82013-03-19 14:44:54 +000072 SkASSERT(typeface);
reed@google.com4b60dbe2013-07-09 15:29:32 +000073 SkASSERT(desc);
74 SkASSERT(ctx);
reed@google.com90808e82013-03-19 14:44:54 +000075
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 fPrev = fNext = NULL;
77
78 fDesc = desc->copy();
reed@google.com0a01f5a2013-05-08 14:19:08 +000079 fScalerContext->getFontMetrics(&fFontMetrics);
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81 // init to 0 so that all of the pointers will be null
82 memset(fGlyphHash, 0, sizeof(fGlyphHash));
83 // init with 0xFF so that the charCode field will be -1, which is invalid
84 memset(fCharToGlyphHash, 0xFF, sizeof(fCharToGlyphHash));
reed@google.com22a02212011-03-01 21:33:48 +000085
reed@google.com6757a3c2013-06-19 19:25:36 +000086 fMemoryUsed = sizeof(*this);
reed@google.com22a02212011-03-01 21:33:48 +000087
reed@google.com6757a3c2013-06-19 19:25:36 +000088 fGlyphArray.setReserve(kMinGlyphCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +000089
90 fMetricsCount = 0;
91 fAdvanceCount = 0;
92 fAuxProcList = NULL;
93}
94
95SkGlyphCache::~SkGlyphCache() {
reed@google.com6757a3c2013-06-19 19:25:36 +000096#if 0
97 {
98 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
99 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
100 size_t glyphHashUsed = 0;
101 size_t uniHashUsed = 0;
102 for (int i = 0; i < kHashCount; ++i) {
103 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
104 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
105 }
106 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
107 size_t imageUsed = 0;
108 for (int i = 0; i < fGlyphArray.count(); ++i) {
109 const SkGlyph& g = *fGlyphArray[i];
110 if (g.fImage) {
111 imageUsed += g.fHeight * g.rowBytes();
112 }
113 }
114
115 printf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
116 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(fCharToGlyphHash), uniHashUsed);
skia.committer@gmail.com4d494f02013-06-20 07:00:59 +0000117
reed@google.com6757a3c2013-06-19 19:25:36 +0000118 }
119#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 SkGlyph** gptr = fGlyphArray.begin();
121 SkGlyph** stop = fGlyphArray.end();
122 while (gptr < stop) {
123 SkPath* path = (*gptr)->fPath;
124 if (path) {
125 SkDELETE(path);
126 }
127 gptr += 1;
128 }
129 SkDescriptor::Free(fDesc);
130 SkDELETE(fScalerContext);
131 this->invokeAndRemoveAuxProcs();
132}
133
134///////////////////////////////////////////////////////////////////////////////
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);
145 const CharGlyphRec& rec = fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 if (rec.fID == id) {
148 return rec.fGlyph->getGlyphID();
149 } 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();
166 uint32_t id = SkGlyph::MakeID(charCode);
167 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000168
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 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;
177}
178
179const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
180 VALIDATE();
181 uint32_t id = SkGlyph::MakeID(glyphID);
182 unsigned index = ID2HashIndex(id);
183 SkGlyph* glyph = fGlyphHash[index];
184
185 if (NULL == glyph || glyph->fID != id) {
186 glyph = this->lookupMetrics(glyphID, kJustAdvance_MetricsType);
187 fGlyphHash[index] = glyph;
188 }
189 return *glyph;
190}
191
192///////////////////////////////////////////////////////////////////////////////
193
194const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
195 VALIDATE();
196 uint32_t id = SkGlyph::MakeID(charCode);
197 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 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;
214}
215
216const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
217 SkFixed x, SkFixed y) {
218 VALIDATE();
219 uint32_t id = SkGlyph::MakeID(charCode, x, y);
220 CharGlyphRec* rec = &fCharToGlyphHash[ID2HashIndex(id)];
reed@google.com22a02212011-03-01 21:33:48 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 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;
237}
238
239const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
240 VALIDATE();
241 uint32_t id = SkGlyph::MakeID(glyphID);
242 unsigned index = ID2HashIndex(id);
243 SkGlyph* glyph = fGlyphHash[index];
reed@google.com22a02212011-03-01 21:33:48 +0000244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 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;
257}
258
259const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID,
260 SkFixed x, SkFixed y) {
261 VALIDATE();
262 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
263 unsigned index = ID2HashIndex(id);
264 SkGlyph* glyph = fGlyphHash[index];
265
266 if (NULL == glyph || glyph->fID != id) {
267 RecordHashCollisionIf(glyph != NULL);
268 glyph = this->lookupMetrics(id, kFull_MetricsType);
269 fGlyphHash[index] = glyph;
270 } else {
271 RecordHashSuccess();
272 if (glyph->isJustAdvance()) {
273 fScalerContext->getMetrics(glyph);
274 }
275 }
276 SkASSERT(glyph->isFullMetrics());
277 return *glyph;
278}
279
280SkGlyph* SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
281 SkGlyph* glyph;
282
283 int hi = 0;
284 int count = fGlyphArray.count();
285
286 if (count) {
287 SkGlyph** gptr = fGlyphArray.begin();
288 int lo = 0;
289
290 hi = count - 1;
291 while (lo < hi) {
292 int mid = (hi + lo) >> 1;
293 if (gptr[mid]->fID < id) {
294 lo = mid + 1;
295 } else {
296 hi = mid;
297 }
298 }
299 glyph = gptr[hi];
300 if (glyph->fID == id) {
301 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
302 fScalerContext->getMetrics(glyph);
303 }
304 return glyph;
305 }
306
307 // check if we need to bump hi before falling though to the allocator
308 if (glyph->fID < id) {
309 hi += 1;
310 }
311 }
312
313 // not found, but hi tells us where to inser the new glyph
314 fMemoryUsed += sizeof(SkGlyph);
315
316 glyph = (SkGlyph*)fGlyphAlloc.alloc(sizeof(SkGlyph),
317 SkChunkAlloc::kThrow_AllocFailType);
reed@android.comf2b98d62010-12-20 18:26:13 +0000318 glyph->init(id);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 *fGlyphArray.insert(hi) = glyph;
reed@google.com22a02212011-03-01 21:33:48 +0000320
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 if (kJustAdvance_MetricsType == mtype) {
322 fScalerContext->getAdvance(glyph);
323 fAdvanceCount += 1;
324 } else {
325 SkASSERT(kFull_MetricsType == mtype);
326 fScalerContext->getMetrics(glyph);
327 fMetricsCount += 1;
328 }
329
330 return glyph;
331}
332
333const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000334 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 if (glyph.fImage == NULL) {
336 size_t size = glyph.computeImageSize();
reed@google.com6757a3c2013-06-19 19:25:36 +0000337 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000339 // check that alloc() actually succeeded
340 if (glyph.fImage) {
341 fScalerContext->getImage(glyph);
reed@google.com5e2df642011-09-21 18:42:09 +0000342 // TODO: the scaler may have changed the maskformat during
343 // getImage (e.g. from AA or LCD to BW) which means we may have
344 // overallocated the buffer. Check if the new computedImageSize
345 // is smaller, and if so, strink the alloc size in fImageAlloc.
reed@google.com22a02212011-03-01 21:33:48 +0000346 fMemoryUsed += size;
347 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 }
349 }
350 return glyph.fImage;
351}
352
353const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
354 if (glyph.fWidth) {
355 if (glyph.fPath == NULL) {
356 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
357 fScalerContext->getPath(glyph, glyph.fPath);
358 fMemoryUsed += sizeof(SkPath) +
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000359 glyph.fPath->countPoints() * sizeof(SkPoint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 }
361 }
362 return glyph.fPath;
363}
364
365///////////////////////////////////////////////////////////////////////////////
366
367bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
368 const AuxProcRec* rec = fAuxProcList;
369 while (rec) {
370 if (rec->fProc == proc) {
371 if (dataPtr) {
372 *dataPtr = rec->fData;
373 }
374 return true;
375 }
376 rec = rec->fNext;
377 }
378 return false;
379}
380
381void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
382 if (proc == NULL) {
383 return;
384 }
385
386 AuxProcRec* rec = fAuxProcList;
387 while (rec) {
388 if (rec->fProc == proc) {
389 rec->fData = data;
390 return;
391 }
392 rec = rec->fNext;
393 }
394 // not found, create a new rec
395 rec = SkNEW(AuxProcRec);
396 rec->fProc = proc;
397 rec->fData = data;
398 rec->fNext = fAuxProcList;
399 fAuxProcList = rec;
400}
401
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402void SkGlyphCache::invokeAndRemoveAuxProcs() {
403 AuxProcRec* rec = fAuxProcList;
404 while (rec) {
405 rec->fProc(rec->fData);
406 AuxProcRec* next = rec->fNext;
407 SkDELETE(rec);
408 rec = next;
409 }
410}
411
412///////////////////////////////////////////////////////////////////////////////
413///////////////////////////////////////////////////////////////////////////////
414
reed@google.com5d248bc2011-11-17 21:49:02 +0000415#include "SkThread.h"
416
reed@google.combaed71f2013-09-26 19:28:27 +0000417size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
reed@google.com09837012012-04-23 15:04:44 +0000418 static const size_t minLimit = 256 * 1024;
419 if (newLimit < minLimit) {
420 newLimit = minLimit;
421 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000422
reed@google.combaed71f2013-09-26 19:28:27 +0000423 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000424
reed@google.combaed71f2013-09-26 19:28:27 +0000425 size_t prevLimit = fCacheSizeLimit;
426 fCacheSizeLimit = newLimit;
427 this->internalPurge();
reed@google.com09837012012-04-23 15:04:44 +0000428 return prevLimit;
429}
430
reed@google.combaed71f2013-09-26 19:28:27 +0000431int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
432 if (newCount < 0) {
433 newCount = 0;
434 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000435
reed@google.combaed71f2013-09-26 19:28:27 +0000436 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000437
reed@google.combaed71f2013-09-26 19:28:27 +0000438 int prevCount = fCacheCountLimit;
439 fCacheCountLimit = newCount;
440 this->internalPurge();
441 return prevCount;
442}
443
reed@google.com26344cf2012-06-27 18:23:01 +0000444void SkGlyphCache_Globals::purgeAll() {
445 SkAutoMutexAcquire ac(fMutex);
reed@google.combaed71f2013-09-26 19:28:27 +0000446 this->internalPurge(fTotalMemoryUsed);
reed@google.com09837012012-04-23 15:04:44 +0000447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
450 void* context) {
reed@google.com5d248bc2011-11-17 21:49:02 +0000451 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 SkAutoMutexAcquire ac(globals.fMutex);
453 SkGlyphCache* cache;
reed@google.com22a02212011-03-01 21:33:48 +0000454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 globals.validate();
reed@google.com22a02212011-03-01 21:33:48 +0000456
reed@google.combaed71f2013-09-26 19:28:27 +0000457 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 if (proc(cache, context)) {
459 break;
460 }
461 }
462
463 globals.validate();
464}
465
466/* This guy calls the visitor from within the mutext lock, so the visitor
467 cannot:
468 - take too much time
469 - try to acquire the mutext again
470 - call a fontscaler (which might call into the cache)
471*/
reed@google.com90808e82013-03-19 14:44:54 +0000472SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
473 const SkDescriptor* desc,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 bool (*proc)(const SkGlyphCache*, void*),
475 void* context) {
reed@google.com90808e82013-03-19 14:44:54 +0000476 if (!typeface) {
477 typeface = SkTypeface::GetDefaultTypeface();
478 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 SkASSERT(desc);
480
reed@google.com5d248bc2011-11-17 21:49:02 +0000481 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkAutoMutexAcquire ac(globals.fMutex);
483 SkGlyphCache* cache;
484 bool insideMutex = true;
485
486 globals.validate();
487
reed@google.combaed71f2013-09-26 19:28:27 +0000488 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 if (cache->fDesc->equals(*desc)) {
reed@google.combaed71f2013-09-26 19:28:27 +0000490 globals.internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 goto FOUND_IT;
492 }
493 }
494
495 /* Release the mutex now, before we create a new entry (which might have
496 side-effects like trying to access the cache/mutex (yikes!)
497 */
498 ac.release(); // release the mutex now
499 insideMutex = false; // can't use globals anymore
500
reed@google.com4b60dbe2013-07-09 15:29:32 +0000501 // Check if we can create a scaler-context before creating the glyphcache.
502 // If not, we may have exhausted OS/font resources, so try purging the
503 // cache once and try again.
504 {
reed@google.com84e22d82013-07-10 15:38:20 +0000505 // pass true the first time, to notice if the scalercontext failed,
506 // so we can try the purge.
507 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000508 if (!ctx) {
509 getSharedGlobals().purgeAll();
reed@google.com84e22d82013-07-10 15:38:20 +0000510 ctx = typeface->createScalerContext(desc, false);
511 SkASSERT(ctx);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000512 }
513 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
514 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515
516FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000517
518 AutoValidate av(cache);
519
reed@google.combaed71f2013-09-26 19:28:27 +0000520 if (!proc(cache, context)) { // need to reattach
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 if (insideMutex) {
reed@google.combaed71f2013-09-26 19:28:27 +0000522 globals.internalAttachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000524 globals.attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 }
526 cache = NULL;
527 }
528 return cache;
529}
530
531void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
532 SkASSERT(cache);
533 SkASSERT(cache->fNext == NULL);
534
reed@google.combaed71f2013-09-26 19:28:27 +0000535 getGlobals().attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536}
537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538///////////////////////////////////////////////////////////////////////////////
539
reed@google.combaed71f2013-09-26 19:28:27 +0000540void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
541 SkAutoMutexAcquire ac(fMutex);
542
543 this->validate();
544 cache->validate();
545
546 this->internalAttachCacheToHead(cache);
547 this->internalPurge();
548}
549
550SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
551 SkGlyphCache* cache = fHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 if (cache) {
553 while (cache->fNext) {
554 cache = cache->fNext;
555 }
556 }
557 return cache;
558}
559
reed@google.combaed71f2013-09-26 19:28:27 +0000560size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
561 this->validate();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000562
reed@google.combaed71f2013-09-26 19:28:27 +0000563 size_t bytesNeeded = 0;
564 if (fTotalMemoryUsed > fCacheSizeLimit) {
565 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
566 }
567 bytesNeeded = SkMax32(bytesNeeded, minBytesNeeded);
568 if (bytesNeeded) {
569 // no small purges!
570 bytesNeeded = SkMax32(bytesNeeded, fTotalMemoryUsed >> 2);
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000571 }
572
reed@google.combaed71f2013-09-26 19:28:27 +0000573 int countNeeded = 0;
574 if (fCacheCount > fCacheCountLimit) {
575 countNeeded = fCacheCount - fCacheCountLimit;
576 // no small purges!
577 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579
reed@google.combaed71f2013-09-26 19:28:27 +0000580 // early exit
581 if (!countNeeded && !bytesNeeded) {
582 return 0;
583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584
585 size_t bytesFreed = 0;
reed@google.combaed71f2013-09-26 19:28:27 +0000586 int countFreed = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587
reed@google.combaed71f2013-09-26 19:28:27 +0000588 // we start at the tail and proceed backwards, as the linklist is in LRU
589 // order, with unimportant entries at the tail.
590 SkGlyphCache* cache = this->internalGetTail();
591 while (cache != NULL &&
592 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkGlyphCache* prev = cache->fPrev;
594 bytesFreed += cache->fMemoryUsed;
reed@google.combaed71f2013-09-26 19:28:27 +0000595 countFreed += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596
reed@google.combaed71f2013-09-26 19:28:27 +0000597 this->internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 SkDELETE(cache);
599 cache = prev;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 }
601
reed@google.combaed71f2013-09-26 19:28:27 +0000602 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603
604#ifdef SPEW_PURGE_STATUS
reed@google.combaed71f2013-09-26 19:28:27 +0000605 if (countFreed && !gSkSuppressFontCachePurgeSpew) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 SkDebugf("purging %dK from font cache [%d entries]\n",
reed@google.combaed71f2013-09-26 19:28:27 +0000607 (int)(bytesFreed >> 10), countFreed);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 }
609#endif
610
611 return bytesFreed;
612}
613
reed@google.combaed71f2013-09-26 19:28:27 +0000614void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
615 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
616 if (fHead) {
617 fHead->fPrev = cache;
618 cache->fNext = fHead;
619 }
620 fHead = cache;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000621
reed@google.combaed71f2013-09-26 19:28:27 +0000622 fCacheCount += 1;
623 fTotalMemoryUsed += cache->fMemoryUsed;
624}
625
626void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
627 SkASSERT(fCacheCount > 0);
628 fCacheCount -= 1;
629 fTotalMemoryUsed -= cache->fMemoryUsed;
630
631 if (cache->fPrev) {
632 cache->fPrev->fNext = cache->fNext;
633 } else {
634 fHead = cache->fNext;
635 }
636 if (cache->fNext) {
637 cache->fNext->fPrev = cache->fPrev;
638 }
639 cache->fPrev = cache->fNext = NULL;
640}
641
reed@android.comf2b98d62010-12-20 18:26:13 +0000642///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000643
reed@google.com09837012012-04-23 15:04:44 +0000644#ifdef SK_DEBUG
reed@google.combaed71f2013-09-26 19:28:27 +0000645
reed@android.comf2b98d62010-12-20 18:26:13 +0000646void SkGlyphCache::validate() const {
djsollen@google.com000dea72012-11-30 16:19:32 +0000647#ifdef SK_DEBUG_GLYPH_CACHE
reed@android.comf2b98d62010-12-20 18:26:13 +0000648 int count = fGlyphArray.count();
649 for (int i = 0; i < count; i++) {
650 const SkGlyph* glyph = fGlyphArray[i];
651 SkASSERT(glyph);
652 SkASSERT(fGlyphAlloc.contains(glyph));
653 if (glyph->fImage) {
reed@google.com6757a3c2013-06-19 19:25:36 +0000654 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
reed@android.comf2b98d62010-12-20 18:26:13 +0000655 }
656 }
djsollen@google.com000dea72012-11-30 16:19:32 +0000657#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000658}
reed@google.combaed71f2013-09-26 19:28:27 +0000659
660void SkGlyphCache_Globals::validate() const {
661 size_t computedBytes = 0;
662 int computedCount = 0;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000663
reed@google.combaed71f2013-09-26 19:28:27 +0000664 const SkGlyphCache* head = fHead;
665 while (head != NULL) {
666 computedBytes += head->fMemoryUsed;
667 computedCount += 1;
668 head = head->fNext;
669 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000670
reed@google.combaed71f2013-09-26 19:28:27 +0000671 SkASSERT(fTotalMemoryUsed == computedBytes);
672 SkASSERT(fCacheCount == computedCount);
673}
674
reed@android.comf2b98d62010-12-20 18:26:13 +0000675#endif
reed@google.com09837012012-04-23 15:04:44 +0000676
677///////////////////////////////////////////////////////////////////////////////
678///////////////////////////////////////////////////////////////////////////////
679
680#include "SkTypefaceCache.h"
681
682size_t SkGraphics::GetFontCacheLimit() {
reed@google.combaed71f2013-09-26 19:28:27 +0000683 return getSharedGlobals().getCacheSizeLimit();
reed@google.com09837012012-04-23 15:04:44 +0000684}
685
686size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
reed@google.combaed71f2013-09-26 19:28:27 +0000687 return getSharedGlobals().setCacheSizeLimit(bytes);
reed@google.com09837012012-04-23 15:04:44 +0000688}
689
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000690size_t SkGraphics::GetFontCacheUsed() {
reed@google.combaed71f2013-09-26 19:28:27 +0000691 return getSharedGlobals().getTotalMemoryUsed();
692}
693
694int SkGraphics::GetFontCacheCountLimit() {
695 return getSharedGlobals().getCacheCountLimit();
696}
697
698int SkGraphics::SetFontCacheCountLimit(int count) {
699 return getSharedGlobals().setCacheCountLimit(count);
700}
701
702int SkGraphics::GetFontCacheCountUsed() {
703 return getSharedGlobals().getCacheCountUsed();
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000704}
705
reed@google.com09837012012-04-23 15:04:44 +0000706void SkGraphics::PurgeFontCache() {
reed@google.com26344cf2012-06-27 18:23:01 +0000707 getSharedGlobals().purgeAll();
reed@google.com09837012012-04-23 15:04:44 +0000708 SkTypefaceCache::PurgeAll();
709}
710
reed@google.com6172d672012-05-17 13:38:03 +0000711size_t SkGraphics::GetTLSFontCacheLimit() {
712 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
reed@google.combaed71f2013-09-26 19:28:27 +0000713 return tls ? tls->getCacheSizeLimit() : 0;
reed@google.com6172d672012-05-17 13:38:03 +0000714}
715
reed@google.com803c67d2012-05-17 13:50:36 +0000716void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
reed@google.com6172d672012-05-17 13:38:03 +0000717 if (0 == bytes) {
718 SkGlyphCache_Globals::DeleteTLS();
719 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000720 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
reed@google.com6172d672012-05-17 13:38:03 +0000721 }
722}