blob: 6ae17b10741bc00204000daf1974137e3e5db967 [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"
mtklein1b249332015-07-07 12:21:21 -070012#include "SkMutex.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"
jvanverth02802f62015-07-02 06:42:49 -070015#include "SkTLS.h"
mtklein1b249332015-07-07 12:21:21 -070016#include "SkTemplates.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
mtklein78358bf2014-06-02 08:44:27 -070021namespace {
22
23SkGlyphCache_Globals* create_globals() {
24 return SkNEW_ARGS(SkGlyphCache_Globals, (SkGlyphCache_Globals::kYes_UseMutex));
bungeman@google.comb6ad00b2014-04-28 13:58:35 +000025}
26
mtklein78358bf2014-06-02 08:44:27 -070027} // namespace
28
mtklein148ec592014-10-13 13:17:56 -070029SK_DECLARE_STATIC_LAZY_PTR(SkGlyphCache_Globals, globals, create_globals);
30
reed@google.combaed71f2013-09-26 19:28:27 +000031// Returns the shared globals
32static SkGlyphCache_Globals& getSharedGlobals() {
mtklein78358bf2014-06-02 08:44:27 -070033 return *globals.get();
reed@google.combaed71f2013-09-26 19:28:27 +000034}
35
36// Returns the TLS globals (if set), or the shared globals
37static SkGlyphCache_Globals& getGlobals() {
38 SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
39 return tls ? *tls : getSharedGlobals();
40}
41
reed@android.com8a1c16f2008-12-17 15:59:43 +000042///////////////////////////////////////////////////////////////////////////////
43
reed40dab982015-01-28 13:28:53 -080044#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
45 #define RecordHashSuccess() fHashHitCount += 1
46 #define RecordHashCollisionIf(pred) do { if (pred) fHashMissCount += 1; } while (0)
reed@android.com8a1c16f2008-12-17 15:59:43 +000047#else
reed40dab982015-01-28 13:28:53 -080048 #define RecordHashSuccess() (void)0
49 #define RecordHashCollisionIf(pred) (void)0
reed@android.com8a1c16f2008-12-17 15:59:43 +000050#endif
51#define RecordHashCollision() RecordHashCollisionIf(true)
52
53///////////////////////////////////////////////////////////////////////////////
54
reed@google.com6757a3c2013-06-19 19:25:36 +000055// so we don't grow our arrays a lot
56#define kMinGlyphCount 16
57#define kMinGlyphImageSize (16*2)
58#define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphCount)
reed@android.com8a1c16f2008-12-17 15:59:43 +000059
reed@google.com4b60dbe2013-07-09 15:29:32 +000060SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkScalerContext* ctx)
61 : fScalerContext(ctx), fGlyphAlloc(kMinAllocAmount) {
reed@google.com90808e82013-03-19 14:44:54 +000062 SkASSERT(typeface);
reed@google.com4b60dbe2013-07-09 15:29:32 +000063 SkASSERT(desc);
64 SkASSERT(ctx);
reed@google.com90808e82013-03-19 14:44:54 +000065
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 fPrev = fNext = NULL;
67
68 fDesc = desc->copy();
reed@google.com0a01f5a2013-05-08 14:19:08 +000069 fScalerContext->getFontMetrics(&fFontMetrics);
herbc1e97b32015-03-05 11:51:11 -080070
herbe70de9e2015-02-27 07:22:48 -080071 // Create the sentinel SkGlyph.
herbc1e97b32015-03-05 11:51:11 -080072 SkGlyph* sentinel = fGlyphArray.insert(0);
73 sentinel->initGlyphFromCombinedID(SkGlyph::kImpossibleID);
74
herbe70de9e2015-02-27 07:22:48 -080075 // Initialize all index to zero which points to the sentinel SkGlyph.
76 memset(fGlyphHash, 0x00, sizeof(fGlyphHash));
77
reed@google.com6757a3c2013-06-19 19:25:36 +000078 fMemoryUsed = sizeof(*this);
reed@google.com22a02212011-03-01 21:33:48 +000079
reed@google.com6757a3c2013-06-19 19:25:36 +000080 fGlyphArray.setReserve(kMinGlyphCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fAuxProcList = NULL;
reed40dab982015-01-28 13:28:53 -080083
84#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
85 fHashHitCount = fHashMissCount = 0;
86#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000087}
88
89SkGlyphCache::~SkGlyphCache() {
reed@google.com6757a3c2013-06-19 19:25:36 +000090#if 0
91 {
92 size_t ptrMem = fGlyphArray.count() * sizeof(SkGlyph*);
93 size_t glyphAlloc = fGlyphAlloc.totalCapacity();
94 size_t glyphHashUsed = 0;
95 size_t uniHashUsed = 0;
96 for (int i = 0; i < kHashCount; ++i) {
97 glyphHashUsed += fGlyphHash[i] ? sizeof(fGlyphHash[0]) : 0;
98 uniHashUsed += fCharToGlyphHash[i].fID != 0xFFFFFFFF ? sizeof(fCharToGlyphHash[0]) : 0;
99 }
100 size_t glyphUsed = fGlyphArray.count() * sizeof(SkGlyph);
101 size_t imageUsed = 0;
102 for (int i = 0; i < fGlyphArray.count(); ++i) {
103 const SkGlyph& g = *fGlyphArray[i];
104 if (g.fImage) {
105 imageUsed += g.fHeight * g.rowBytes();
106 }
107 }
108
herbf8dd0762015-01-28 14:12:12 -0800109 SkDebugf("glyphPtrArray,%zu, Alloc,%zu, imageUsed,%zu, glyphUsed,%zu, glyphHashAlloc,%zu, glyphHashUsed,%zu, unicharHashAlloc,%zu, unicharHashUsed,%zu\n",
110 ptrMem, glyphAlloc, imageUsed, glyphUsed, sizeof(fGlyphHash), glyphHashUsed, sizeof(CharGlyphRec) * kHashCount, uniHashUsed);
skia.committer@gmail.com4d494f02013-06-20 07:00:59 +0000111
reed@google.com6757a3c2013-06-19 19:25:36 +0000112 }
113#endif
herbe70de9e2015-02-27 07:22:48 -0800114 SkGlyph* gptr = fGlyphArray.begin();
115 SkGlyph* stop = fGlyphArray.end();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 while (gptr < stop) {
herbe70de9e2015-02-27 07:22:48 -0800117 SkPath* path = gptr->fPath;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 if (path) {
119 SkDELETE(path);
120 }
121 gptr += 1;
122 }
123 SkDescriptor::Free(fDesc);
124 SkDELETE(fScalerContext);
125 this->invokeAndRemoveAuxProcs();
126}
127
herbf8dd0762015-01-28 14:12:12 -0800128SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(uint32_t id) {
129 if (NULL == fCharToGlyphHash.get()) {
herbe70de9e2015-02-27 07:22:48 -0800130 // Allocate the array.
herbf8dd0762015-01-28 14:12:12 -0800131 fCharToGlyphHash.reset(kHashCount);
herbc1e97b32015-03-05 11:51:11 -0800132 // Initialize entries of fCharToGlyphHash to index the sentinel glyph and
133 // an fID value that will not match any id.
134 for (int i = 0; i <kHashCount; ++i) {
135 fCharToGlyphHash[i].fID = SkGlyph::kImpossibleID;
136 fCharToGlyphHash[i].fGlyphIndex = 0;
137 }
herbf8dd0762015-01-28 14:12:12 -0800138 }
herbc1e97b32015-03-05 11:51:11 -0800139
herbf8dd0762015-01-28 14:12:12 -0800140 return &fCharToGlyphHash[ID2HashIndex(id)];
141}
142
herbe70de9e2015-02-27 07:22:48 -0800143void SkGlyphCache::adjustCaches(int insertion_index) {
144 for (int i = 0; i < kHashCount; ++i) {
145 if (fGlyphHash[i] >= SkToU16(insertion_index)) {
146 fGlyphHash[i] += 1;
147 }
148 }
149 if (fCharToGlyphHash.get() != NULL) {
150 for (int i = 0; i < kHashCount; ++i) {
151 if (fCharToGlyphHash[i].fGlyphIndex >= SkToU16(insertion_index)) {
152 fCharToGlyphHash[i].fGlyphIndex += 1;
153 }
154 }
155 }
156}
157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158///////////////////////////////////////////////////////////////////////////////
159
160#ifdef SK_DEBUG
reed@android.comf2b98d62010-12-20 18:26:13 +0000161#define VALIDATE() AutoValidate av(this)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162#else
163#define VALIDATE()
164#endif
165
166uint16_t SkGlyphCache::unicharToGlyph(SkUnichar charCode) {
167 VALIDATE();
168 uint32_t id = SkGlyph::MakeID(charCode);
herbf8dd0762015-01-28 14:12:12 -0800169 const CharGlyphRec& rec = *this->getCharGlyphRec(id);
reed@google.com22a02212011-03-01 21:33:48 +0000170
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171 if (rec.fID == id) {
herbe70de9e2015-02-27 07:22:48 -0800172 return fGlyphArray[rec.fGlyphIndex].getGlyphID();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 } else {
174 return fScalerContext->charToGlyphID(charCode);
175 }
176}
177
reed@android.com9d3a9852010-01-08 14:07:42 +0000178SkUnichar SkGlyphCache::glyphToUnichar(uint16_t glyphID) {
179 return fScalerContext->glyphIDToChar(glyphID);
180}
181
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000182unsigned SkGlyphCache::getGlyphCount() {
183 return fScalerContext->getGlyphCount();
184}
185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186///////////////////////////////////////////////////////////////////////////////
187
188const SkGlyph& SkGlyphCache::getUnicharAdvance(SkUnichar charCode) {
189 VALIDATE();
herbe70de9e2015-02-27 07:22:48 -0800190 return *this->lookupByChar(charCode, kJustAdvance_MetricsType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191}
192
193const SkGlyph& SkGlyphCache::getGlyphIDAdvance(uint16_t glyphID) {
194 VALIDATE();
195 uint32_t id = SkGlyph::MakeID(glyphID);
herbe70de9e2015-02-27 07:22:48 -0800196 return *this->lookupByCombinedID(id, kJustAdvance_MetricsType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197}
198
199///////////////////////////////////////////////////////////////////////////////
200
201const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode) {
202 VALIDATE();
herbe70de9e2015-02-27 07:22:48 -0800203 return *this->lookupByChar(charCode, kFull_MetricsType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204}
205
206const SkGlyph& SkGlyphCache::getUnicharMetrics(SkUnichar charCode,
207 SkFixed x, SkFixed y) {
208 VALIDATE();
herbe70de9e2015-02-27 07:22:48 -0800209 return *this->lookupByChar(charCode, kFull_MetricsType, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210}
211
212const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID) {
213 VALIDATE();
214 uint32_t id = SkGlyph::MakeID(glyphID);
herbe70de9e2015-02-27 07:22:48 -0800215 return *this->lookupByCombinedID(id, kFull_MetricsType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216}
217
reed40dab982015-01-28 13:28:53 -0800218const SkGlyph& SkGlyphCache::getGlyphIDMetrics(uint16_t glyphID, SkFixed x, SkFixed y) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 VALIDATE();
220 uint32_t id = SkGlyph::MakeID(glyphID, x, y);
herbe70de9e2015-02-27 07:22:48 -0800221 return *this->lookupByCombinedID(id, kFull_MetricsType);
222}
mtklein5a2a5e72015-02-09 07:52:51 -0800223
herbe70de9e2015-02-27 07:22:48 -0800224SkGlyph* SkGlyphCache::lookupByChar(SkUnichar charCode, MetricsType type, SkFixed x, SkFixed y) {
225 uint32_t id = SkGlyph::MakeID(charCode, x, y);
226 CharGlyphRec* rec = this->getCharGlyphRec(id);
227 SkGlyph* glyph;
228 if (rec->fID != id) {
herbc1e97b32015-03-05 11:51:11 -0800229 RecordHashCollisionIf(glyph_index != SkGlyph::kImpossibleID);
herbe70de9e2015-02-27 07:22:48 -0800230 // this ID is based on the UniChar
231 rec->fID = id;
232 // this ID is based on the glyph index
233 id = SkGlyph::MakeID(fScalerContext->charToGlyphID(charCode), x, y);
234 rec->fGlyphIndex = this->lookupMetrics(id, type);
235 glyph = &fGlyphArray[rec->fGlyphIndex];
mtklein5a2a5e72015-02-09 07:52:51 -0800236 } else {
237 RecordHashSuccess();
herbe70de9e2015-02-27 07:22:48 -0800238 glyph = &fGlyphArray[rec->fGlyphIndex];
239 if (type == kFull_MetricsType && glyph->isJustAdvance()) {
mtklein5a2a5e72015-02-09 07:52:51 -0800240 fScalerContext->getMetrics(glyph);
241 }
242 }
herbe70de9e2015-02-27 07:22:48 -0800243 return glyph;
herbb4c29ac2015-02-09 06:38:28 -0800244}
bsalomon9bf4e5b2015-02-02 21:06:23 -0800245
herbe70de9e2015-02-27 07:22:48 -0800246SkGlyph* SkGlyphCache::lookupByCombinedID(uint32_t id, MetricsType type) {
247 uint32_t hash_index = ID2HashIndex(id);
248 uint16_t glyph_index = fGlyphHash[hash_index];
249 SkGlyph* glyph = &fGlyphArray[glyph_index];
herbc1e97b32015-03-05 11:51:11 -0800250
herbe70de9e2015-02-27 07:22:48 -0800251 if (glyph->fID != id) {
herbc1e97b32015-03-05 11:51:11 -0800252 RecordHashCollisionIf(glyph_index != SkGlyph::kImpossibleID);
herbe70de9e2015-02-27 07:22:48 -0800253 glyph_index = this->lookupMetrics(id, type);
254 fGlyphHash[hash_index] = glyph_index;
255 glyph = &fGlyphArray[glyph_index];
256 } else {
257 RecordHashSuccess();
258 if (type == kFull_MetricsType && glyph->isJustAdvance()) {
259 fScalerContext->getMetrics(glyph);
mtklein5a2a5e72015-02-09 07:52:51 -0800260 }
herbe70de9e2015-02-27 07:22:48 -0800261 }
262 return glyph;
263}
mtklein5a2a5e72015-02-09 07:52:51 -0800264
herbe70de9e2015-02-27 07:22:48 -0800265uint16_t SkGlyphCache::lookupMetrics(uint32_t id, MetricsType mtype) {
herbc1e97b32015-03-05 11:51:11 -0800266 SkASSERT(id != SkGlyph::kImpossibleID);
herbe70de9e2015-02-27 07:22:48 -0800267 // Count is always greater than 0 because of the sentinel.
268 // The fGlyphArray cache is in descending order, so that the sentinel with a value of ~0 is
269 // always at index 0.
270 SkGlyph* gptr = fGlyphArray.begin();
271 int lo = 0;
272 int hi = fGlyphArray.count() - 1;
273 while (lo < hi) {
274 int mid = (hi + lo) >> 1;
275 if (gptr[mid].fID > id) {
276 lo = mid + 1;
277 } else {
278 hi = mid;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 }
280 }
281
herbe70de9e2015-02-27 07:22:48 -0800282 uint16_t glyph_index = hi;
283 SkGlyph* glyph = &gptr[glyph_index];
284 if (glyph->fID == id) {
285 if (kFull_MetricsType == mtype && glyph->isJustAdvance()) {
286 fScalerContext->getMetrics(glyph);
287 }
herbc1e97b32015-03-05 11:51:11 -0800288 SkASSERT(glyph->fID != SkGlyph::kImpossibleID);
herbe70de9e2015-02-27 07:22:48 -0800289 return glyph_index;
290 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
herbe70de9e2015-02-27 07:22:48 -0800292 // check if we need to bump hi before falling though to the allocator
293 if (glyph->fID > id) {
294 glyph_index += 1;
295 }
herbc1e97b32015-03-05 11:51:11 -0800296
herbe70de9e2015-02-27 07:22:48 -0800297 // Not found, but hi contains the index of the insertion point of the new glyph.
298 fMemoryUsed += sizeof(SkGlyph);
herbc1e97b32015-03-05 11:51:11 -0800299
herbe70de9e2015-02-27 07:22:48 -0800300 this->adjustCaches(glyph_index);
301
302 glyph = fGlyphArray.insert(glyph_index);
303 glyph->initGlyphFromCombinedID(id);
reed@google.com22a02212011-03-01 21:33:48 +0000304
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 if (kJustAdvance_MetricsType == mtype) {
306 fScalerContext->getAdvance(glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 } else {
308 SkASSERT(kFull_MetricsType == mtype);
309 fScalerContext->getMetrics(glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 }
311
herbc1e97b32015-03-05 11:51:11 -0800312 SkASSERT(glyph->fID != SkGlyph::kImpossibleID);
herbe70de9e2015-02-27 07:22:48 -0800313 return glyph_index;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314}
315
316const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
scarybeasts@gmail.com17f694b2010-10-18 23:29:36 +0000317 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) {
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000318 if (NULL == glyph.fImage) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 size_t size = glyph.computeImageSize();
reed@google.com6757a3c2013-06-19 19:25:36 +0000320 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 SkChunkAlloc::kReturnNil_AllocFailType);
reed@google.com22a02212011-03-01 21:33:48 +0000322 // check that alloc() actually succeeded
bsalomon49f085d2014-09-05 13:34:00 -0700323 if (glyph.fImage) {
reed@google.com22a02212011-03-01 21:33:48 +0000324 fScalerContext->getImage(glyph);
reed@google.com5e2df642011-09-21 18:42:09 +0000325 // TODO: the scaler may have changed the maskformat during
326 // getImage (e.g. from AA or LCD to BW) which means we may have
327 // overallocated the buffer. Check if the new computedImageSize
328 // is smaller, and if so, strink the alloc size in fImageAlloc.
reed@google.com22a02212011-03-01 21:33:48 +0000329 fMemoryUsed += size;
330 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 }
332 }
333 return glyph.fImage;
334}
335
336const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
337 if (glyph.fWidth) {
338 if (glyph.fPath == NULL) {
339 const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
340 fScalerContext->getPath(glyph, glyph.fPath);
341 fMemoryUsed += sizeof(SkPath) +
bsalomon@google.comdf9d6562012-06-07 21:43:15 +0000342 glyph.fPath->countPoints() * sizeof(SkPoint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 }
344 }
345 return glyph.fPath;
346}
347
reed40dab982015-01-28 13:28:53 -0800348void SkGlyphCache::dump() const {
349 const SkTypeface* face = fScalerContext->getTypeface();
350 const SkScalerContextRec& rec = fScalerContext->getRec();
351 SkMatrix matrix;
352 rec.getSingleMatrix(&matrix);
353 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
354 SkString name;
355 face->getFamilyName(&name);
356
357 SkString msg;
358 msg.printf("cache typeface:%x %25s:%d size:%2g [%g %g %g %g] lum:%02X devG:%d pntG:%d cntr:%d glyphs:%3d",
359 face->uniqueID(), name.c_str(), face->style(), rec.fTextSize,
360 matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewX],
361 matrix[SkMatrix::kMSkewY], matrix[SkMatrix::kMScaleY],
362 rec.fLumBits & 0xFF, rec.fDeviceGamma, rec.fPaintGamma, rec.fContrast,
363 fGlyphArray.count());
364#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
365 const int sum = SkTMax(fHashHitCount + fHashMissCount, 1); // avoid divide-by-zero
366 msg.appendf(" hash:%2d\n", 100 * fHashHitCount / sum);
367#endif
368 SkDebugf("%s\n", msg.c_str());
369}
370
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371///////////////////////////////////////////////////////////////////////////////
372
373bool SkGlyphCache::getAuxProcData(void (*proc)(void*), void** dataPtr) const {
374 const AuxProcRec* rec = fAuxProcList;
375 while (rec) {
376 if (rec->fProc == proc) {
377 if (dataPtr) {
378 *dataPtr = rec->fData;
379 }
380 return true;
381 }
382 rec = rec->fNext;
383 }
384 return false;
385}
386
387void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) {
388 if (proc == NULL) {
389 return;
390 }
391
392 AuxProcRec* rec = fAuxProcList;
393 while (rec) {
394 if (rec->fProc == proc) {
395 rec->fData = data;
396 return;
397 }
398 rec = rec->fNext;
399 }
400 // not found, create a new rec
401 rec = SkNEW(AuxProcRec);
402 rec->fProc = proc;
403 rec->fData = data;
404 rec->fNext = fAuxProcList;
405 fAuxProcList = rec;
406}
407
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408void SkGlyphCache::invokeAndRemoveAuxProcs() {
409 AuxProcRec* rec = fAuxProcList;
410 while (rec) {
411 rec->fProc(rec->fData);
412 AuxProcRec* next = rec->fNext;
413 SkDELETE(rec);
414 rec = next;
415 }
416}
417
418///////////////////////////////////////////////////////////////////////////////
419///////////////////////////////////////////////////////////////////////////////
420
reed@google.combaed71f2013-09-26 19:28:27 +0000421size_t SkGlyphCache_Globals::setCacheSizeLimit(size_t newLimit) {
reed@google.com09837012012-04-23 15:04:44 +0000422 static const size_t minLimit = 256 * 1024;
423 if (newLimit < minLimit) {
424 newLimit = minLimit;
425 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000426
reed@google.combaed71f2013-09-26 19:28:27 +0000427 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000428
reed@google.combaed71f2013-09-26 19:28:27 +0000429 size_t prevLimit = fCacheSizeLimit;
430 fCacheSizeLimit = newLimit;
431 this->internalPurge();
reed@google.com09837012012-04-23 15:04:44 +0000432 return prevLimit;
433}
434
reed@google.combaed71f2013-09-26 19:28:27 +0000435int SkGlyphCache_Globals::setCacheCountLimit(int newCount) {
436 if (newCount < 0) {
437 newCount = 0;
438 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000439
reed@google.combaed71f2013-09-26 19:28:27 +0000440 SkAutoMutexAcquire ac(fMutex);
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000441
reed@google.combaed71f2013-09-26 19:28:27 +0000442 int prevCount = fCacheCountLimit;
443 fCacheCountLimit = newCount;
444 this->internalPurge();
445 return prevCount;
446}
447
reed@google.com26344cf2012-06-27 18:23:01 +0000448void SkGlyphCache_Globals::purgeAll() {
449 SkAutoMutexAcquire ac(fMutex);
reed@google.combaed71f2013-09-26 19:28:27 +0000450 this->internalPurge(fTotalMemoryUsed);
reed@google.com09837012012-04-23 15:04:44 +0000451}
452
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453/* This guy calls the visitor from within the mutext lock, so the visitor
454 cannot:
455 - take too much time
456 - try to acquire the mutext again
457 - call a fontscaler (which might call into the cache)
458*/
reed@google.com90808e82013-03-19 14:44:54 +0000459SkGlyphCache* SkGlyphCache::VisitCache(SkTypeface* typeface,
460 const SkDescriptor* desc,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 bool (*proc)(const SkGlyphCache*, void*),
462 void* context) {
reed@google.com90808e82013-03-19 14:44:54 +0000463 if (!typeface) {
464 typeface = SkTypeface::GetDefaultTypeface();
465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkASSERT(desc);
467
reed@google.com5d248bc2011-11-17 21:49:02 +0000468 SkGlyphCache_Globals& globals = getGlobals();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469 SkAutoMutexAcquire ac(globals.fMutex);
470 SkGlyphCache* cache;
471 bool insideMutex = true;
472
473 globals.validate();
474
reed@google.combaed71f2013-09-26 19:28:27 +0000475 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 if (cache->fDesc->equals(*desc)) {
reed@google.combaed71f2013-09-26 19:28:27 +0000477 globals.internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 goto FOUND_IT;
479 }
480 }
481
482 /* Release the mutex now, before we create a new entry (which might have
483 side-effects like trying to access the cache/mutex (yikes!)
484 */
485 ac.release(); // release the mutex now
486 insideMutex = false; // can't use globals anymore
487
reed@google.com4b60dbe2013-07-09 15:29:32 +0000488 // Check if we can create a scaler-context before creating the glyphcache.
489 // If not, we may have exhausted OS/font resources, so try purging the
490 // cache once and try again.
491 {
reed@google.com84e22d82013-07-10 15:38:20 +0000492 // pass true the first time, to notice if the scalercontext failed,
493 // so we can try the purge.
494 SkScalerContext* ctx = typeface->createScalerContext(desc, true);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000495 if (!ctx) {
496 getSharedGlobals().purgeAll();
reed@google.com84e22d82013-07-10 15:38:20 +0000497 ctx = typeface->createScalerContext(desc, false);
498 SkASSERT(ctx);
reed@google.com4b60dbe2013-07-09 15:29:32 +0000499 }
500 cache = SkNEW_ARGS(SkGlyphCache, (typeface, desc, ctx));
501 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
503FOUND_IT:
reed@android.comf2b98d62010-12-20 18:26:13 +0000504
505 AutoValidate av(cache);
506
reed@google.combaed71f2013-09-26 19:28:27 +0000507 if (!proc(cache, context)) { // need to reattach
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 if (insideMutex) {
reed@google.combaed71f2013-09-26 19:28:27 +0000509 globals.internalAttachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000511 globals.attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 }
513 cache = NULL;
514 }
515 return cache;
516}
517
518void SkGlyphCache::AttachCache(SkGlyphCache* cache) {
519 SkASSERT(cache);
520 SkASSERT(cache->fNext == NULL);
521
reed@google.combaed71f2013-09-26 19:28:27 +0000522 getGlobals().attachCacheToHead(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523}
524
reed40dab982015-01-28 13:28:53 -0800525void SkGlyphCache::Dump() {
526 SkGlyphCache_Globals& globals = getGlobals();
527 SkAutoMutexAcquire ac(globals.fMutex);
528 SkGlyphCache* cache;
529
530 globals.validate();
531
532 SkDebugf("SkGlyphCache strikes:%d memory:%d\n",
533 globals.getCacheCountUsed(), (int)globals.getTotalMemoryUsed());
534
535#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
536 int hitCount = 0;
537 int missCount = 0;
538#endif
539
540 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext) {
541#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
542 hitCount += cache->fHashHitCount;
543 missCount += cache->fHashMissCount;
544#endif
545 cache->dump();
546 }
547#ifdef SK_GLYPHCACHE_TRACK_HASH_STATS
548 SkDebugf("Hash hit percent:%2d\n", 100 * hitCount / (hitCount + missCount));
549#endif
550}
551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552///////////////////////////////////////////////////////////////////////////////
553
reed@google.combaed71f2013-09-26 19:28:27 +0000554void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) {
555 SkAutoMutexAcquire ac(fMutex);
556
557 this->validate();
558 cache->validate();
559
560 this->internalAttachCacheToHead(cache);
561 this->internalPurge();
562}
563
564SkGlyphCache* SkGlyphCache_Globals::internalGetTail() const {
565 SkGlyphCache* cache = fHead;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 if (cache) {
567 while (cache->fNext) {
568 cache = cache->fNext;
569 }
570 }
571 return cache;
572}
573
reed@google.combaed71f2013-09-26 19:28:27 +0000574size_t SkGlyphCache_Globals::internalPurge(size_t minBytesNeeded) {
575 this->validate();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000576
reed@google.combaed71f2013-09-26 19:28:27 +0000577 size_t bytesNeeded = 0;
578 if (fTotalMemoryUsed > fCacheSizeLimit) {
579 bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
580 }
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000581 bytesNeeded = SkTMax(bytesNeeded, minBytesNeeded);
reed@google.combaed71f2013-09-26 19:28:27 +0000582 if (bytesNeeded) {
583 // no small purges!
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +0000584 bytesNeeded = SkTMax(bytesNeeded, fTotalMemoryUsed >> 2);
reed@google.comeb9a9bf2012-04-23 13:43:30 +0000585 }
586
reed@google.combaed71f2013-09-26 19:28:27 +0000587 int countNeeded = 0;
588 if (fCacheCount > fCacheCountLimit) {
589 countNeeded = fCacheCount - fCacheCountLimit;
590 // no small purges!
591 countNeeded = SkMax32(countNeeded, fCacheCount >> 2);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593
reed@google.combaed71f2013-09-26 19:28:27 +0000594 // early exit
595 if (!countNeeded && !bytesNeeded) {
596 return 0;
597 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598
599 size_t bytesFreed = 0;
reed@google.combaed71f2013-09-26 19:28:27 +0000600 int countFreed = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601
reed@google.combaed71f2013-09-26 19:28:27 +0000602 // we start at the tail and proceed backwards, as the linklist is in LRU
603 // order, with unimportant entries at the tail.
604 SkGlyphCache* cache = this->internalGetTail();
605 while (cache != NULL &&
606 (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkGlyphCache* prev = cache->fPrev;
608 bytesFreed += cache->fMemoryUsed;
reed@google.combaed71f2013-09-26 19:28:27 +0000609 countFreed += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610
reed@google.combaed71f2013-09-26 19:28:27 +0000611 this->internalDetachCache(cache);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 SkDELETE(cache);
613 cache = prev;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 }
615
reed@google.combaed71f2013-09-26 19:28:27 +0000616 this->validate();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617
618#ifdef SPEW_PURGE_STATUS
commit-bot@chromium.org261d0152014-05-12 15:48:38 +0000619 if (countFreed) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 SkDebugf("purging %dK from font cache [%d entries]\n",
reed@google.combaed71f2013-09-26 19:28:27 +0000621 (int)(bytesFreed >> 10), countFreed);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 }
623#endif
624
625 return bytesFreed;
626}
627
reed@google.combaed71f2013-09-26 19:28:27 +0000628void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) {
629 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext);
630 if (fHead) {
631 fHead->fPrev = cache;
632 cache->fNext = fHead;
633 }
634 fHead = cache;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000635
reed@google.combaed71f2013-09-26 19:28:27 +0000636 fCacheCount += 1;
637 fTotalMemoryUsed += cache->fMemoryUsed;
638}
639
640void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) {
641 SkASSERT(fCacheCount > 0);
642 fCacheCount -= 1;
643 fTotalMemoryUsed -= cache->fMemoryUsed;
644
645 if (cache->fPrev) {
646 cache->fPrev->fNext = cache->fNext;
647 } else {
648 fHead = cache->fNext;
649 }
650 if (cache->fNext) {
651 cache->fNext->fPrev = cache->fPrev;
652 }
653 cache->fPrev = cache->fNext = NULL;
654}
655
reed@android.comf2b98d62010-12-20 18:26:13 +0000656///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000657
reed@google.com09837012012-04-23 15:04:44 +0000658#ifdef SK_DEBUG
reed@google.combaed71f2013-09-26 19:28:27 +0000659
reed@android.comf2b98d62010-12-20 18:26:13 +0000660void SkGlyphCache::validate() const {
djsollen@google.com000dea72012-11-30 16:19:32 +0000661#ifdef SK_DEBUG_GLYPH_CACHE
reed@android.comf2b98d62010-12-20 18:26:13 +0000662 int count = fGlyphArray.count();
663 for (int i = 0; i < count; i++) {
herbc2ff5292015-03-06 16:15:23 -0800664 const SkGlyph* glyph = &fGlyphArray[i];
reed@android.comf2b98d62010-12-20 18:26:13 +0000665 SkASSERT(glyph);
reed@android.comf2b98d62010-12-20 18:26:13 +0000666 if (glyph->fImage) {
reed@google.com6757a3c2013-06-19 19:25:36 +0000667 SkASSERT(fGlyphAlloc.contains(glyph->fImage));
reed@android.comf2b98d62010-12-20 18:26:13 +0000668 }
669 }
djsollen@google.com000dea72012-11-30 16:19:32 +0000670#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000671}
reed@google.combaed71f2013-09-26 19:28:27 +0000672
673void SkGlyphCache_Globals::validate() const {
674 size_t computedBytes = 0;
675 int computedCount = 0;
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000676
reed@google.combaed71f2013-09-26 19:28:27 +0000677 const SkGlyphCache* head = fHead;
678 while (head != NULL) {
679 computedBytes += head->fMemoryUsed;
680 computedCount += 1;
681 head = head->fNext;
682 }
skia.committer@gmail.com65caeaf2013-09-27 07:01:29 +0000683
reed@google.combaed71f2013-09-26 19:28:27 +0000684 SkASSERT(fTotalMemoryUsed == computedBytes);
685 SkASSERT(fCacheCount == computedCount);
686}
687
reed@android.comf2b98d62010-12-20 18:26:13 +0000688#endif
reed@google.com09837012012-04-23 15:04:44 +0000689
690///////////////////////////////////////////////////////////////////////////////
691///////////////////////////////////////////////////////////////////////////////
692
693#include "SkTypefaceCache.h"
694
695size_t SkGraphics::GetFontCacheLimit() {
reed@google.combaed71f2013-09-26 19:28:27 +0000696 return getSharedGlobals().getCacheSizeLimit();
reed@google.com09837012012-04-23 15:04:44 +0000697}
698
699size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
reed@google.combaed71f2013-09-26 19:28:27 +0000700 return getSharedGlobals().setCacheSizeLimit(bytes);
reed@google.com09837012012-04-23 15:04:44 +0000701}
702
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000703size_t SkGraphics::GetFontCacheUsed() {
reed@google.combaed71f2013-09-26 19:28:27 +0000704 return getSharedGlobals().getTotalMemoryUsed();
705}
706
707int SkGraphics::GetFontCacheCountLimit() {
708 return getSharedGlobals().getCacheCountLimit();
709}
710
711int SkGraphics::SetFontCacheCountLimit(int count) {
712 return getSharedGlobals().setCacheCountLimit(count);
713}
714
715int SkGraphics::GetFontCacheCountUsed() {
716 return getSharedGlobals().getCacheCountUsed();
reed@google.com79a1c34ee2012-07-30 13:08:01 +0000717}
718
reed@google.com09837012012-04-23 15:04:44 +0000719void SkGraphics::PurgeFontCache() {
reed@google.com26344cf2012-06-27 18:23:01 +0000720 getSharedGlobals().purgeAll();
reed@google.com09837012012-04-23 15:04:44 +0000721 SkTypefaceCache::PurgeAll();
722}
723
reed@google.com6172d672012-05-17 13:38:03 +0000724size_t SkGraphics::GetTLSFontCacheLimit() {
725 const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
reed@google.combaed71f2013-09-26 19:28:27 +0000726 return tls ? tls->getCacheSizeLimit() : 0;
reed@google.com6172d672012-05-17 13:38:03 +0000727}
728
reed@google.com803c67d2012-05-17 13:50:36 +0000729void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
reed@google.com6172d672012-05-17 13:38:03 +0000730 if (0 == bytes) {
731 SkGlyphCache_Globals::DeleteTLS();
732 } else {
reed@google.combaed71f2013-09-26 19:28:27 +0000733 SkGlyphCache_Globals::GetTLS().setCacheSizeLimit(bytes);
reed@google.com6172d672012-05-17 13:38:03 +0000734 }
735}