gfx/skia/trunk/src/gpu/GrTextStrike.cpp

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

mercurial