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.
9 #include "GrRectanizer.h"
10 #include "GrTextStrike.h"
11 #include "GrTextStrike_impl.h"
14 #include "SkDistanceFieldGen.h"
16 ///////////////////////////////////////////////////////////////////////////////
18 #define GR_ATLAS_TEXTURE_WIDTH 1024
19 #define GR_ATLAS_TEXTURE_HEIGHT 2048
21 #define GR_PLOT_WIDTH 256
22 #define GR_PLOT_HEIGHT 256
24 #define GR_NUM_PLOTS_X (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
25 #define GR_NUM_PLOTS_Y (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
27 #define FONT_CACHE_STATS 0
29 static int g_PurgeCount = 0;
32 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
34 for (int i = 0; i < kAtlasCount; ++i) {
41 GrFontCache::~GrFontCache() {
42 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
43 while (!iter.done()) {
47 for (int i = 0; i < kAtlasCount; ++i) {
52 GrPrintf("Num purges: %d\n", g_PurgeCount);
56 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
57 static const GrPixelConfig sPixelConfigs[] = {
58 kAlpha_8_GrPixelConfig,
59 kRGB_565_GrPixelConfig,
60 kSkia8888_GrPixelConfig,
61 kSkia8888_GrPixelConfig
63 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
65 return sPixelConfigs[format];
68 static int mask_format_to_atlas_index(GrMaskFormat format) {
69 static const int sAtlasIndices[] = {
70 GrFontCache::kA8_AtlasType,
71 GrFontCache::k565_AtlasType,
72 GrFontCache::k8888_AtlasType,
73 GrFontCache::k8888_AtlasType
75 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
77 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
78 return sAtlasIndices[format];
81 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
82 GrMaskFormat format = scaler->getMaskFormat();
83 GrPixelConfig config = mask_format_to_pixel_config(format);
84 int atlasIndex = mask_format_to_atlas_index(format);
85 if (NULL == fAtlases[atlasIndex]) {
86 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
87 GR_ATLAS_TEXTURE_HEIGHT);
88 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
94 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
95 (this, scaler->getKey(), format, fAtlases[atlasIndex]));
99 fHead->fPrev = strike;
101 SkASSERT(NULL == fTail);
104 strike->fPrev = NULL;
105 strike->fNext = fHead;
111 void GrFontCache::freeAll() {
112 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
113 while (!iter.done()) {
118 for (int i = 0; i < kAtlasCount; ++i) {
126 void GrFontCache::purgeStrike(GrTextStrike* strike) {
127 fCache.remove(*(strike->fFontScalerKey));
128 this->detachStrikeFromList(strike);
132 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
133 SkASSERT(NULL != preserveStrike);
135 GrAtlas* atlas = preserveStrike->fAtlas;
136 GrPlot* plot = atlas->getUnusedPlot();
142 GrTextStrike* strike = fHead;
143 GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
145 if (maskFormat != strike->fMaskFormat) {
146 strike = strike->fNext;
150 GrTextStrike* strikeToPurge = strike;
151 strike = strikeToPurge->fNext;
152 strikeToPurge->removePlot(plot);
154 // clear out any empty strikes (except this one)
155 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
156 this->purgeStrike(strikeToPurge);
168 void GrFontCache::validate() const {
169 int count = fCache.count();
173 } else if (1 == count) {
174 SkASSERT(fHead == fTail);
176 SkASSERT(fHead != fTail);
180 const GrTextStrike* strike = fHead;
183 strike = strike->fNext;
185 SkASSERT(count == count2);
191 strike = strike->fPrev;
193 SkASSERT(count == count2);
197 void GrFontCache::dump() const {
198 static int gDumpCount = 0;
199 for (int i = 0; i < kAtlasCount; ++i) {
200 if (NULL != fAtlases[i]) {
201 GrTexture* texture = fAtlases[i]->getTexture();
202 if (NULL != texture) {
204 #ifdef SK_BUILD_FOR_ANDROID
205 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
207 filename.printf("fontcache_%d%d.png", gDumpCount, i);
209 texture->savePixels(filename.c_str());
216 ///////////////////////////////////////////////////////////////////////////////
223 The text strike is specific to a given font/style/matrix setup, which is
224 represented by the GrHostFontScaler object we are given in getGlyph().
226 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
227 atlas and a position within that texture.
230 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key,
232 GrAtlas* atlas) : fPool(64) {
233 fFontScalerKey = key;
234 fFontScalerKey->ref();
236 fFontCache = cache; // no need to ref, it won't go away before we do
237 fAtlas = atlas; // no need to ref, it won't go away before we do
239 fMaskFormat = format;
242 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
247 GrTextStrike::~GrTextStrike() {
248 fFontScalerKey->unref();
249 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
250 while (!iter.done()) {
257 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
261 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
262 GrFontScaler* scaler) {
264 if (fUseDistanceField) {
265 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
269 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
274 GrGlyph* glyph = fPool.alloc();
275 glyph->init(packed, bounds);
280 void GrTextStrike::removePlot(const GrPlot* plot) {
281 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
282 while (!iter.done()) {
283 if (plot == (*iter).fPlot) {
284 (*iter).fPlot = NULL;
289 GrAtlas::RemovePlot(&fPlotUsage, plot);
293 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
294 #if 0 // testing hack to force us to flush our cache often
296 if ((++gCounter % 10) == 0) return false;
301 SkASSERT(fCache.find(glyph->fPackedID));
302 SkASSERT(NULL == glyph->fPlot);
304 SkAutoUnref ar(SkSafeRef(scaler));
306 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
308 size_t size = glyph->fBounds.area() * bytesPerPixel;
309 GrAutoMalloc<1024> storage(size);
311 if (fUseDistanceField) {
312 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
318 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
320 glyph->width() * bytesPerPixel,
326 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, glyph->width(),
327 glyph->height(), storage.get(),
328 &glyph->fAtlasLocation);