blob: 9f9ea130f647d57ca319b595f0d6b72c6992d351 [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 "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +00009#include "GrRectanizer.h"
10#include "GrTextStrike.h"
11#include "GrTextStrike_impl.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
52 GrPrintf("Num purges: %d\n", g_PurgeCount);
53#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 kSkia8888_GrPixelConfig
62 };
63 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
64
65 return sPixelConfigs[format];
66}
67
68static int mask_format_to_atlas_index(GrMaskFormat format) {
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000069 static const int sAtlasIndices[] = {
70 GrFontCache::kA8_AtlasType,
71 GrFontCache::k565_AtlasType,
72 GrFontCache::k8888_AtlasType,
73 GrFontCache::k8888_AtlasType
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000074 };
75 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
76
77 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
78 return sAtlasIndices[format];
commit-bot@chromium.org95294412013-09-26 15:28:40 +000079}
80
jvanverthdd6d2272014-07-22 13:25:26 -070081GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000082 GrMaskFormat format = scaler->getMaskFormat();
commit-bot@chromium.org95294412013-09-26 15:28:40 +000083 GrPixelConfig config = mask_format_to_pixel_config(format);
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000084 int atlasIndex = mask_format_to_atlas_index(format);
robertphillips1d86ee82014-06-24 15:08:49 -070085 if (NULL == fAtlases[atlasIndex]) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000086 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
87 GR_ATLAS_TEXTURE_HEIGHT);
robertphillips952841b2014-06-30 08:26:50 -070088 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
robertphillips1d86ee82014-06-24 15:08:49 -070089 textureSize,
90 GR_NUM_PLOTS_X,
91 GR_NUM_PLOTS_Y,
92 true));
reed@google.comac10a2d2010-12-22 21:39:39 +000093 }
tomhudson@google.comc377baf2012-07-09 20:17:56 +000094 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
robertphillips1d86ee82014-06-24 15:08:49 -070095 (this, scaler->getKey(), format, fAtlases[atlasIndex]));
jvanverthdd6d2272014-07-22 13:25:26 -070096 fCache.add(strike);
reed@google.comac10a2d2010-12-22 21:39:39 +000097
98 if (fHead) {
99 fHead->fPrev = strike;
100 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000101 SkASSERT(NULL == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000102 fTail = strike;
103 }
104 strike->fPrev = NULL;
105 strike->fNext = fHead;
106 fHead = strike;
107
108 return strike;
109}
110
reed@google.comac10a2d2010-12-22 21:39:39 +0000111void GrFontCache::freeAll() {
jvanverthdd6d2272014-07-22 13:25:26 -0700112 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
113 while (!iter.done()) {
114 SkDELETE(&(*iter));
115 ++iter;
116 }
117 fCache.rewind();
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000118 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -0700119 delete fAtlases[i];
120 fAtlases[i] = NULL;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000121 }
bsalomon@google.com8fe72472011-03-30 21:26:44 +0000122 fHead = NULL;
123 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000124}
125
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000126void GrFontCache::purgeStrike(GrTextStrike* strike) {
jvanverthdd6d2272014-07-22 13:25:26 -0700127 fCache.remove(*(strike->fFontScalerKey));
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000128 this->detachStrikeFromList(strike);
129 delete strike;
130}
131
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000132bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
jvanverth@google.combbe55fd2013-09-16 20:28:37 +0000133 SkASSERT(NULL != preserveStrike);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000134
robertphillips1d86ee82014-06-24 15:08:49 -0700135 GrAtlas* atlas = preserveStrike->fAtlas;
136 GrPlot* plot = atlas->getUnusedPlot();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000137 if (NULL == plot) {
138 return false;
139 }
140 plot->resetRects();
141
142 GrTextStrike* strike = fHead;
jvanverth@google.combbe55fd2013-09-16 20:28:37 +0000143 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000144 while (strike) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000145 if (maskFormat != strike->fMaskFormat) {
146 strike = strike->fNext;
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000147 continue;
148 }
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000149
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000150 GrTextStrike* strikeToPurge = strike;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000151 strike = strikeToPurge->fNext;
152 strikeToPurge->removePlot(plot);
153
154 // clear out any empty strikes (except this one)
robertphillips1d86ee82014-06-24 15:08:49 -0700155 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000156 this->purgeStrike(strikeToPurge);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000157 }
158 }
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000159
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000160#if FONT_CACHE_STATS
161 ++g_PurgeCount;
162#endif
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000163
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000164 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000165}
166
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000167#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000168void GrFontCache::validate() const {
169 int count = fCache.count();
170 if (0 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000171 SkASSERT(!fHead);
172 SkASSERT(!fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000173 } else if (1 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000174 SkASSERT(fHead == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000175 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000176 SkASSERT(fHead != fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000177 }
178
179 int count2 = 0;
180 const GrTextStrike* strike = fHead;
181 while (strike) {
182 count2 += 1;
183 strike = strike->fNext;
184 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000185 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000186
187 count2 = 0;
188 strike = fTail;
189 while (strike) {
190 count2 += 1;
191 strike = strike->fPrev;
192 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000193 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000194}
195#endif
196
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000197void GrFontCache::dump() const {
198 static int gDumpCount = 0;
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000199 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -0700200 if (NULL != fAtlases[i]) {
201 GrTexture* texture = fAtlases[i]->getTexture();
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000202 if (NULL != texture) {
203 SkString filename;
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000204#ifdef SK_BUILD_FOR_ANDROID
205 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
206#else
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000207 filename.printf("fontcache_%d%d.png", gDumpCount, i);
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000208#endif
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000209 texture->savePixels(filename.c_str());
210 }
211 }
212 }
213 ++gDumpCount;
214}
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000215
reed@google.comac10a2d2010-12-22 21:39:39 +0000216///////////////////////////////////////////////////////////////////////////////
217
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000218#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000219 static int gCounter;
220#endif
221
222/*
223 The text strike is specific to a given font/style/matrix setup, which is
224 represented by the GrHostFontScaler object we are given in getGlyph().
225
226 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
227 atlas and a position within that texture.
228 */
229
jvanverth733f5f52014-07-11 19:45:16 -0700230GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key,
reed@google.com98539c62011-03-15 15:40:16 +0000231 GrMaskFormat format,
robertphillips1d86ee82014-06-24 15:08:49 -0700232 GrAtlas* atlas) : fPool(64) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000233 fFontScalerKey = key;
234 fFontScalerKey->ref();
235
236 fFontCache = cache; // no need to ref, it won't go away before we do
robertphillips1d86ee82014-06-24 15:08:49 -0700237 fAtlas = atlas; // no need to ref, it won't go away before we do
reed@google.comac10a2d2010-12-22 21:39:39 +0000238
reed@google.com98539c62011-03-15 15:40:16 +0000239 fMaskFormat = format;
240
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000241#ifdef SK_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000242// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 gCounter += 1;
244#endif
245}
246
reed@google.comac10a2d2010-12-22 21:39:39 +0000247GrTextStrike::~GrTextStrike() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000248 fFontScalerKey->unref();
jvanverthdd6d2272014-07-22 13:25:26 -0700249 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
250 while (!iter.done()) {
251 (*iter).free();
252 ++iter;
253 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000254
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000255#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000256 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000257// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000258#endif
259}
260
261GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
262 GrFontScaler* scaler) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000263 SkIRect bounds;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000264 if (fUseDistanceField) {
265 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
266 return NULL;
267 }
268 } else {
269 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
270 return NULL;
271 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000272 }
273
274 GrGlyph* glyph = fPool.alloc();
275 glyph->init(packed, bounds);
jvanverthdd6d2272014-07-22 13:25:26 -0700276 fCache.add(glyph);
reed@google.comac10a2d2010-12-22 21:39:39 +0000277 return glyph;
278}
279
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000280void GrTextStrike::removePlot(const GrPlot* plot) {
jvanverthdd6d2272014-07-22 13:25:26 -0700281 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
282 while (!iter.done()) {
283 if (plot == (*iter).fPlot) {
284 (*iter).fPlot = NULL;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000285 }
jvanverthdd6d2272014-07-22 13:25:26 -0700286 ++iter;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000287 }
288
robertphillipsc4f30b12014-07-13 10:09:42 -0700289 GrAtlas::RemovePlot(&fPlotUsage, plot);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000290}
291
jvanverth@google.comd830d132013-11-11 20:54:09 +0000292
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000293bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000294#if 0 // testing hack to force us to flush our cache often
295 static int gCounter;
296 if ((++gCounter % 10) == 0) return false;
297#endif
298
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000299 SkASSERT(glyph);
300 SkASSERT(scaler);
jvanverthdd6d2272014-07-22 13:25:26 -0700301 SkASSERT(fCache.find(glyph->fPackedID));
commit-bot@chromium.org49e80832013-10-07 18:20:27 +0000302 SkASSERT(NULL == glyph->fPlot);
reed@google.comac10a2d2010-12-22 21:39:39 +0000303
mtkleina179a1e2014-07-15 13:29:34 -0700304 SkAutoUnref ar(SkSafeRef(scaler));
reed@google.com98539c62011-03-15 15:40:16 +0000305
306 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
reed@google.comac10a2d2010-12-22 21:39:39 +0000307
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000308 size_t size = glyph->fBounds.area() * bytesPerPixel;
309 SkAutoSMalloc<1024> storage(size);
jvanverth@google.comd830d132013-11-11 20:54:09 +0000310 if (fUseDistanceField) {
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000311 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
312 glyph->height(),
313 storage.get())) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000314 return false;
315 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000316 } else {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000317 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
318 glyph->height(),
319 glyph->width() * bytesPerPixel,
320 storage.get())) {
321 return false;
322 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000323 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000324
robertphillips1d86ee82014-06-24 15:08:49 -0700325 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, glyph->width(),
326 glyph->height(), storage.get(),
327 &glyph->fAtlasLocation);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000328
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000329 if (NULL == plot) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000330 return false;
331 }
332
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000333 glyph->fPlot = plot;
reed@google.comac10a2d2010-12-22 21:39:39 +0000334 return true;
335}