• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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