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 #if SK_DISTANCEFIELD_FONTS
16 #include "edtaa3.h"
17 #endif
18
19 ///////////////////////////////////////////////////////////////////////////////
20
21 #define FONT_CACHE_STATS 0
22 #if FONT_CACHE_STATS
23 static int g_PurgeCount = 0;
24 #endif
25
GrFontCache(GrGpu * gpu)26 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
27 gpu->ref();
28 for (int i = 0; i < kAtlasCount; ++i) {
29 fAtlasMgr[i] = NULL;
30 }
31
32 fHead = fTail = NULL;
33 }
34
~GrFontCache()35 GrFontCache::~GrFontCache() {
36 fCache.deleteAll();
37 for (int i = 0; i < kAtlasCount; ++i) {
38 delete fAtlasMgr[i];
39 }
40 fGpu->unref();
41 #if FONT_CACHE_STATS
42 GrPrintf("Num purges: %d\n", g_PurgeCount);
43 #endif
44 }
45
mask_format_to_pixel_config(GrMaskFormat format)46 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
47 static const GrPixelConfig sPixelConfigs[] = {
48 kAlpha_8_GrPixelConfig,
49 kRGB_565_GrPixelConfig,
50 kSkia8888_GrPixelConfig,
51 kSkia8888_GrPixelConfig
52 };
53 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
54
55 return sPixelConfigs[format];
56 }
57
mask_format_to_atlas_index(GrMaskFormat format)58 static int mask_format_to_atlas_index(GrMaskFormat format) {
59 static const int sAtlasIndices[] = {
60 GrFontCache::kA8_AtlasType,
61 GrFontCache::k565_AtlasType,
62 GrFontCache::k8888_AtlasType,
63 GrFontCache::k8888_AtlasType
64 };
65 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
66
67 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
68 return sAtlasIndices[format];
69 }
70
generateStrike(GrFontScaler * scaler,const Key & key)71 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
72 const Key& key) {
73 GrMaskFormat format = scaler->getMaskFormat();
74 GrPixelConfig config = mask_format_to_pixel_config(format);
75 int atlasIndex = mask_format_to_atlas_index(format);
76 if (NULL == fAtlasMgr[atlasIndex]) {
77 fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config));
78 }
79 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
80 (this, scaler->getKey(), format, fAtlasMgr[atlasIndex]));
81 fCache.insert(key, strike);
82
83 if (fHead) {
84 fHead->fPrev = strike;
85 } else {
86 SkASSERT(NULL == fTail);
87 fTail = strike;
88 }
89 strike->fPrev = NULL;
90 strike->fNext = fHead;
91 fHead = strike;
92
93 return strike;
94 }
95
freeAll()96 void GrFontCache::freeAll() {
97 fCache.deleteAll();
98 for (int i = 0; i < kAtlasCount; ++i) {
99 delete fAtlasMgr[i];
100 fAtlasMgr[i] = NULL;
101 }
102 fHead = NULL;
103 fTail = NULL;
104 }
105
purgeStrike(GrTextStrike * strike)106 void GrFontCache::purgeStrike(GrTextStrike* strike) {
107 const GrFontCache::Key key(strike->fFontScalerKey);
108 fCache.remove(key, strike);
109 this->detachStrikeFromList(strike);
110 delete strike;
111 }
112
purgeExceptFor(GrTextStrike * preserveStrike)113 void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
114 SkASSERT(NULL != preserveStrike);
115 GrTextStrike* strike = fTail;
116 bool purge = true;
117 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
118 while (strike) {
119 if (strike == preserveStrike || maskFormat != strike->fMaskFormat) {
120 strike = strike->fPrev;
121 continue;
122 }
123 GrTextStrike* strikeToPurge = strike;
124 strike = strikeToPurge->fPrev;
125 if (purge) {
126 // keep purging if we won't free up any atlases with this strike.
127 purge = strikeToPurge->fAtlas.isEmpty();
128 this->purgeStrike(strikeToPurge);
129 }
130 }
131 #if FONT_CACHE_STATS
132 ++g_PurgeCount;
133 #endif
134 }
135
freePlotExceptFor(GrTextStrike * preserveStrike)136 void GrFontCache::freePlotExceptFor(GrTextStrike* preserveStrike) {
137 SkASSERT(NULL != preserveStrike);
138 GrTextStrike* strike = fTail;
139 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
140 while (strike) {
141 if (strike == preserveStrike || maskFormat != strike->fMaskFormat) {
142 strike = strike->fPrev;
143 continue;
144 }
145 GrTextStrike* strikeToPurge = strike;
146 strike = strikeToPurge->fPrev;
147 if (strikeToPurge->removeUnusedPlots()) {
148 if (strikeToPurge->fAtlas.isEmpty()) {
149 this->purgeStrike(strikeToPurge);
150 }
151 break;
152 }
153 }
154 }
155
156 #ifdef SK_DEBUG
validate() const157 void GrFontCache::validate() const {
158 int count = fCache.count();
159 if (0 == count) {
160 SkASSERT(!fHead);
161 SkASSERT(!fTail);
162 } else if (1 == count) {
163 SkASSERT(fHead == fTail);
164 } else {
165 SkASSERT(fHead != fTail);
166 }
167
168 int count2 = 0;
169 const GrTextStrike* strike = fHead;
170 while (strike) {
171 count2 += 1;
172 strike = strike->fNext;
173 }
174 SkASSERT(count == count2);
175
176 count2 = 0;
177 strike = fTail;
178 while (strike) {
179 count2 += 1;
180 strike = strike->fPrev;
181 }
182 SkASSERT(count == count2);
183 }
184 #endif
185
186 #ifdef SK_DEVELOPER
dump() const187 void GrFontCache::dump() const {
188 static int gDumpCount = 0;
189 for (int i = 0; i < kAtlasCount; ++i) {
190 if (NULL != fAtlasMgr[i]) {
191 GrTexture* texture = fAtlasMgr[i]->getTexture();
192 if (NULL != texture) {
193 SkString filename;
194 filename.printf("fontcache_%d%d.png", gDumpCount, i);
195 texture->savePixels(filename.c_str());
196 }
197 }
198 }
199 ++gDumpCount;
200 }
201 #endif
202
203 ///////////////////////////////////////////////////////////////////////////////
204
205 #ifdef SK_DEBUG
206 static int gCounter;
207 #endif
208
209 #if SK_DISTANCEFIELD_FONTS
210 #define DISTANCE_FIELD_PAD 4
211 #define DISTANCE_FIELD_RANGE (4.0)
212 #endif
213
214 /*
215 The text strike is specific to a given font/style/matrix setup, which is
216 represented by the GrHostFontScaler object we are given in getGlyph().
217
218 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
219 atlas and a position within that texture.
220 */
221
GrTextStrike(GrFontCache * cache,const GrKey * key,GrMaskFormat format,GrAtlasMgr * atlasMgr)222 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
223 GrMaskFormat format,
224 GrAtlasMgr* atlasMgr) : fPool(64), fAtlas(atlasMgr) {
225 fFontScalerKey = key;
226 fFontScalerKey->ref();
227
228 fFontCache = cache; // no need to ref, it won't go away before we do
229 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
230
231 fMaskFormat = format;
232
233 #ifdef SK_DEBUG
234 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
235 gCounter += 1;
236 #endif
237 }
238
239 // these signatures are needed because they're used with
240 // SkTDArray::visitAll() (see destructor & removeUnusedAtlases())
free_glyph(GrGlyph * & glyph)241 static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
242
invalidate_glyph(GrGlyph * & glyph)243 static void invalidate_glyph(GrGlyph*& glyph) {
244 if (glyph->fPlot && glyph->fPlot->drawToken().isIssued()) {
245 glyph->fPlot = NULL;
246 }
247 }
248
~GrTextStrike()249 GrTextStrike::~GrTextStrike() {
250 fFontScalerKey->unref();
251 fCache.getArray().visitAll(free_glyph);
252
253 #ifdef SK_DEBUG
254 gCounter -= 1;
255 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
256 #endif
257 }
258
generateGlyph(GrGlyph::PackedID packed,GrFontScaler * scaler)259 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
260 GrFontScaler* scaler) {
261 SkIRect bounds;
262 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
263 return NULL;
264 }
265
266 GrGlyph* glyph = fPool.alloc();
267 #if SK_DISTANCEFIELD_FONTS
268 // expand bounds to hold full distance field data
269 if (fUseDistanceField) {
270 bounds.fLeft -= DISTANCE_FIELD_PAD;
271 bounds.fRight += DISTANCE_FIELD_PAD;
272 bounds.fTop -= DISTANCE_FIELD_PAD;
273 bounds.fBottom += DISTANCE_FIELD_PAD;
274 }
275 #endif
276 glyph->init(packed, bounds);
277 fCache.insert(packed, glyph);
278 return glyph;
279 }
280
removeUnusedPlots()281 bool GrTextStrike::removeUnusedPlots() {
282 fCache.getArray().visitAll(invalidate_glyph);
283 return fAtlasMgr->removeUnusedPlots(&fAtlas);
284 }
285
286
getGlyphAtlas(GrGlyph * glyph,GrFontScaler * scaler)287 bool GrTextStrike::getGlyphAtlas(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 GrPlot* plot;
303 #if SK_DISTANCEFIELD_FONTS
304 if (fUseDistanceField) {
305 SkASSERT(1 == bytesPerPixel);
306
307 // we've already expanded the glyph dimensions to match the final size
308 // but must shrink back down to get the packed glyph data
309 int dfWidth = glyph->width();
310 int dfHeight = glyph->height();
311 int width = dfWidth - 2*DISTANCE_FIELD_PAD;
312 int height = dfHeight - 2*DISTANCE_FIELD_PAD;
313 size_t stride = width*bytesPerPixel;
314
315 size_t size = width * height * bytesPerPixel;
316 SkAutoSMalloc<1024> storage(size);
317 if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) {
318 return false;
319 }
320
321 // alloc storage for distance field glyph
322 size_t dfSize = dfWidth * dfHeight * bytesPerPixel;
323 SkAutoSMalloc<1024> dfStorage(dfSize);
324
325 // copy glyph into distance field storage
326 sk_bzero(dfStorage.get(), dfSize);
327
328 unsigned char* ptr = (unsigned char*) storage.get();
329 unsigned char* dfPtr = (unsigned char*) dfStorage.get();
330 size_t dfStride = dfWidth*bytesPerPixel;
331 dfPtr += DISTANCE_FIELD_PAD*dfStride;
332 dfPtr += DISTANCE_FIELD_PAD*bytesPerPixel;
333
334 for (int i = 0; i < height; ++i) {
335 memcpy(dfPtr, ptr, stride);
336
337 dfPtr += dfStride;
338 ptr += stride;
339 }
340
341 // generate distance field data
342 SkAutoSMalloc<1024> distXStorage(dfWidth*dfHeight*sizeof(short));
343 SkAutoSMalloc<1024> distYStorage(dfWidth*dfHeight*sizeof(short));
344 SkAutoSMalloc<1024> outerDistStorage(dfWidth*dfHeight*sizeof(double));
345 SkAutoSMalloc<1024> innerDistStorage(dfWidth*dfHeight*sizeof(double));
346 SkAutoSMalloc<1024> gxStorage(dfWidth*dfHeight*sizeof(double));
347 SkAutoSMalloc<1024> gyStorage(dfWidth*dfHeight*sizeof(double));
348
349 short* distX = (short*) distXStorage.get();
350 short* distY = (short*) distYStorage.get();
351 double* outerDist = (double*) outerDistStorage.get();
352 double* innerDist = (double*) innerDistStorage.get();
353 double* gx = (double*) gxStorage.get();
354 double* gy = (double*) gyStorage.get();
355
356 dfPtr = (unsigned char*) dfStorage.get();
357 EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
358 EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, outerDist);
359
360 for (int i = 0; i < dfWidth*dfHeight; ++i) {
361 *dfPtr = 255 - *dfPtr;
362 dfPtr++;
363 }
364 dfPtr = (unsigned char*) dfStorage.get();
365 sk_bzero(gx, sizeof(double)*dfWidth*dfHeight);
366 sk_bzero(gy, sizeof(double)*dfWidth*dfHeight);
367 EDTAA::computegradient(dfPtr, dfWidth, dfHeight, gx, gy);
368 EDTAA::edtaa3(dfPtr, gx, gy, dfWidth, dfHeight, distX, distY, innerDist);
369
370 for (int i = 0; i < dfWidth*dfHeight; ++i) {
371 unsigned char val;
372 double outerval = outerDist[i];
373 if (outerval < 0.0) {
374 outerval = 0.0;
375 }
376 double innerval = innerDist[i];
377 if (innerval < 0.0) {
378 innerval = 0.0;
379 }
380 double dist = outerval - innerval;
381 if (dist <= -DISTANCE_FIELD_RANGE) {
382 val = 255;
383 } else if (dist > DISTANCE_FIELD_RANGE) {
384 val = 0;
385 } else {
386 val = (unsigned char)((DISTANCE_FIELD_RANGE-dist)*128.0/DISTANCE_FIELD_RANGE);
387 }
388 *dfPtr++ = val;
389 }
390
391 // copy to atlas
392 plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(),
393 &glyph->fAtlasLocation);
394
395 } else {
396 #endif
397 size_t size = glyph->fBounds.area() * bytesPerPixel;
398 SkAutoSMalloc<1024> storage(size);
399 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
400 glyph->height(),
401 glyph->width() * bytesPerPixel,
402 storage.get())) {
403 return false;
404 }
405
406 plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
407 glyph->height(), storage.get(),
408 &glyph->fAtlasLocation);
409 #if SK_DISTANCEFIELD_FONTS
410 }
411 #endif
412
413 if (NULL == plot) {
414 return false;
415 }
416
417 glyph->fPlot = plot;
418 return true;
419 }
420