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 "GrSurfacePriv.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() {
43 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
44 while (!iter.done()) {
48 for (int i = 0; i < kAtlasCount; ++i) {
53 SkDebugf("Num purges: %d\n", g_PurgeCount);
57 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
58 static const GrPixelConfig sPixelConfigs[] = {
59 kAlpha_8_GrPixelConfig,
60 kRGB_565_GrPixelConfig,
61 kSkia8888_GrPixelConfig,
62 kSkia8888_GrPixelConfig
64 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch);
66 return sPixelConfigs[format];
69 static int mask_format_to_atlas_index(GrMaskFormat format) {
70 static const int sAtlasIndices[] = {
71 GrFontCache::kA8_AtlasType,
72 GrFontCache::k565_AtlasType,
73 GrFontCache::k8888_AtlasType,
74 GrFontCache::k8888_AtlasType
76 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
78 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
79 return sAtlasIndices[format];
82 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
83 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey()));
87 fHead->fPrev = strike;
89 SkASSERT(NULL == fTail);
93 strike->fNext = fHead;
99 void GrFontCache::freeAll() {
100 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
101 while (!iter.done()) {
106 for (int i = 0; i < kAtlasCount; ++i) {
114 void GrFontCache::purgeStrike(GrTextStrike* strike) {
115 fCache.remove(*(strike->fFontScalerKey));
116 this->detachStrikeFromList(strike);
121 GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* usage,
122 int width, int height, const void* image,
124 GrPixelConfig config = mask_format_to_pixel_config(format);
125 int atlasIndex = mask_format_to_atlas_index(format);
126 if (NULL == fAtlases[atlasIndex]) {
127 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
128 GR_ATLAS_TEXTURE_HEIGHT);
129 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSurfaceFlags,
135 return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc);
139 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* glyph) {
140 SkASSERT(preserveStrike);
142 int index = mask_format_to_atlas_index(glyph->fMaskFormat);
143 GrAtlas* atlas = fAtlases[index];
144 GrPlot* plot = atlas->getUnusedPlot();
150 GrTextStrike* strike = fHead;
152 GrTextStrike* strikeToPurge = strike;
153 strike = strikeToPurge->fNext;
154 strikeToPurge->removePlot(plot);
156 // clear out any empty strikes (except this one)
157 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) {
158 this->purgeStrike(strikeToPurge);
170 void GrFontCache::validate() const {
171 int count = fCache.count();
175 } else if (1 == count) {
176 SkASSERT(fHead == fTail);
178 SkASSERT(fHead != fTail);
182 const GrTextStrike* strike = fHead;
185 strike = strike->fNext;
187 SkASSERT(count == count2);
193 strike = strike->fPrev;
195 SkASSERT(count == count2);
199 void GrFontCache::dump() const {
200 static int gDumpCount = 0;
201 for (int i = 0; i < kAtlasCount; ++i) {
203 GrTexture* texture = fAtlases[i]->getTexture();
206 #ifdef SK_BUILD_FOR_ANDROID
207 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
209 filename.printf("fontcache_%d%d.png", gDumpCount, i);
211 texture->surfacePriv().savePixels(filename.c_str());
218 ///////////////////////////////////////////////////////////////////////////////
225 The text strike is specific to a given font/style/matrix setup, which is
226 represented by the GrHostFontScaler object we are given in getGlyph().
228 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
229 atlas and a position within that texture.
232 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) : fPool(64) {
233 fFontScalerKey = key;
234 fFontScalerKey->ref();
236 fFontCache = cache; // no need to ref, it won't go away before we do
239 // SkDebugf(" GrTextStrike %p %d\n", this, gCounter);
244 GrTextStrike::~GrTextStrike() {
245 fFontScalerKey->unref();
246 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
247 while (!iter.done()) {
254 // SkDebugf("~GrTextStrike %p %d\n", this, gCounter);
258 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
259 GrFontScaler* scaler) {
261 if (fUseDistanceField) {
262 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
266 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
270 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
272 GrGlyph* glyph = fPool.alloc();
273 glyph->init(packed, bounds, format);
278 void GrTextStrike::removePlot(const GrPlot* plot) {
279 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
280 while (!iter.done()) {
281 if (plot == (*iter).fPlot) {
282 (*iter).fPlot = NULL;
287 GrAtlas::RemovePlot(&fPlotUsage, plot);
290 bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
291 int width = glyph->fBounds.width();
292 int height = glyph->fBounds.height();
293 int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
294 if (width + pad > GR_PLOT_WIDTH) {
297 if (height + pad > GR_PLOT_HEIGHT) {
304 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
305 #if 0 // testing hack to force us to flush our cache often
307 if ((++gCounter % 10) == 0) return false;
312 SkASSERT(fCache.find(glyph->fPackedID));
313 SkASSERT(NULL == glyph->fPlot);
315 SkAutoUnref ar(SkSafeRef(scaler));
317 int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
319 size_t size = glyph->fBounds.area() * bytesPerPixel;
320 GrAutoMalloc<1024> storage(size);
322 if (fUseDistanceField) {
323 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
329 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
331 glyph->width() * bytesPerPixel,
337 GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage,
338 glyph->width(), glyph->height(),
339 storage.get(), &glyph->fAtlasLocation);