Update To 11.40.268.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 "GrSurfacePriv.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         fAtlases[i] = NULL;
37     }
38
39     fHead = fTail = NULL;
40 }
41
42 GrFontCache::~GrFontCache() {
43     SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
44     while (!iter.done()) {
45         SkDELETE(&(*iter));
46         ++iter;
47     }
48     for (int i = 0; i < kAtlasCount; ++i) {
49         delete fAtlases[i];
50     }
51     fGpu->unref();
52 #if FONT_CACHE_STATS
53       SkDebugf("Num purges: %d\n", g_PurgeCount);
54 #endif
55 }
56
57 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
58     static const GrPixelConfig sPixelConfigs[] = {
59         kAlpha_8_GrPixelConfig,
60         kRGB_565_GrPixelConfig,
61         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
69 static int mask_format_to_atlas_index(GrMaskFormat format) {
70     static const int sAtlasIndices[] = {
71         GrFontCache::kA8_AtlasType,
72         GrFontCache::k565_AtlasType,
73         GrFontCache::k8888_AtlasType,
74         GrFontCache::k8888_AtlasType
75     };
76     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
77
78     SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
79     return sAtlasIndices[format];
80 }
81
82 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
83     GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
84     fCache.add(strike);
85
86     if (fHead) {
87         fHead->fPrev = strike;
88     } else {
89         SkASSERT(NULL == fTail);
90         fTail = strike;
91     }
92     strike->fPrev = NULL;
93     strike->fNext = fHead;
94     fHead = strike;
95
96     return strike;
97 }
98
99 void GrFontCache::freeAll() {
100     SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
101     while (!iter.done()) {
102         SkDELETE(&(*iter));
103         ++iter;
104     }
105     fCache.rewind();
106     for (int i = 0; i < kAtlasCount; ++i) {
107         delete fAtlases[i];
108         fAtlases[i] = NULL;
109     }
110     fHead = NULL;
111     fTail = NULL;
112 }
113
114 void GrFontCache::purgeStrike(GrTextStrike* strike) {
115     fCache.remove(*(strike->fFontScalerKey));
116     this->detachStrikeFromList(strike);
117     delete strike;
118 }
119
120
121 GrPlot* 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);
129         fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
130                                                     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
139 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
140     SkASSERT(preserveStrike);
141
142     int index = mask_format_to_atlas_index(glyph->fMaskFormat);
143     GrAtlas* atlas = fAtlases[index];
144     GrPlot* plot = atlas->getUnusedPlot();
145     if (NULL == plot) {
146         return false;
147     }
148     plot->resetRects();
149
150     GrTextStrike* strike = fHead;
151     while (strike) {
152         GrTextStrike* strikeToPurge = strike;
153         strike = strikeToPurge->fNext;
154         strikeToPurge->removePlot(plot);
155
156         // clear out any empty strikes (except this one)
157         if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
158             this->purgeStrike(strikeToPurge);
159         }
160     }
161
162 #if FONT_CACHE_STATS
163     ++g_PurgeCount;
164 #endif
165
166     return true;
167 }
168
169 #ifdef SK_DEBUG
170 void GrFontCache::validate() const {
171     int count = fCache.count();
172     if (0 == count) {
173         SkASSERT(!fHead);
174         SkASSERT(!fTail);
175     } else if (1 == count) {
176         SkASSERT(fHead == fTail);
177     } else {
178         SkASSERT(fHead != fTail);
179     }
180
181     int count2 = 0;
182     const GrTextStrike* strike = fHead;
183     while (strike) {
184         count2 += 1;
185         strike = strike->fNext;
186     }
187     SkASSERT(count == count2);
188
189     count2 = 0;
190     strike = fTail;
191     while (strike) {
192         count2 += 1;
193         strike = strike->fPrev;
194     }
195     SkASSERT(count == count2);
196 }
197 #endif
198
199 void GrFontCache::dump() const {
200     static int gDumpCount = 0;
201     for (int i = 0; i < kAtlasCount; ++i) {
202         if (fAtlases[i]) {
203             GrTexture* texture = fAtlases[i]->getTexture();
204             if (texture) {
205                 SkString filename;
206 #ifdef SK_BUILD_FOR_ANDROID
207                 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
208 #else
209                 filename.printf("fontcache_%d%d.png", gDumpCount, i);
210 #endif
211                 texture->surfacePriv().savePixels(filename.c_str());
212             }
213         }
214     }
215     ++gDumpCount;
216 }
217
218 ///////////////////////////////////////////////////////////////////////////////
219
220 #ifdef SK_DEBUG
221     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
232 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) : 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
238 #ifdef SK_DEBUG
239 //    SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
240     gCounter += 1;
241 #endif
242 }
243
244 GrTextStrike::~GrTextStrike() {
245     fFontScalerKey->unref();
246     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
247     while (!iter.done()) {
248         (*iter).free();
249         ++iter;
250     }
251
252 #ifdef SK_DEBUG
253     gCounter -= 1;
254 //    SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
255 #endif
256 }
257
258 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
259                                      GrFontScaler* scaler) {
260     SkIRect bounds;
261     if (fUseDistanceField) {
262         if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
263             return NULL;
264         }
265     } else {
266         if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
267             return NULL;
268         }
269     }
270     GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
271     
272     GrGlyph* glyph = fPool.alloc();
273     glyph->init(packed, bounds, format);
274     fCache.add(glyph);
275     return glyph;
276 }
277
278 void GrTextStrike::removePlot(const GrPlot* plot) {
279     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
280     while (!iter.done()) {
281         if (plot == (*iter).fPlot) {
282             (*iter).fPlot = NULL;
283         }
284         ++iter;
285     }
286
287     GrAtlas::RemovePlot(&fPlotUsage, plot);
288 }
289
290 bool 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 }
303
304 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
305 #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
310     SkASSERT(glyph);
311     SkASSERT(scaler);
312     SkASSERT(fCache.find(glyph->fPackedID));
313     SkASSERT(NULL == glyph->fPlot);
314
315     SkAutoUnref ar(SkSafeRef(scaler));
316
317     int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
318
319     size_t size = glyph->fBounds.area() * bytesPerPixel;
320     GrAutoMalloc<1024> storage(size);
321
322     if (fUseDistanceField) {
323         if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
324                                            glyph->height(),
325                                            storage.get())) {
326             return false;
327         }
328     } else {
329         if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
330                                          glyph->height(),
331                                          glyph->width() * bytesPerPixel,
332                                          storage.get())) {
333             return false;
334         }
335     }
336
337     GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
338                                           glyph->width(), glyph->height(),
339                                           storage.get(), &glyph->fAtlasLocation);
340
341     if (NULL == plot) {
342         return false;
343     }
344
345     glyph->fPlot = plot;
346     return true;
347 }