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 fAtlasMgr[i] = NULL; 36 } 37 38 fHead = fTail = NULL; 39 } 40 41 GrFontCache::~GrFontCache() { 42 fCache.deleteAll(); 43 for (int i = 0; i < kAtlasCount; ++i) { 44 delete fAtlasMgr[i]; 45 } 46 fGpu->unref(); 47 #if FONT_CACHE_STATS 48 GrPrintf("Num purges: %d\n", g_PurgeCount); 49 #endif 50 } 51 52 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) { 53 static const GrPixelConfig sPixelConfigs[] = { 54 kAlpha_8_GrPixelConfig, 55 kRGB_565_GrPixelConfig, 56 kSkia8888_GrPixelConfig, 57 kSkia8888_GrPixelConfig 58 }; 59 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch); 60 61 return sPixelConfigs[format]; 62 } 63 64 static int mask_format_to_atlas_index(GrMaskFormat format) { 65 static const int sAtlasIndices[] = { 66 GrFontCache::kA8_AtlasType, 67 GrFontCache::k565_AtlasType, 68 GrFontCache::k8888_AtlasType, 69 GrFontCache::k8888_AtlasType 70 }; 71 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch); 72 73 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); 74 return sAtlasIndices[format]; 75 } 76 77 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, 78 const Key& key) { 79 GrMaskFormat format = scaler->getMaskFormat(); 80 GrPixelConfig config = mask_format_to_pixel_config(format); 81 int atlasIndex = mask_format_to_atlas_index(format); 82 if (NULL == fAtlasMgr[atlasIndex]) { 83 SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, 84 GR_ATLAS_TEXTURE_HEIGHT); 85 fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config, 86 textureSize, 87 GR_NUM_PLOTS_X, 88 GR_NUM_PLOTS_Y, 89 true)); 90 } 91 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, 92 (this, scaler->getKey(), format, fAtlasMgr[atlasIndex])); 93 fCache.insert(key, strike); 94 95 if (fHead) { 96 fHead->fPrev = strike; 97 } else { 98 SkASSERT(NULL == fTail); 99 fTail = strike; 100 } 101 strike->fPrev = NULL; 102 strike->fNext = fHead; 103 fHead = strike; 104 105 return strike; 106 } 107 108 void GrFontCache::freeAll() { 109 fCache.deleteAll(); 110 for (int i = 0; i < kAtlasCount; ++i) { 111 delete fAtlasMgr[i]; 112 fAtlasMgr[i] = NULL; 113 } 114 fHead = NULL; 115 fTail = NULL; 116 } 117 118 void GrFontCache::purgeStrike(GrTextStrike* strike) { 119 const GrFontCache::Key key(strike->fFontScalerKey); 120 fCache.remove(key, strike); 121 this->detachStrikeFromList(strike); 122 delete strike; 123 } 124 125 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { 126 SkASSERT(NULL != preserveStrike); 127 128 GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr; 129 GrPlot* plot = atlasMgr->getUnusedPlot(); 130 if (NULL == plot) { 131 return false; 132 } 133 plot->resetRects(); 134 135 GrTextStrike* strike = fHead; 136 GrMaskFormat maskFormat = preserveStrike->fMaskFormat; 137 while (strike) { 138 if (maskFormat != strike->fMaskFormat) { 139 strike = strike->fNext; 140 continue; 141 } 142 143 GrTextStrike* strikeToPurge = strike; 144 strike = strikeToPurge->fNext; 145 strikeToPurge->removePlot(plot); 146 147 // clear out any empty strikes (except this one) 148 if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) { 149 this->purgeStrike(strikeToPurge); 150 } 151 } 152 153 #if FONT_CACHE_STATS 154 ++g_PurgeCount; 155 #endif 156 157 return true; 158 } 159 160 #ifdef SK_DEBUG 161 void GrFontCache::validate() const { 162 int count = fCache.count(); 163 if (0 == count) { 164 SkASSERT(!fHead); 165 SkASSERT(!fTail); 166 } else if (1 == count) { 167 SkASSERT(fHead == fTail); 168 } else { 169 SkASSERT(fHead != fTail); 170 } 171 172 int count2 = 0; 173 const GrTextStrike* strike = fHead; 174 while (strike) { 175 count2 += 1; 176 strike = strike->fNext; 177 } 178 SkASSERT(count == count2); 179 180 count2 = 0; 181 strike = fTail; 182 while (strike) { 183 count2 += 1; 184 strike = strike->fPrev; 185 } 186 SkASSERT(count == count2); 187 } 188 #endif 189 190 void GrFontCache::dump() const { 191 static int gDumpCount = 0; 192 for (int i = 0; i < kAtlasCount; ++i) { 193 if (NULL != fAtlasMgr[i]) { 194 GrTexture* texture = fAtlasMgr[i]->getTexture(); 195 if (NULL != texture) { 196 SkString filename; 197 #ifdef SK_BUILD_FOR_ANDROID 198 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i); 199 #else 200 filename.printf("fontcache_%d%d.png", gDumpCount, i); 201 #endif 202 texture->savePixels(filename.c_str()); 203 } 204 } 205 } 206 ++gDumpCount; 207 } 208 209 /////////////////////////////////////////////////////////////////////////////// 210 211 #ifdef SK_DEBUG 212 static int gCounter; 213 #endif 214 215 /* 216 The text strike is specific to a given font/style/matrix setup, which is 217 represented by the GrHostFontScaler object we are given in getGlyph(). 218 219 We map a 32bit glyphID to a GrGlyph record, which in turn points to a 220 atlas and a position within that texture. 221 */ 222 223 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, 224 GrMaskFormat format, 225 GrAtlasMgr* atlasMgr) : fPool(64) { 226 fFontScalerKey = key; 227 fFontScalerKey->ref(); 228 229 fFontCache = cache; // no need to ref, it won't go away before we do 230 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do 231 232 fMaskFormat = format; 233 234 #ifdef SK_DEBUG 235 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); 236 gCounter += 1; 237 #endif 238 } 239 240 // this signature is needed because it's used with 241 // SkTDArray::visitAll() (see destructor) 242 static void free_glyph(GrGlyph*& glyph) { glyph->free(); } 243 244 GrTextStrike::~GrTextStrike() { 245 fFontScalerKey->unref(); 246 fCache.getArray().visitAll(free_glyph); 247 248 #ifdef SK_DEBUG 249 gCounter -= 1; 250 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter); 251 #endif 252 } 253 254 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, 255 GrFontScaler* scaler) { 256 SkIRect bounds; 257 if (fUseDistanceField) { 258 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) { 259 return NULL; 260 } 261 } else { 262 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { 263 return NULL; 264 } 265 } 266 267 GrGlyph* glyph = fPool.alloc(); 268 glyph->init(packed, bounds); 269 fCache.insert(packed, glyph); 270 return glyph; 271 } 272 273 void GrTextStrike::removePlot(const GrPlot* plot) { 274 SkTDArray<GrGlyph*>& glyphArray = fCache.getArray(); 275 for (int i = 0; i < glyphArray.count(); ++i) { 276 if (plot == glyphArray[i]->fPlot) { 277 glyphArray[i]->fPlot = NULL; 278 } 279 } 280 281 fAtlasMgr->removePlot(&fAtlas, plot); 282 } 283 284 285 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { 286 #if 0 // testing hack to force us to flush our cache often 287 static int gCounter; 288 if ((++gCounter % 10) == 0) return false; 289 #endif 290 291 SkASSERT(glyph); 292 SkASSERT(scaler); 293 SkASSERT(fCache.contains(glyph)); 294 SkASSERT(NULL == glyph->fPlot); 295 296 SkAutoRef ar(scaler); 297 298 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); 299 300 size_t size = glyph->fBounds.area() * bytesPerPixel; 301 SkAutoSMalloc<1024> storage(size); 302 if (fUseDistanceField) { 303 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(), 304 glyph->height(), 305 storage.get())) { 306 return false; 307 } 308 } else { 309 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), 310 glyph->height(), 311 glyph->width() * bytesPerPixel, 312 storage.get())) { 313 return false; 314 } 315 } 316 317 GrPlot* plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), 318 glyph->height(), storage.get(), 319 &glyph->fAtlasLocation); 320 321 if (NULL == plot) { 322 return false; 323 } 324 325 glyph->fPlot = plot; 326 return true; 327 } 328