blob: 19cdf63031c7345deedba71f7eaf3736eb1deaaa [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"
bsalomonafbf2d62014-09-30 12:18:44 -070010#include "GrSurfacePriv.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000011#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
commit-bot@chromium.org8065ec52014-03-11 15:57:40 +000015#include "SkDistanceFieldGen.h"
jvanverth@google.comd830d132013-11-11 20:54:09 +000016
reed@google.comfa35e3d2012-06-26 20:16:17 +000017///////////////////////////////////////////////////////////////////////////////
18
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000019#define GR_ATLAS_TEXTURE_WIDTH 1024
20#define GR_ATLAS_TEXTURE_HEIGHT 2048
21
22#define GR_PLOT_WIDTH 256
23#define GR_PLOT_HEIGHT 256
24
25#define GR_NUM_PLOTS_X (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
26#define GR_NUM_PLOTS_Y (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
27
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000028#define FONT_CACHE_STATS 0
29#if FONT_CACHE_STATS
30static int g_PurgeCount = 0;
31#endif
32
reed@google.comac10a2d2010-12-22 21:39:39 +000033GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
34 gpu->ref();
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000035 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -070036 fAtlases[i] = NULL;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000037 }
reed@google.comac10a2d2010-12-22 21:39:39 +000038
39 fHead = fTail = NULL;
40}
41
42GrFontCache::~GrFontCache() {
jvanverthdd6d2272014-07-22 13:25:26 -070043 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
44 while (!iter.done()) {
45 SkDELETE(&(*iter));
46 ++iter;
47 }
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000048 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -070049 delete fAtlases[i];
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +000050 }
reed@google.comac10a2d2010-12-22 21:39:39 +000051 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000052#if FONT_CACHE_STATS
53 GrPrintf("Num purges: %d\n", g_PurgeCount);
54#endif
reed@google.comac10a2d2010-12-22 21:39:39 +000055}
56
commit-bot@chromium.org95294412013-09-26 15:28:40 +000057static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000058 static const GrPixelConfig sPixelConfigs[] = {
59 kAlpha_8_GrPixelConfig,
60 kRGB_565_GrPixelConfig,
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000061 kSkia8888_GrPixelConfig,
62 kSkia8888_GrPixelConfig
63 };
64 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
65
66 return sPixelConfigs[format];
67}
68
69static int mask_format_to_atlas_index(GrMaskFormat format) {
skia.committer@gmail.com6e515d62013-12-04 07:02:26 +000070 static const int sAtlasIndices[] = {
71 GrFontCache::kA8_AtlasType,
72 GrFontCache::k565_AtlasType,
73 GrFontCache::k8888_AtlasType,
74 GrFontCache::k8888_AtlasType
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +000075 };
76 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
77
78 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
79 return sAtlasIndices[format];
commit-bot@chromium.org95294412013-09-26 15:28:40 +000080}
81
jvanverthdd6d2272014-07-22 13:25:26 -070082GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
jvanverth294c3262014-10-10 11:36:12 -070083 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
jvanverthdd6d2272014-07-22 13:25:26 -070084 fCache.add(strike);
reed@google.comac10a2d2010-12-22 21:39:39 +000085
86 if (fHead) {
87 fHead->fPrev = strike;
88 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000089 SkASSERT(NULL == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +000090 fTail = strike;
91 }
92 strike->fPrev = NULL;
93 strike->fNext = fHead;
94 fHead = strike;
95
96 return strike;
97}
98
reed@google.comac10a2d2010-12-22 21:39:39 +000099void GrFontCache::freeAll() {
jvanverthdd6d2272014-07-22 13:25:26 -0700100 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
101 while (!iter.done()) {
102 SkDELETE(&(*iter));
103 ++iter;
104 }
105 fCache.rewind();
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000106 for (int i = 0; i < kAtlasCount; ++i) {
robertphillips1d86ee82014-06-24 15:08:49 -0700107 delete fAtlases[i];
108 fAtlases[i] = NULL;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000109 }
bsalomon@google.com8fe72472011-03-30 21:26:44 +0000110 fHead = NULL;
111 fTail = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000112}
113
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000114void GrFontCache::purgeStrike(GrTextStrike* strike) {
jvanverthdd6d2272014-07-22 13:25:26 -0700115 fCache.remove(*(strike->fFontScalerKey));
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000116 this->detachStrikeFromList(strike);
117 delete strike;
118}
119
jvanverth294c3262014-10-10 11:36:12 -0700120
121GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
122 int width, int height, const void* image,
123 SkIPoint16* loc) {
124 GrPixelConfig config = mask_format_to_pixel_config(format);
125 int atlasIndex = mask_format_to_atlas_index(format);
126 if (NULL == fAtlases[atlasIndex]) {
127 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
128 GR_ATLAS_TEXTURE_HEIGHT);
bsalomonf2703d82014-10-28 14:33:06 -0700129 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
jvanverth294c3262014-10-10 11:36:12 -0700130 textureSize,
131 GR_NUM_PLOTS_X,
132 GR_NUM_PLOTS_Y,
133 true));
134 }
135 return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
136}
137
138
139bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
bsalomon49f085d2014-09-05 13:34:00 -0700140 SkASSERT(preserveStrike);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000141
jvanverth294c3262014-10-10 11:36:12 -0700142 int index = mask_format_to_atlas_index(glyph->fMaskFormat);
143 GrAtlas* atlas = fAtlases[index];
robertphillips1d86ee82014-06-24 15:08:49 -0700144 GrPlot* plot = atlas->getUnusedPlot();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000145 if (NULL == plot) {
146 return false;
147 }
148 plot->resetRects();
149
150 GrTextStrike* strike = fHead;
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000151 while (strike) {
bsalomon@google.com7359eae2011-06-21 21:18:25 +0000152 GrTextStrike* strikeToPurge = strike;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000153 strike = strikeToPurge->fNext;
154 strikeToPurge->removePlot(plot);
155
156 // clear out any empty strikes (except this one)
robertphillips1d86ee82014-06-24 15:08:49 -0700157 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
commit-bot@chromium.orgb2e9fa52013-10-27 20:50:23 +0000158 this->purgeStrike(strikeToPurge);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000159 }
160 }
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000161
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000162#if FONT_CACHE_STATS
163 ++g_PurgeCount;
164#endif
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000165
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000166 return true;
reed@google.comac10a2d2010-12-22 21:39:39 +0000167}
168
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000169#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000170void GrFontCache::validate() const {
171 int count = fCache.count();
172 if (0 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000173 SkASSERT(!fHead);
174 SkASSERT(!fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000175 } else if (1 == count) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000176 SkASSERT(fHead == fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000177 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000178 SkASSERT(fHead != fTail);
reed@google.comac10a2d2010-12-22 21:39:39 +0000179 }
180
181 int count2 = 0;
182 const GrTextStrike* strike = fHead;
183 while (strike) {
184 count2 += 1;
185 strike = strike->fNext;
186 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000187 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000188
189 count2 = 0;
190 strike = fTail;
191 while (strike) {
192 count2 += 1;
193 strike = strike->fPrev;
194 }
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000195 SkASSERT(count == count2);
reed@google.comac10a2d2010-12-22 21:39:39 +0000196}
197#endif
198
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000199void GrFontCache::dump() const {
200 static int gDumpCount = 0;
commit-bot@chromium.orgf8cb1842013-12-03 19:45:22 +0000201 for (int i = 0; i < kAtlasCount; ++i) {
bsalomon49f085d2014-09-05 13:34:00 -0700202 if (fAtlases[i]) {
robertphillips1d86ee82014-06-24 15:08:49 -0700203 GrTexture* texture = fAtlases[i]->getTexture();
bsalomon49f085d2014-09-05 13:34:00 -0700204 if (texture) {
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000205 SkString filename;
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000206#ifdef SK_BUILD_FOR_ANDROID
207 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
208#else
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000209 filename.printf("fontcache_%d%d.png", gDumpCount, i);
commit-bot@chromium.org4362a382014-03-26 19:49:03 +0000210#endif
bsalomonafbf2d62014-09-30 12:18:44 -0700211 texture->surfacePriv().savePixels(filename.c_str());
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000212 }
213 }
214 }
215 ++gDumpCount;
216}
commit-bot@chromium.org03e3e892013-10-02 18:19:17 +0000217
reed@google.comac10a2d2010-12-22 21:39:39 +0000218///////////////////////////////////////////////////////////////////////////////
219
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000220#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000221 static int gCounter;
222#endif
223
224/*
225 The text strike is specific to a given font/style/matrix setup, which is
226 represented by the GrHostFontScaler object we are given in getGlyph().
227
228 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
229 atlas and a position within that texture.
230 */
231
jvanverth294c3262014-10-10 11:36:12 -0700232GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) : 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
reed@google.com98539c62011-03-15 15:40:16 +0000237
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000238#ifdef SK_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +0000239// GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000240 gCounter += 1;
241#endif
242}
243
reed@google.comac10a2d2010-12-22 21:39:39 +0000244GrTextStrike::~GrTextStrike() {
reed@google.comac10a2d2010-12-22 21:39:39 +0000245 fFontScalerKey->unref();
jvanverthdd6d2272014-07-22 13:25:26 -0700246 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
247 while (!iter.done()) {
248 (*iter).free();
249 ++iter;
250 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000251
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000252#ifdef SK_DEBUG
reed@google.comac10a2d2010-12-22 21:39:39 +0000253 gCounter -= 1;
reed@google.com3ef80cf2011-07-05 19:09:47 +0000254// GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +0000255#endif
256}
257
258GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
259 GrFontScaler* scaler) {
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000260 SkIRect bounds;
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000261 if (fUseDistanceField) {
262 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
263 return NULL;
264 }
265 } else {
266 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
267 return NULL;
268 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000269 }
jvanverth294c3262014-10-10 11:36:12 -0700270 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
271
reed@google.comac10a2d2010-12-22 21:39:39 +0000272 GrGlyph* glyph = fPool.alloc();
jvanverth294c3262014-10-10 11:36:12 -0700273 glyph->init(packed, bounds, format);
jvanverthdd6d2272014-07-22 13:25:26 -0700274 fCache.add(glyph);
reed@google.comac10a2d2010-12-22 21:39:39 +0000275 return glyph;
276}
277
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000278void GrTextStrike::removePlot(const GrPlot* plot) {
jvanverthdd6d2272014-07-22 13:25:26 -0700279 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
280 while (!iter.done()) {
281 if (plot == (*iter).fPlot) {
282 (*iter).fPlot = NULL;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000283 }
jvanverthdd6d2272014-07-22 13:25:26 -0700284 ++iter;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000285 }
286
robertphillipsc4f30b12014-07-13 10:09:42 -0700287 GrAtlas::RemovePlot(&fPlotUsage, plot);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000288}
289
jvanverth681e65b2014-09-19 13:07:38 -0700290bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
291 int width = glyph->fBounds.width();
292 int height = glyph->fBounds.height();
293 int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
294 if (width + pad > GR_PLOT_WIDTH) {
295 return true;
296 }
297 if (height + pad > GR_PLOT_HEIGHT) {
298 return true;
299 }
300
301 return false;
302}
jvanverth@google.comd830d132013-11-11 20:54:09 +0000303
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000304bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
reed@google.com0ebe81a2011-04-04 20:06:59 +0000305#if 0 // testing hack to force us to flush our cache often
306 static int gCounter;
307 if ((++gCounter % 10) == 0) return false;
308#endif
309
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000310 SkASSERT(glyph);
311 SkASSERT(scaler);
jvanverthdd6d2272014-07-22 13:25:26 -0700312 SkASSERT(fCache.find(glyph->fPackedID));
commit-bot@chromium.org49e80832013-10-07 18:20:27 +0000313 SkASSERT(NULL == glyph->fPlot);
reed@google.comac10a2d2010-12-22 21:39:39 +0000314
mtkleina179a1e2014-07-15 13:29:34 -0700315 SkAutoUnref ar(SkSafeRef(scaler));
reed@google.com98539c62011-03-15 15:40:16 +0000316
jvanverth294c3262014-10-10 11:36:12 -0700317 int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
reed@google.comac10a2d2010-12-22 21:39:39 +0000318
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000319 size_t size = glyph->fBounds.area() * bytesPerPixel;
georgeb62508b2014-08-12 18:00:47 -0700320 GrAutoMalloc<1024> storage(size);
321
jvanverth@google.comd830d132013-11-11 20:54:09 +0000322 if (fUseDistanceField) {
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000323 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
324 glyph->height(),
325 storage.get())) {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000326 return false;
327 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000328 } else {
jvanverth@google.comd830d132013-11-11 20:54:09 +0000329 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
330 glyph->height(),
331 glyph->width() * bytesPerPixel,
332 storage.get())) {
333 return false;
334 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000335 }
jvanverth@google.comd830d132013-11-11 20:54:09 +0000336
jvanverth294c3262014-10-10 11:36:12 -0700337 GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
338 glyph->width(), glyph->height(),
339 storage.get(), &glyph->fAtlasLocation);
commit-bot@chromium.org762cd802014-04-14 22:05:07 +0000340
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000341 if (NULL == plot) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000342 return false;
343 }
344
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000345 glyph->fPlot = plot;
reed@google.comac10a2d2010-12-22 21:39:39 +0000346 return true;
347}