2 * Copyright 2010 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
10 #include "GrRectanizer.h"
11 #include "GrTextStrike.h"
12 #include "GrTextStrike_impl.h"
15 #include "SkDistanceFieldGen.h"
17 ///////////////////////////////////////////////////////////////////////////////
19 #define FONT_CACHE_STATS 0
21 static int g_PurgeCount = 0;
24 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
26 for (int i = 0; i < kAtlasCount; ++i) {
33 GrFontCache::~GrFontCache() {
35 for (int i = 0; i < kAtlasCount; ++i) {
40 GrPrintf("Num purges: %d\n", g_PurgeCount);
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
51 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
53 return sPixelConfigs[format];
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
63 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
65 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
66 return sAtlasIndices[format];
69 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
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));
77 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
78 (this, scaler->getKey(), format, fAtlasMgr[atlasIndex]));
79 fCache.insert(key, strike);
82 fHead->fPrev = strike;
84 SkASSERT(NULL == fTail);
88 strike->fNext = fHead;
94 void GrFontCache::freeAll() {
96 for (int i = 0; i < kAtlasCount; ++i) {
104 void GrFontCache::purgeStrike(GrTextStrike* strike) {
105 const GrFontCache::Key key(strike->fFontScalerKey);
106 fCache.remove(key, strike);
107 this->detachStrikeFromList(strike);
111 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
112 SkASSERT(NULL != preserveStrike);
114 GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr;
115 GrPlot* plot = atlasMgr->getUnusedPlot();
121 GrTextStrike* strike = fHead;
122 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
124 if (maskFormat != strike->fMaskFormat) {
125 strike = strike->fNext;
129 GrTextStrike* strikeToPurge = strike;
130 strike = strikeToPurge->fNext;
131 strikeToPurge->removePlot(plot);
133 // clear out any empty strikes (except this one)
134 if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) {
135 this->purgeStrike(strikeToPurge);
147 void GrFontCache::validate() const {
148 int count = fCache.count();
152 } else if (1 == count) {
153 SkASSERT(fHead == fTail);
155 SkASSERT(fHead != fTail);
159 const GrTextStrike* strike = fHead;
162 strike = strike->fNext;
164 SkASSERT(count == count2);
170 strike = strike->fPrev;
172 SkASSERT(count == count2);
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) {
184 #ifdef SK_BUILD_FOR_ANDROID
185 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
187 filename.printf("fontcache_%d%d.png", gDumpCount, i);
189 texture->savePixels(filename.c_str());
197 ///////////////////////////////////////////////////////////////////////////////
203 // this acts as the max magnitude for the distance field,
204 // as well as the pad we need around the glyph
205 #define DISTANCE_FIELD_RANGE 4
208 The text strike is specific to a given font/style/matrix setup, which is
209 represented by the GrHostFontScaler object we are given in getGlyph().
211 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
212 atlas and a position within that texture.
215 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
217 GrAtlasMgr* atlasMgr) : fPool(64) {
218 fFontScalerKey = key;
219 fFontScalerKey->ref();
221 fFontCache = cache; // no need to ref, it won't go away before we do
222 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
224 fMaskFormat = format;
227 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
232 // this signature is needed because it's used with
233 // SkTDArray::visitAll() (see destructor)
234 static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
236 GrTextStrike::~GrTextStrike() {
237 fFontScalerKey->unref();
238 fCache.getArray().visitAll(free_glyph);
242 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
246 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
247 GrFontScaler* scaler) {
249 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
253 GrGlyph* glyph = fPool.alloc();
254 // expand bounds to hold full distance field data
256 int pad = DISTANCE_FIELD_RANGE+1;
257 if (fUseDistanceField) {
259 bounds.fRight += pad;
261 bounds.fBottom += pad;
263 glyph->init(packed, bounds);
264 fCache.insert(packed, glyph);
268 void GrTextStrike::removePlot(const GrPlot* plot) {
269 SkTDArray<GrGlyph*>& glyphArray = fCache.getArray();
270 for (int i = 0; i < glyphArray.count(); ++i) {
271 if (plot == glyphArray[i]->fPlot) {
272 glyphArray[i]->fPlot = NULL;
276 fAtlasMgr->removePlot(&fAtlas, plot);
280 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
281 #if 0 // testing hack to force us to flush our cache often
283 if ((++gCounter % 10) == 0) return false;
288 SkASSERT(fCache.contains(glyph));
289 SkASSERT(NULL == glyph->fPlot);
291 SkAutoRef ar(scaler);
293 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
296 if (fUseDistanceField) {
297 // we've already expanded the glyph dimensions to match the final size
298 // but must shrink back down to get the packed glyph data
299 int dfWidth = glyph->width();
300 int dfHeight = glyph->height();
301 int pad = DISTANCE_FIELD_RANGE+1;
302 int width = dfWidth - 2*pad;
303 int height = dfHeight - 2*pad;
304 int stride = width*bytesPerPixel;
306 size_t size = width * height * bytesPerPixel;
307 SkAutoSMalloc<1024> storage(size);
308 if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) {
312 // alloc storage for distance field glyph
313 size_t dfSize = dfWidth * dfHeight * bytesPerPixel;
314 SkAutoSMalloc<1024> dfStorage(dfSize);
316 if (1 == bytesPerPixel) {
317 (void) SkGenerateDistanceFieldFromImage((unsigned char*)dfStorage.get(),
318 (unsigned char*)storage.get(),
319 width, height, DISTANCE_FIELD_RANGE);
321 // TODO: Fix color emoji
322 // for now, copy glyph into distance field storage
323 // this is not correct, but it won't crash
324 sk_bzero(dfStorage.get(), dfSize);
325 unsigned char* ptr = (unsigned char*) storage.get();
326 unsigned char* dfPtr = (unsigned char*) dfStorage.get();
327 size_t dfStride = dfWidth*bytesPerPixel;
328 dfPtr += DISTANCE_FIELD_RANGE*dfStride;
329 dfPtr += DISTANCE_FIELD_RANGE*bytesPerPixel;
331 for (int i = 0; i < height; ++i) {
332 memcpy(dfPtr, ptr, stride);
340 plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(),
341 &glyph->fAtlasLocation);
344 size_t size = glyph->fBounds.area() * bytesPerPixel;
345 SkAutoSMalloc<1024> storage(size);
346 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
348 glyph->width() * bytesPerPixel,
353 plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
354 glyph->height(), storage.get(),
355 &glyph->fAtlasLocation);