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 "GrGpu.h" 9 #include "GrRectanizer.h" 10 #include "GrTextStrike.h" 11 #include "GrTextStrike_impl.h" 12 #include "SkString.h" 13 14 #include "SkDistanceFieldGen.h" 15 16 /////////////////////////////////////////////////////////////////////////////// 17 18 #define GR_ATLAS_TEXTURE_WIDTH 1024 19 #define GR_ATLAS_TEXTURE_HEIGHT 2048 20 21 #define GR_PLOT_WIDTH 256 22 #define GR_PLOT_HEIGHT 256 23 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) 26 27 #define FONT_CACHE_STATS 0 28 #if FONT_CACHE_STATS 29 static int g_PurgeCount = 0; 30 #endif 31 32 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { 33 gpu->ref(); 34 for (int i = 0; i < kAtlasCount; ++i) { 35 fAtlases[i] = NULL; 36 } 37 38 fHead = fTail = NULL; 39 } 40 41 GrFontCache::~GrFontCache() { 42 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache); 43 while (!iter.done()) { 44 SkDELETE(&(*iter)); 45 ++iter; 46 } 47 for (int i = 0; i < kAtlasCount; ++i) { 48 delete fAtlases[i]; 49 } 50 fGpu->unref(); 51 #if FONT_CACHE_STATS 52 GrPrintf("Num purges: %d\n", g_PurgeCount); 53 #endif 54 } 55 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 62 }; 63 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch); 64 65 return sPixelConfigs[format]; 66 } 67 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 74 }; 75 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch); 76 77 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); 78 return sAtlasIndices[format]; 79 } 80 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, 89 textureSize, 90 GR_NUM_PLOTS_X, 91 GR_NUM_PLOTS_Y, 92 true)); 93 } 94 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, 95 (this, scaler->getKey(), format, fAtlases[atlasIndex])); 96 fCache.add(strike); 97 98 if (fHead) { 99 fHead->fPrev = strike; 100 } else { 101 SkASSERT(NULL == fTail); 102 fTail = strike; 103 } 104 strike->fPrev = NULL; 105 strike->fNext = fHead; 106 fHead = strike; 107 108 return strike; 109 } 110 111 void GrFontCache::freeAll() { 112 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache); 113 while (!iter.done()) { 114 SkDELETE(&(*iter)); 115 ++iter; 116 } 117 fCache.rewind(); 118 for (int i = 0; i < kAtlasCount; ++i) { 119 delete fAtlases[i]; 120 fAtlases[i] = NULL; 121 } 122 fHead = NULL; 123 fTail = NULL; 124 } 125 126 void GrFontCache::purgeStrike(GrTextStrike* strike) { 127 fCache.remove(*(strike->fFontScalerKey)); 128 this->detachStrikeFromList(strike); 129 delete strike; 130 } 131 132 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { 133 SkASSERT(preserveStrike); 134 135 GrAtlas* atlas = preserveStrike->fAtlas; 136 GrPlot* plot = atlas->getUnusedPlot(); 137 if (NULL == plot) { 138 return false; 139 } 140 plot->resetRects(); 141 142 GrTextStrike* strike = fHead; 143 GrMaskFormat maskFormat = preserveStrike->fMaskFormat; 144 while (strike) { 145 if (maskFormat != strike->fMaskFormat) { 146 strike = strike->fNext; 147 continue; 148 } 149 150 GrTextStrike* strikeToPurge = strike; 151 strike = strikeToPurge->fNext; 152 strikeToPurge->removePlot(plot); 153 154 // clear out any empty strikes (except this one) 155 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty()) { 156 this->purgeStrike(strikeToPurge); 157 } 158 } 159 160 #if FONT_CACHE_STATS 161 ++g_PurgeCount; 162 #endif 163 164 return true; 165 } 166 167 #ifdef SK_DEBUG 168 void GrFontCache::validate() const { 169 int count = fCache.count(); 170 if (0 == count) { 171 SkASSERT(!fHead); 172 SkASSERT(!fTail); 173 } else if (1 == count) { 174 SkASSERT(fHead == fTail); 175 } else { 176 SkASSERT(fHead != fTail); 177 } 178 179 int count2 = 0; 180 const GrTextStrike* strike = fHead; 181 while (strike) { 182 count2 += 1; 183 strike = strike->fNext; 184 } 185 SkASSERT(count == count2); 186 187 count2 = 0; 188 strike = fTail; 189 while (strike) { 190 count2 += 1; 191 strike = strike->fPrev; 192 } 193 SkASSERT(count == count2); 194 } 195 #endif 196 197 void GrFontCache::dump() const { 198 static int gDumpCount = 0; 199 for (int i = 0; i < kAtlasCount; ++i) { 200 if (fAtlases[i]) { 201 GrTexture* texture = fAtlases[i]->getTexture(); 202 if (texture) { 203 SkString filename; 204 #ifdef SK_BUILD_FOR_ANDROID 205 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i); 206 #else 207 filename.printf("fontcache_%d%d.png", gDumpCount, i); 208 #endif 209 texture->savePixels(filename.c_str()); 210 } 211 } 212 } 213 ++gDumpCount; 214 } 215 216 /////////////////////////////////////////////////////////////////////////////// 217 218 #ifdef SK_DEBUG 219 static int gCounter; 220 #endif 221 222 /* 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(). 225 226 We map a 32bit glyphID to a GrGlyph record, which in turn points to a 227 atlas and a position within that texture. 228 */ 229 230 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key, 231 GrMaskFormat format, 232 GrAtlas* atlas) : fPool(64) { 233 fFontScalerKey = key; 234 fFontScalerKey->ref(); 235 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 238 239 fMaskFormat = format; 240 241 #ifdef SK_DEBUG 242 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); 243 gCounter += 1; 244 #endif 245 } 246 247 GrTextStrike::~GrTextStrike() { 248 fFontScalerKey->unref(); 249 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache); 250 while (!iter.done()) { 251 (*iter).free(); 252 ++iter; 253 } 254 255 #ifdef SK_DEBUG 256 gCounter -= 1; 257 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter); 258 #endif 259 } 260 261 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, 262 GrFontScaler* scaler) { 263 SkIRect bounds; 264 if (fUseDistanceField) { 265 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) { 266 return NULL; 267 } 268 } else { 269 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { 270 return NULL; 271 } 272 } 273 274 GrGlyph* glyph = fPool.alloc(); 275 glyph->init(packed, bounds); 276 fCache.add(glyph); 277 return glyph; 278 } 279 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; 285 } 286 ++iter; 287 } 288 289 GrAtlas::RemovePlot(&fPlotUsage, plot); 290 } 291 292 bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) { 293 int width = glyph->fBounds.width(); 294 int height = glyph->fBounds.height(); 295 int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0; 296 if (width + pad > GR_PLOT_WIDTH) { 297 return true; 298 } 299 if (height + pad > GR_PLOT_HEIGHT) { 300 return true; 301 } 302 303 return false; 304 } 305 306 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { 307 #if 0 // testing hack to force us to flush our cache often 308 static int gCounter; 309 if ((++gCounter % 10) == 0) return false; 310 #endif 311 312 SkASSERT(glyph); 313 SkASSERT(scaler); 314 SkASSERT(fCache.find(glyph->fPackedID)); 315 SkASSERT(NULL == glyph->fPlot); 316 317 SkAutoUnref ar(SkSafeRef(scaler)); 318 319 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); 320 321 size_t size = glyph->fBounds.area() * bytesPerPixel; 322 GrAutoMalloc<1024> storage(size); 323 324 if (fUseDistanceField) { 325 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(), 326 glyph->height(), 327 storage.get())) { 328 return false; 329 } 330 } else { 331 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), 332 glyph->height(), 333 glyph->width() * bytesPerPixel, 334 storage.get())) { 335 return false; 336 } 337 } 338 339 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, glyph->width(), 340 glyph->height(), storage.get(), 341 &glyph->fAtlasLocation); 342 343 if (NULL == plot) { 344 return false; 345 } 346 347 glyph->fPlot = plot; 348 return true; 349 } 350