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