1 /*
2 * 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.
6 */
7
8 #include "GrGpu.h"
9 #include "GrRectanizer.h"
10 #include "GrTextStrike.h"
11 #include "GrTextStrike_impl.h"
12 #include "SkString.h"
13
14 #include "SkDistanceFieldGen.h"
15
16 ///////////////////////////////////////////////////////////////////////////////
17
18 #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
27 #define FONT_CACHE_STATS 0
28 #if FONT_CACHE_STATS
29 static int g_PurgeCount = 0;
30 #endif
31
GrFontCache(GrGpu * gpu)32 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
33 gpu->ref();
34 for (int i = 0; i < kAtlasCount; ++i) {
35 fAtlases[i] = NULL;
36 }
37
38 fHead = fTail = NULL;
39 }
40
~GrFontCache()41 GrFontCache::~GrFontCache() {
42 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
43 while (!iter.done()) {
44 SkDELETE(&(*iter));
45 ++iter;
46 }
47 for (int i = 0; i < kAtlasCount; ++i) {
48 delete fAtlases[i];
49 }
50 fGpu->unref();
51 #if FONT_CACHE_STATS
52 GrPrintf("Num purges: %d\n", g_PurgeCount);
53 #endif
54 }
55
mask_format_to_pixel_config(GrMaskFormat format)56 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
57 static const GrPixelConfig sPixelConfigs[] = {
58 kAlpha_8_GrPixelConfig,
59 kRGB_565_GrPixelConfig,
60 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
mask_format_to_atlas_index(GrMaskFormat format)68 static int mask_format_to_atlas_index(GrMaskFormat format) {
69 static const int sAtlasIndices[] = {
70 GrFontCache::kA8_AtlasType,
71 GrFontCache::k565_AtlasType,
72 GrFontCache::k8888_AtlasType,
73 GrFontCache::k8888_AtlasType
74 };
75 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
76
77 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
78 return sAtlasIndices[format];
79 }
80
generateStrike(GrFontScaler * scaler)81 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
82 GrMaskFormat format = scaler->getMaskFormat();
83 GrPixelConfig config = mask_format_to_pixel_config(format);
84 int atlasIndex = mask_format_to_atlas_index(format);
85 if (NULL == fAtlases[atlasIndex]) {
86 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
87 GR_ATLAS_TEXTURE_HEIGHT);
88 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
89 textureSize,
90 GR_NUM_PLOTS_X,
91 GR_NUM_PLOTS_Y,
92 true));
93 }
94 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
95 (this, scaler->getKey(), format, fAtlases[atlasIndex]));
96 fCache.add(strike);
97
98 if (fHead) {
99 fHead->fPrev = strike;
100 } else {
101 SkASSERT(NULL == fTail);
102 fTail = strike;
103 }
104 strike->fPrev = NULL;
105 strike->fNext = fHead;
106 fHead = strike;
107
108 return strike;
109 }
110
freeAll()111 void GrFontCache::freeAll() {
112 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
113 while (!iter.done()) {
114 SkDELETE(&(*iter));
115 ++iter;
116 }
117 fCache.rewind();
118 for (int i = 0; i < kAtlasCount; ++i) {
119 delete fAtlases[i];
120 fAtlases[i] = NULL;
121 }
122 fHead = NULL;
123 fTail = NULL;
124 }
125
purgeStrike(GrTextStrike * strike)126 void GrFontCache::purgeStrike(GrTextStrike* strike) {
127 fCache.remove(*(strike->fFontScalerKey));
128 this->detachStrikeFromList(strike);
129 delete strike;
130 }
131
freeUnusedPlot(GrTextStrike * preserveStrike)132 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
133 SkASSERT(preserveStrike);
134
135 GrAtlas* atlas = preserveStrike->fAtlas;
136 GrPlot* plot = atlas->getUnusedPlot();
137 if (NULL == plot) {
138 return false;
139 }
140 plot->resetRects();
141
142 GrTextStrike* strike = fHead;
143 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
144 while (strike) {
145 if (maskFormat != strike->fMaskFormat) {
146 strike = strike->fNext;
147 continue;
148 }
149
150 GrTextStrike* strikeToPurge = strike;
151 strike = strikeToPurge->fNext;
152 strikeToPurge->removePlot(plot);
153
154 // clear out any empty strikes (except this one)
155 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
156 this->purgeStrike(strikeToPurge);
157 }
158 }
159
160 #if FONT_CACHE_STATS
161 ++g_PurgeCount;
162 #endif
163
164 return true;
165 }
166
167 #ifdef SK_DEBUG
validate() const168 void GrFontCache::validate() const {
169 int count = fCache.count();
170 if (0 == count) {
171 SkASSERT(!fHead);
172 SkASSERT(!fTail);
173 } else if (1 == count) {
174 SkASSERT(fHead == fTail);
175 } else {
176 SkASSERT(fHead != fTail);
177 }
178
179 int count2 = 0;
180 const GrTextStrike* strike = fHead;
181 while (strike) {
182 count2 += 1;
183 strike = strike->fNext;
184 }
185 SkASSERT(count == count2);
186
187 count2 = 0;
188 strike = fTail;
189 while (strike) {
190 count2 += 1;
191 strike = strike->fPrev;
192 }
193 SkASSERT(count == count2);
194 }
195 #endif
196
dump() const197 void GrFontCache::dump() const {
198 static int gDumpCount = 0;
199 for (int i = 0; i < kAtlasCount; ++i) {
200 if (fAtlases[i]) {
201 GrTexture* texture = fAtlases[i]->getTexture();
202 if (texture) {
203 SkString filename;
204 #ifdef SK_BUILD_FOR_ANDROID
205 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
206 #else
207 filename.printf("fontcache_%d%d.png", gDumpCount, i);
208 #endif
209 texture->savePixels(filename.c_str());
210 }
211 }
212 }
213 ++gDumpCount;
214 }
215
216 ///////////////////////////////////////////////////////////////////////////////
217
218 #ifdef SK_DEBUG
219 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
GrTextStrike(GrFontCache * cache,const GrFontDescKey * key,GrMaskFormat format,GrAtlas * atlas)230 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key,
231 GrMaskFormat format,
232 GrAtlas* atlas) : fPool(64) {
233 fFontScalerKey = key;
234 fFontScalerKey->ref();
235
236 fFontCache = cache; // no need to ref, it won't go away before we do
237 fAtlas = atlas; // no need to ref, it won't go away before we do
238
239 fMaskFormat = format;
240
241 #ifdef SK_DEBUG
242 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
243 gCounter += 1;
244 #endif
245 }
246
~GrTextStrike()247 GrTextStrike::~GrTextStrike() {
248 fFontScalerKey->unref();
249 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
250 while (!iter.done()) {
251 (*iter).free();
252 ++iter;
253 }
254
255 #ifdef SK_DEBUG
256 gCounter -= 1;
257 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
258 #endif
259 }
260
generateGlyph(GrGlyph::PackedID packed,GrFontScaler * scaler)261 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
262 GrFontScaler* scaler) {
263 SkIRect bounds;
264 if (fUseDistanceField) {
265 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
266 return NULL;
267 }
268 } else {
269 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
270 return NULL;
271 }
272 }
273
274 GrGlyph* glyph = fPool.alloc();
275 glyph->init(packed, bounds);
276 fCache.add(glyph);
277 return glyph;
278 }
279
removePlot(const GrPlot * plot)280 void GrTextStrike::removePlot(const GrPlot* plot) {
281 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
282 while (!iter.done()) {
283 if (plot == (*iter).fPlot) {
284 (*iter).fPlot = NULL;
285 }
286 ++iter;
287 }
288
289 GrAtlas::RemovePlot(&fPlotUsage, plot);
290 }
291
glyphTooLargeForAtlas(GrGlyph * glyph)292 bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
293 int width = glyph->fBounds.width();
294 int height = glyph->fBounds.height();
295 int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
296 if (width + pad > GR_PLOT_WIDTH) {
297 return true;
298 }
299 if (height + pad > GR_PLOT_HEIGHT) {
300 return true;
301 }
302
303 return false;
304 }
305
addGlyphToAtlas(GrGlyph * glyph,GrFontScaler * scaler)306 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
307 #if 0 // testing hack to force us to flush our cache often
308 static int gCounter;
309 if ((++gCounter % 10) == 0) return false;
310 #endif
311
312 SkASSERT(glyph);
313 SkASSERT(scaler);
314 SkASSERT(fCache.find(glyph->fPackedID));
315 SkASSERT(NULL == glyph->fPlot);
316
317 SkAutoUnref ar(SkSafeRef(scaler));
318
319 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
320
321 size_t size = glyph->fBounds.area() * bytesPerPixel;
322 GrAutoMalloc<1024> storage(size);
323
324 if (fUseDistanceField) {
325 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
326 glyph->height(),
327 storage.get())) {
328 return false;
329 }
330 } else {
331 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
332 glyph->height(),
333 glyph->width() * bytesPerPixel,
334 storage.get())) {
335 return false;
336 }
337 }
338
339 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, glyph->width(),
340 glyph->height(), storage.get(),
341 &glyph->fAtlasLocation);
342
343 if (NULL == plot) {
344 return false;
345 }
346
347 glyph->fPlot = plot;
348 return true;
349 }
350