blob: 3e83e6c950ba6927699b17e462f9f4fde33ef35d [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
jvanverth787cdf92014-12-04 10:46:50 -08008#include "GrFontCache.h"
reed@google.comac10a2d2010-12-22 21:39:39 +00009#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000010#include "GrRectanizer.h"
bsalomonafbf2d62014-09-30 12:18:44 -070011#include "GrSurfacePriv.h"
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +000012#include "SkString.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000013
commit-bot@chromium.org8065ec52014-03-11 15:57:40 +000014#include "SkDistanceFieldGen.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000015
reed@google.comfa35e3d2012-06-26 20:16:17 +000016///////////////////////////////////////////////////////////////////////////////
17
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000018#define GR_ATLAS_TEXTURE_WIDTH 1024
19#define GR_ATLAS_TEXTURE_HEIGHT 2048
20
21#define GR_PLOT_WIDTH 256
22#define GR_PLOT_HEIGHT 256
23
24#define GR_NUM_PLOTS_X (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
25#define GR_NUM_PLOTS_Y (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
26
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000027#define FONT_CACHE_STATS 0
28#if FONT_CACHE_STATS
29static int g_PurgeCount = 0;
30#endif
31
reed@google.comac10a2d2010-12-22 21:39:39 +000032GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
33 gpu->ref();
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000034 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -070035 fAtlases[i] = NULL;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000036 }
reed@google.comac10a2d2010-12-22 21:39:39 +000037
38 fHead = fTail = NULL;
39}
40
41GrFontCache::~GrFontCache() {
jvanverthdd6d2272014-07-22 13:25:26 -070042 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
43 while (!iter.done()) {
44 SkDELETE(&(*iter));
45 ++iter;
46 }
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000047 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -070048 delete fAtlases[i];
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000049 }
reed@google.comac10a2d2010-12-22 21:39:39 +000050 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000051#if FONT_CACHE_STATS
tfarina38406c82014-10-31 07:11:12 -070052 SkDebugf("Num purges: %d\n", g_PurgeCount);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000053#endif
reed@google.comac10a2d2010-12-22 21:39:39 +000054}
55
commit-bot@chromium.org95294412013-09-26 15:28:40 +000056static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000057 static const GrPixelConfig sPixelConfigs[] = {
58 kAlpha_8_GrPixelConfig,
59 kRGB_565_GrPixelConfig,
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000060 kSkia8888_GrPixelConfig
61 };
62 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
63
64 return sPixelConfigs[format];
65}
66
67static int mask_format_to_atlas_index(GrMaskFormat format) {
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000068 static const int sAtlasIndices[] = {
69 GrFontCache::kA8_AtlasType,
70 GrFontCache::k565_AtlasType,
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000071 GrFontCache::k8888_AtlasType
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000072 };
73 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
74
75 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
76 return sAtlasIndices[format];
commit-bot@chromium.org95294412013-09-26 15:28:40 +000077}
78
jvanverthdd6d2272014-07-22 13:25:26 -070079GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
jvanverth294c3262014-10-10 11:36:12 -070080 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
jvanverthdd6d2272014-07-22 13:25:26 -070081 fCache.add(strike);
reed@google.comac10a2d2010-12-22 21:39:39 +000082
83 if (fHead) {
84 fHead->fPrev = strike;
85 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000086 SkASSERT(NULL == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +000087 fTail = strike;
88 }
89 strike->fPrev = NULL;
90 strike->fNext = fHead;
91 fHead = strike;
92
93 return strike;
94}
95
reed@google.comac10a2d2010-12-22 21:39:39 +000096void GrFontCache::freeAll() {
jvanverthdd6d2272014-07-22 13:25:26 -070097 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
98 while (!iter.done()) {
99 SkDELETE(&(*iter));
100 ++iter;
101 }
102 fCache.rewind();
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000103 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -0700104 delete fAtlases[i];
105 fAtlases[i] = NULL;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000106 }
bsalomon@google.com8fe72472011-03-30 21:26:44 +0000107 fHead = NULL;
108 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000109}
110
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000111void GrFontCache::purgeStrike(GrTextStrike* strike) {
jvanverthdd6d2272014-07-22 13:25:26 -0700112 fCache.remove(*(strike->fFontScalerKey));
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000113 this->detachStrikeFromList(strike);
114 delete strike;
115}
116
jvanverth294c3262014-10-10 11:36:12 -0700117
118GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
119 int width, int height, const void* image,
120 SkIPoint16* loc) {
121 GrPixelConfig config = mask_format_to_pixel_config(format);
122 int atlasIndex = mask_format_to_atlas_index(format);
123 if (NULL == fAtlases[atlasIndex]) {
124 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
125 GR_ATLAS_TEXTURE_HEIGHT);
bsalomonf2703d82014-10-28 14:33:06 -0700126 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
jvanverth294c3262014-10-10 11:36:12 -0700127 textureSize,
128 GR_NUM_PLOTS_X,
129 GR_NUM_PLOTS_Y,
130 true));
131 }
132 return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
133}
134
135
136bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
bsalomon49f085d2014-09-05 13:34:00 -0700137 SkASSERT(preserveStrike);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000138
jvanverth294c3262014-10-10 11:36:12 -0700139 int index = mask_format_to_atlas_index(glyph->fMaskFormat);
140 GrAtlas* atlas = fAtlases[index];
robertphillips1d86ee82014-06-24 15:08:49 -0700141 GrPlot* plot = atlas->getUnusedPlot();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000142 if (NULL == plot) {
143 return false;
144 }
145 plot->resetRects();
146
147 GrTextStrike* strike = fHead;
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000148 while (strike) {
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000149 GrTextStrike* strikeToPurge = strike;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000150 strike = strikeToPurge->fNext;
151 strikeToPurge->removePlot(plot);
152
153 // clear out any empty strikes (except this one)
robertphillips1d86ee82014-06-24 15:08:49 -0700154 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000155 this->purgeStrike(strikeToPurge);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000156 }
157 }
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000158
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000159#if FONT_CACHE_STATS
160 ++g_PurgeCount;
161#endif
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000162
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000163 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000164}
165
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000166#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000167void GrFontCache::validate() const {
168 int count = fCache.count();
169 if (0 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000170 SkASSERT(!fHead);
171 SkASSERT(!fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000172 } else if (1 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000173 SkASSERT(fHead == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000174 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000175 SkASSERT(fHead != fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000176 }
177
178 int count2 = 0;
179 const GrTextStrike* strike = fHead;
180 while (strike) {
181 count2 += 1;
182 strike = strike->fNext;
183 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000184 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000185
186 count2 = 0;
187 strike = fTail;
188 while (strike) {
189 count2 += 1;
190 strike = strike->fPrev;
191 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000192 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000193}
194#endif
195
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000196void GrFontCache::dump() const {
197 static int gDumpCount = 0;
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000198 for (int i = 0; i < kAtlasCount; ++i) {
bsalomon49f085d2014-09-05 13:34:00 -0700199 if (fAtlases[i]) {
robertphillips1d86ee82014-06-24 15:08:49 -0700200 GrTexture* texture = fAtlases[i]->getTexture();
bsalomon49f085d2014-09-05 13:34:00 -0700201 if (texture) {
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000202 SkString filename;
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000203#ifdef SK_BUILD_FOR_ANDROID
204 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
205#else
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000206 filename.printf("fontcache_%d%d.png", gDumpCount, i);
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000207#endif
bsalomonafbf2d62014-09-30 12:18:44 -0700208 texture->surfacePriv().savePixels(filename.c_str());
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000209 }
210 }
211 }
212 ++gDumpCount;
213}
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000214
reed@google.comac10a2d2010-12-22 21:39:39 +0000215///////////////////////////////////////////////////////////////////////////////
216
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000217#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000218 static int gCounter;
219#endif
220
221/*
222 The text strike is specific to a given font/style/matrix setup, which is
223 represented by the GrHostFontScaler object we are given in getGlyph().
224
225 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
226 atlas and a position within that texture.
227 */
228
mtklein59dba142014-12-12 08:41:23 -0800229GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key)
230 : fPool(9/*start allocations at 512 bytes*/) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 fFontScalerKey = key;
232 fFontScalerKey->ref();
233
234 fFontCache = cache; // no need to ref, it won't go away before we do
reed@google.com98539c62011-03-15 15:40:16 +0000235
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000236#ifdef SK_DEBUG
tfarina38406c82014-10-31 07:11:12 -0700237// SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000238 gCounter += 1;
239#endif
240}
241
reed@google.comac10a2d2010-12-22 21:39:39 +0000242GrTextStrike::~GrTextStrike() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 fFontScalerKey->unref();
jvanverthdd6d2272014-07-22 13:25:26 -0700244 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
245 while (!iter.done()) {
246 (*iter).free();
247 ++iter;
248 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000249
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000250#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000251 gCounter -= 1;
tfarina38406c82014-10-31 07:11:12 -0700252// SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000253#endif
254}
255
256GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
257 GrFontScaler* scaler) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000258 SkIRect bounds;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000259 if (fUseDistanceField) {
260 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
261 return NULL;
262 }
263 } else {
264 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
265 return NULL;
266 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000267 }
jvanverth294c3262014-10-10 11:36:12 -0700268 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
269
joshualitt947556f2014-11-21 09:03:45 -0800270 GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
jvanverth294c3262014-10-10 11:36:12 -0700271 glyph->init(packed, bounds, format);
jvanverthdd6d2272014-07-22 13:25:26 -0700272 fCache.add(glyph);
reed@google.comac10a2d2010-12-22 21:39:39 +0000273 return glyph;
274}
275
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000276void GrTextStrike::removePlot(const GrPlot* plot) {
jvanverthdd6d2272014-07-22 13:25:26 -0700277 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
278 while (!iter.done()) {
279 if (plot == (*iter).fPlot) {
280 (*iter).fPlot = NULL;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000281 }
jvanverthdd6d2272014-07-22 13:25:26 -0700282 ++iter;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000283 }
284
robertphillipsc4f30b12014-07-13 10:09:42 -0700285 GrAtlas::RemovePlot(&fPlotUsage, plot);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000286}
287
jvanverth681e65b2014-09-19 13:07:38 -0700288bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
289 int width = glyph->fBounds.width();
290 int height = glyph->fBounds.height();
291 int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
292 if (width + pad > GR_PLOT_WIDTH) {
293 return true;
294 }
295 if (height + pad > GR_PLOT_HEIGHT) {
296 return true;
297 }
298
299 return false;
300}
jvanverth@google.comd830d132013-11-11 20:54:09 +0000301
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000302bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000303#if 0 // testing hack to force us to flush our cache often
304 static int gCounter;
305 if ((++gCounter % 10) == 0) return false;
306#endif
307
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000308 SkASSERT(glyph);
309 SkASSERT(scaler);
jvanverthdd6d2272014-07-22 13:25:26 -0700310 SkASSERT(fCache.find(glyph->fPackedID));
commit-bot@chromium.org49e80832013-10-07 18:20:27 +0000311 SkASSERT(NULL == glyph->fPlot);
reed@google.comac10a2d2010-12-22 21:39:39 +0000312
mtkleina179a1e2014-07-15 13:29:34 -0700313 SkAutoUnref ar(SkSafeRef(scaler));
reed@google.com98539c62011-03-15 15:40:16 +0000314
jvanverth294c3262014-10-10 11:36:12 -0700315 int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
reed@google.comac10a2d2010-12-22 21:39:39 +0000316
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000317 size_t size = glyph->fBounds.area() * bytesPerPixel;
georgeb62508b2014-08-12 18:00:47 -0700318 GrAutoMalloc<1024> storage(size);
319
jvanverth@google.comd830d132013-11-11 20:54:09 +0000320 if (fUseDistanceField) {
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000321 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
322 glyph->height(),
323 storage.get())) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000324 return false;
325 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000326 } else {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000327 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
328 glyph->height(),
329 glyph->width() * bytesPerPixel,
330 storage.get())) {
331 return false;
332 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000333 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000334
jvanverth294c3262014-10-10 11:36:12 -0700335 GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
336 glyph->width(), glyph->height(),
337 storage.get(), &glyph->fAtlasLocation);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000338
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000339 if (NULL == plot) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000340 return false;
341 }
342
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000343 glyph->fPlot = plot;
reed@google.comac10a2d2010-12-22 21:39:39 +0000344 return true;
345}