blob: e21fd482757d6ae446eef1e3b767b0bff6e975c0 [file] [log] [blame]
reed@google.comac10a2d2010-12-22 21:39:39 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2010 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@google.comac10a2d2010-12-22 21:39:39 +00006 */
7
reed@google.comac10a2d2010-12-22 21:39:39 +00008#include "GrAtlas.h"
9#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000010#include "GrRectanizer.h"
11#include "GrTextStrike.h"
12#include "GrTextStrike_impl.h"
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +000013#include "SkString.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000014
reed@google.comfa35e3d2012-06-26 20:16:17 +000015SK_DEFINE_INST_COUNT(GrFontScaler)
16SK_DEFINE_INST_COUNT(GrKey)
17
18///////////////////////////////////////////////////////////////////////////////
19
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000020#define FONT_CACHE_STATS 0
21#if FONT_CACHE_STATS
22static int g_PurgeCount = 0;
23#endif
24
reed@google.comac10a2d2010-12-22 21:39:39 +000025GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
26 gpu->ref();
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000027 for (int i = 0; i < kMaskFormatCount; ++i) {
28 fAtlasMgr[i] = NULL;
29 }
reed@google.comac10a2d2010-12-22 21:39:39 +000030
31 fHead = fTail = NULL;
32}
33
34GrFontCache::~GrFontCache() {
35 fCache.deleteAll();
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000036 for (int i = 0; i < kMaskFormatCount; ++i) {
37 delete fAtlasMgr[i];
38 }
reed@google.comac10a2d2010-12-22 21:39:39 +000039 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000040#if FONT_CACHE_STATS
41 GrPrintf("Num purges: %d\n", g_PurgeCount);
42#endif
reed@google.comac10a2d2010-12-22 21:39:39 +000043}
44
commit-bot@chromium.org95294412013-09-26 15:28:40 +000045static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
46 switch (format) {
47 case kA8_GrMaskFormat:
48 return kAlpha_8_GrPixelConfig;
49 case kA565_GrMaskFormat:
50 return kRGB_565_GrPixelConfig;
51 case kA888_GrMaskFormat:
52 return kSkia8888_GrPixelConfig;
53 default:
54 SkDEBUGFAIL("unknown maskformat");
55 }
56 return kUnknown_GrPixelConfig;
57}
58
reed@google.comac10a2d2010-12-22 21:39:39 +000059GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
60 const Key& key) {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000061 GrMaskFormat format = scaler->getMaskFormat();
commit-bot@chromium.org95294412013-09-26 15:28:40 +000062 GrPixelConfig config = mask_format_to_pixel_config(format);
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000063 if (NULL == fAtlasMgr[format]) {
commit-bot@chromium.org95294412013-09-26 15:28:40 +000064 fAtlasMgr[format] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config));
reed@google.comac10a2d2010-12-22 21:39:39 +000065 }
tomhudson@google.comc377baf2012-07-09 20:17:56 +000066 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
commit-bot@chromium.org95294412013-09-26 15:28:40 +000067 (this, scaler->getKey(), format, fAtlasMgr[format]));
reed@google.comac10a2d2010-12-22 21:39:39 +000068 fCache.insert(key, strike);
69
70 if (fHead) {
71 fHead->fPrev = strike;
72 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000073 SkASSERT(NULL == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +000074 fTail = strike;
75 }
76 strike->fPrev = NULL;
77 strike->fNext = fHead;
78 fHead = strike;
79
80 return strike;
81}
82
reed@google.comac10a2d2010-12-22 21:39:39 +000083void GrFontCache::freeAll() {
84 fCache.deleteAll();
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000085 for (int i = 0; i < kMaskFormatCount; ++i) {
86 delete fAtlasMgr[i];
87 fAtlasMgr[i] = NULL;
88 }
bsalomon@google.com8fe72472011-03-30 21:26:44 +000089 fHead = NULL;
90 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000091}
92
93void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
jvanverth@google.combbe55fd2013-09-16 20:28:37 +000094 SkASSERT(NULL != preserveStrike);
reed@google.comac10a2d2010-12-22 21:39:39 +000095 GrTextStrike* strike = fTail;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000096 bool purge = true;
jvanverth@google.combbe55fd2013-09-16 20:28:37 +000097 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
bsalomon@google.com7359eae2011-06-21 21:18:25 +000098 while (strike) {
jvanverth@google.combbe55fd2013-09-16 20:28:37 +000099 if (strike == preserveStrike || maskFormat != strike->fMaskFormat) {
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000100 strike = strike->fPrev;
101 continue;
102 }
103 GrTextStrike* strikeToPurge = strike;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000104 strike = strikeToPurge->fPrev;
105 if (purge) {
106 // keep purging if we won't free up any atlases with this strike.
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000107 purge = strikeToPurge->fAtlas.isEmpty();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000108 int index = fCache.slowFindIndex(strikeToPurge);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000109 SkASSERT(index >= 0);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000110 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
111 this->detachStrikeFromList(strikeToPurge);
112 delete strikeToPurge;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000113 }
114 }
115#if FONT_CACHE_STATS
116 ++g_PurgeCount;
117#endif
118}
119
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000120void GrFontCache::freePlotExceptFor(GrTextStrike* preserveStrike) {
jvanverth@google.combbe55fd2013-09-16 20:28:37 +0000121 SkASSERT(NULL != preserveStrike);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000122 GrTextStrike* strike = fTail;
jvanverth@google.combbe55fd2013-09-16 20:28:37 +0000123 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000124 while (strike) {
jvanverth@google.combbe55fd2013-09-16 20:28:37 +0000125 if (strike == preserveStrike || maskFormat != strike->fMaskFormat) {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000126 strike = strike->fPrev;
127 continue;
128 }
129 GrTextStrike* strikeToPurge = strike;
130 strike = strikeToPurge->fPrev;
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000131 if (strikeToPurge->removeUnusedPlots()) {
132 if (strikeToPurge->fAtlas.isEmpty()) {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000133 int index = fCache.slowFindIndex(strikeToPurge);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000134 SkASSERT(index >= 0);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000135 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
136 this->detachStrikeFromList(strikeToPurge);
137 delete strikeToPurge;
138 }
139 break;
140 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000141 }
142}
143
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000144#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000145void GrFontCache::validate() const {
146 int count = fCache.count();
147 if (0 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000148 SkASSERT(!fHead);
149 SkASSERT(!fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000150 } else if (1 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000151 SkASSERT(fHead == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000152 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000153 SkASSERT(fHead != fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000154 }
155
156 int count2 = 0;
157 const GrTextStrike* strike = fHead;
158 while (strike) {
159 count2 += 1;
160 strike = strike->fNext;
161 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000162 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000163
164 count2 = 0;
165 strike = fTail;
166 while (strike) {
167 count2 += 1;
168 strike = strike->fPrev;
169 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000170 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000171}
172#endif
173
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000174#ifdef SK_DEVELOPER
175void GrFontCache::dump() const {
176 static int gDumpCount = 0;
177 for (int i = 0; i < kMaskFormatCount; ++i) {
178 if (NULL != fAtlasMgr[i]) {
179 GrTexture* texture = fAtlasMgr[i]->getTexture();
180 if (NULL != texture) {
181 SkString filename;
182 filename.printf("fontcache_%d%d.png", gDumpCount, i);
183 texture->savePixels(filename.c_str());
184 }
185 }
186 }
187 ++gDumpCount;
188}
189#endif
190
reed@google.comac10a2d2010-12-22 21:39:39 +0000191///////////////////////////////////////////////////////////////////////////////
192
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000193#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000194 static int gCounter;
195#endif
196
197/*
198 The text strike is specific to a given font/style/matrix setup, which is
199 represented by the GrHostFontScaler object we are given in getGlyph().
200
201 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
202 atlas and a position within that texture.
203 */
204
205GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000206 GrMaskFormat format,
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000207 GrAtlasMgr* atlasMgr) : fPool(64), fAtlas(atlasMgr) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000208 fFontScalerKey = key;
209 fFontScalerKey->ref();
210
211 fFontCache = cache; // no need to ref, it won't go away before we do
212 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
reed@google.comac10a2d2010-12-22 21:39:39 +0000213
reed@google.com98539c62011-03-15 15:40:16 +0000214 fMaskFormat = format;
215
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000216#ifdef SK_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000217// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000218 gCounter += 1;
219#endif
220}
221
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000222// these signatures are needed because they're used with
223// SkTDArray::visitAll() (see destructor & removeUnusedAtlases())
224static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
225
226static void invalidate_glyph(GrGlyph*& glyph) {
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000227 if (glyph->fPlot && glyph->fPlot->drawToken().isIssued()) {
228 glyph->fPlot = NULL;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000229 }
230}
reed@google.comac10a2d2010-12-22 21:39:39 +0000231
232GrTextStrike::~GrTextStrike() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000233 fFontScalerKey->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000234 fCache.getArray().visitAll(free_glyph);
reed@google.comac10a2d2010-12-22 21:39:39 +0000235
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000236#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000237 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000238// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000239#endif
240}
241
242GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
243 GrFontScaler* scaler) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000244 SkIRect bounds;
reed@google.comac10a2d2010-12-22 21:39:39 +0000245 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
246 return NULL;
247 }
248
249 GrGlyph* glyph = fPool.alloc();
250 glyph->init(packed, bounds);
251 fCache.insert(packed, glyph);
252 return glyph;
253}
254
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000255bool GrTextStrike::removeUnusedPlots() {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000256 fCache.getArray().visitAll(invalidate_glyph);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000257 return fAtlasMgr->removeUnusedPlots(&fAtlas);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000258}
259
commit-bot@chromium.org49e80832013-10-07 18:20:27 +0000260bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000261#if 0 // testing hack to force us to flush our cache often
262 static int gCounter;
263 if ((++gCounter % 10) == 0) return false;
264#endif
265
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000266 SkASSERT(glyph);
267 SkASSERT(scaler);
268 SkASSERT(fCache.contains(glyph));
commit-bot@chromium.org49e80832013-10-07 18:20:27 +0000269 SkASSERT(NULL == glyph->fPlot);
reed@google.comac10a2d2010-12-22 21:39:39 +0000270
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000271 SkAutoRef ar(scaler);
reed@google.com98539c62011-03-15 15:40:16 +0000272
273 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
274 size_t size = glyph->fBounds.area() * bytesPerPixel;
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000275 SkAutoSMalloc<1024> storage(size);
reed@google.comac10a2d2010-12-22 21:39:39 +0000276 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
reed@google.com98539c62011-03-15 15:40:16 +0000277 glyph->height(),
278 glyph->width() * bytesPerPixel,
reed@google.comac10a2d2010-12-22 21:39:39 +0000279 storage.get())) {
280 return false;
281 }
282
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000283 GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
284 glyph->height(), storage.get(),
285 &glyph->fAtlasLocation);
286 if (NULL == plot) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000287 return false;
288 }
289
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000290 glyph->fPlot = plot;
reed@google.comac10a2d2010-12-22 21:39:39 +0000291 return true;
292}