Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / gpu / GrTextStrike.cpp
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
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
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
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
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
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
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
126 void GrFontCache::purgeStrike(GrTextStrike* strike) {
127     fCache.remove(*(strike->fFontScalerKey));
128     this->detachStrikeFromList(strike);
129     delete strike;
130 }
131
132 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
133     SkASSERT(NULL != 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
168 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
197 void GrFontCache::dump() const {
198     static int gDumpCount = 0;
199     for (int i = 0; i < kAtlasCount; ++i) {
200         if (NULL != fAtlases[i]) {
201             GrTexture* texture = fAtlases[i]->getTexture();
202             if (NULL != 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
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
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
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
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
292
293 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
294 #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
299     SkASSERT(glyph);
300     SkASSERT(scaler);
301     SkASSERT(fCache.find(glyph->fPackedID));
302     SkASSERT(NULL == glyph->fPlot);
303
304     SkAutoUnref ar(SkSafeRef(scaler));
305
306     int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
307
308     size_t size = glyph->fBounds.area() * bytesPerPixel;
309     GrAutoMalloc<1024> storage(size);
310
311     if (fUseDistanceField) {
312         if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
313                                            glyph->height(),
314                                            storage.get())) {
315             return false;
316         }
317     } else {
318         if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
319                                          glyph->height(),
320                                          glyph->width() * bytesPerPixel,
321                                          storage.get())) {
322             return false;
323         }
324     }
325
326     GrPlot* plot  = fAtlas->addToAtlas(&fPlotUsage, glyph->width(),
327                                        glyph->height(), storage.get(),
328                                        &glyph->fAtlasLocation);
329
330     if (NULL == plot) {
331         return false;
332     }
333
334     glyph->fPlot = plot;
335     return true;
336 }