• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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