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 GR_ATLAS_TEXTURE_WIDTH 1024
20 #define GR_ATLAS_TEXTURE_HEIGHT 2048
22 #define GR_PLOT_WIDTH 256
23 #define GR_PLOT_HEIGHT 256
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)
28 #define FONT_CACHE_STATS 0
30 static int g_PurgeCount = 0;
33 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
35 for (int i = 0; i < kAtlasCount; ++i) {
42 GrFontCache::~GrFontCache() {
44 for (int i = 0; i < kAtlasCount; ++i) {
49 GrPrintf("Num purges: %d\n", g_PurgeCount);
53 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
54 static const GrPixelConfig sPixelConfigs[] = {
55 kAlpha_8_GrPixelConfig,
56 kRGB_565_GrPixelConfig,
57 kSkia8888_GrPixelConfig,
58 kSkia8888_GrPixelConfig
60 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
62 return sPixelConfigs[format];
65 static int mask_format_to_atlas_index(GrMaskFormat format) {
66 static const int sAtlasIndices[] = {
67 GrFontCache::kA8_AtlasType,
68 GrFontCache::k565_AtlasType,
69 GrFontCache::k8888_AtlasType,
70 GrFontCache::k8888_AtlasType
72 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
74 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
75 return sAtlasIndices[format];
78 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
80 GrMaskFormat format = scaler->getMaskFormat();
81 GrPixelConfig config = mask_format_to_pixel_config(format);
82 int atlasIndex = mask_format_to_atlas_index(format);
83 if (NULL == fAtlasMgr[atlasIndex]) {
84 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
85 GR_ATLAS_TEXTURE_HEIGHT);
86 fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config,
91 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
92 (this, scaler->getKey(), format, fAtlasMgr[atlasIndex]));
93 fCache.insert(key, strike);
96 fHead->fPrev = strike;
98 SkASSERT(NULL == fTail);
101 strike->fPrev = NULL;
102 strike->fNext = fHead;
108 void GrFontCache::freeAll() {
110 for (int i = 0; i < kAtlasCount; ++i) {
118 void GrFontCache::purgeStrike(GrTextStrike* strike) {
119 const GrFontCache::Key key(strike->fFontScalerKey);
120 fCache.remove(key, strike);
121 this->detachStrikeFromList(strike);
125 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
126 SkASSERT(NULL != preserveStrike);
128 GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr;
129 GrPlot* plot = atlasMgr->getUnusedPlot();
135 GrTextStrike* strike = fHead;
136 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
138 if (maskFormat != strike->fMaskFormat) {
139 strike = strike->fNext;
143 GrTextStrike* strikeToPurge = strike;
144 strike = strikeToPurge->fNext;
145 strikeToPurge->removePlot(plot);
147 // clear out any empty strikes (except this one)
148 if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) {
149 this->purgeStrike(strikeToPurge);
161 void GrFontCache::validate() const {
162 int count = fCache.count();
166 } else if (1 == count) {
167 SkASSERT(fHead == fTail);
169 SkASSERT(fHead != fTail);
173 const GrTextStrike* strike = fHead;
176 strike = strike->fNext;
178 SkASSERT(count == count2);
184 strike = strike->fPrev;
186 SkASSERT(count == count2);
191 void GrFontCache::dump() const {
192 static int gDumpCount = 0;
193 for (int i = 0; i < kAtlasCount; ++i) {
194 if (NULL != fAtlasMgr[i]) {
195 GrTexture* texture = fAtlasMgr[i]->getTexture();
196 if (NULL != texture) {
198 #ifdef SK_BUILD_FOR_ANDROID
199 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
201 filename.printf("fontcache_%d%d.png", gDumpCount, i);
203 texture->savePixels(filename.c_str());
211 ///////////////////////////////////////////////////////////////////////////////
218 The text strike is specific to a given font/style/matrix setup, which is
219 represented by the GrHostFontScaler object we are given in getGlyph().
221 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
222 atlas and a position within that texture.
225 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
227 GrAtlasMgr* atlasMgr) : fPool(64) {
228 fFontScalerKey = key;
229 fFontScalerKey->ref();
231 fFontCache = cache; // no need to ref, it won't go away before we do
232 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do
234 fMaskFormat = format;
237 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
242 // this signature is needed because it's used with
243 // SkTDArray::visitAll() (see destructor)
244 static void free_glyph(GrGlyph*& glyph) { glyph->free(); }
246 GrTextStrike::~GrTextStrike() {
247 fFontScalerKey->unref();
248 fCache.getArray().visitAll(free_glyph);
252 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
256 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
257 GrFontScaler* scaler) {
259 if (fUseDistanceField) {
260 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
264 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
269 GrGlyph* glyph = fPool.alloc();
270 glyph->init(packed, bounds);
271 fCache.insert(packed, glyph);
275 void GrTextStrike::removePlot(const GrPlot* plot) {
276 SkTDArray<GrGlyph*>& glyphArray = fCache.getArray();
277 for (int i = 0; i < glyphArray.count(); ++i) {
278 if (plot == glyphArray[i]->fPlot) {
279 glyphArray[i]->fPlot = NULL;
283 fAtlasMgr->removePlot(&fAtlas, plot);
287 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
288 #if 0 // testing hack to force us to flush our cache often
290 if ((++gCounter % 10) == 0) return false;
295 SkASSERT(fCache.contains(glyph));
296 SkASSERT(NULL == glyph->fPlot);
298 SkAutoRef ar(scaler);
300 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
302 size_t size = glyph->fBounds.area() * bytesPerPixel;
303 SkAutoSMalloc<1024> storage(size);
304 if (fUseDistanceField) {
305 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
311 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
313 glyph->width() * bytesPerPixel,
319 GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(),
320 glyph->height(), storage.get(),
321 &glyph->fAtlasLocation);