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 "GrAtlas.h" 9 #include "GrGpu.h" 10 #include "GrRectanizer.h" 11 #include "GrTextStrike.h" 12 #include "GrTextStrike_impl.h" 13 14 SK_DEFINE_INST_COUNT(GrFontScaler) 15 SK_DEFINE_INST_COUNT(GrKey) 16 17 /////////////////////////////////////////////////////////////////////////////// 18 19 #define FONT_CACHE_STATS 0 20 #if FONT_CACHE_STATS 21 static int g_PurgeCount = 0; 22 #endif 23 24 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { 25 gpu->ref(); 26 fAtlasMgr = NULL; 27 28 fHead = fTail = NULL; 29 } 30 31 GrFontCache::~GrFontCache() { 32 fCache.deleteAll(); 33 delete fAtlasMgr; 34 fGpu->unref(); 35 #if FONT_CACHE_STATS 36 GrPrintf("Num purges: %d\n", g_PurgeCount); 37 #endif 38 } 39 40 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, 41 const Key& key) { 42 if (NULL == fAtlasMgr) { 43 fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu)); 44 } 45 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, 46 (this, scaler->getKey(), 47 scaler->getMaskFormat(), fAtlasMgr)); 48 fCache.insert(key, strike); 49 50 if (fHead) { 51 fHead->fPrev = strike; 52 } else { 53 GrAssert(NULL == fTail); 54 fTail = strike; 55 } 56 strike->fPrev = NULL; 57 strike->fNext = fHead; 58 fHead = strike; 59 60 return strike; 61 } 62 63 void GrFontCache::freeAll() { 64 fCache.deleteAll(); 65 delete fAtlasMgr; 66 fAtlasMgr = NULL; 67 fHead = NULL; 68 fTail = NULL; 69 } 70 71 void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) { 72 GrTextStrike* strike = fTail; 73 bool purge = true; 74 while (strike) { 75 if (strike == preserveStrike) { 76 strike = strike->fPrev; 77 continue; 78 } 79 GrTextStrike* strikeToPurge = strike; 80 strike = strikeToPurge->fPrev; 81 if (purge) { 82 // keep purging if we won't free up any atlases with this strike. 83 purge = (NULL == strikeToPurge->fAtlas); 84 int index = fCache.slowFindIndex(strikeToPurge); 85 GrAssert(index >= 0); 86 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); 87 this->detachStrikeFromList(strikeToPurge); 88 delete strikeToPurge; 89 } else { 90 // for the remaining strikes, we just mark them unused 91 GrAtlas::MarkAllUnused(strikeToPurge->fAtlas); 92 } 93 } 94 #if FONT_CACHE_STATS 95 ++g_PurgeCount; 96 #endif 97 } 98 99 void GrFontCache::freeAtlasExceptFor(GrTextStrike* preserveStrike) { 100 GrTextStrike* strike = fTail; 101 while (strike) { 102 if (strike == preserveStrike) { 103 strike = strike->fPrev; 104 continue; 105 } 106 GrTextStrike* strikeToPurge = strike; 107 strike = strikeToPurge->fPrev; 108 if (strikeToPurge->removeUnusedAtlases()) { 109 if (NULL == strikeToPurge->fAtlas) { 110 int index = fCache.slowFindIndex(strikeToPurge); 111 GrAssert(index >= 0); 112 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); 113 this->detachStrikeFromList(strikeToPurge); 114 delete strikeToPurge; 115 } 116 break; 117 } 118 } 119 } 120 121 #if GR_DEBUG 122 void GrFontCache::validate() const { 123 int count = fCache.count(); 124 if (0 == count) { 125 GrAssert(!fHead); 126 GrAssert(!fTail); 127 } else if (1 == count) { 128 GrAssert(fHead == fTail); 129 } else { 130 GrAssert(fHead != fTail); 131 } 132 133 int count2 = 0; 134 const GrTextStrike* strike = fHead; 135 while (strike) { 136 count2 += 1; 137 strike = strike->fNext; 138 } 139 GrAssert(count == count2); 140 141 count2 = 0; 142 strike = fTail; 143 while (strike) { 144 count2 += 1; 145 strike = strike->fPrev; 146 } 147 GrAssert(count == count2); 148 } 149 #endif 150 151 /////////////////////////////////////////////////////////////////////////////// 152 153 #if GR_DEBUG 154 static int gCounter; 155 #endif 156 157 /* 158 The text strike is specific to a given font/style/matrix setup, which is 159 represented by the GrHostFontScaler object we are given in getGlyph(). 160 161 We map a 32bit glyphID to a GrGlyph record, which in turn points to a 162 atlas and a position within that texture. 163 */ 164 165 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, 166 GrMaskFormat format, 167 GrAtlasMgr* atlasMgr) : fPool(64) { 168 fFontScalerKey = key; 169 fFontScalerKey->ref(); 170 171 fFontCache = cache; // no need to ref, it won't go away before we do 172 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do 173 fAtlas = NULL; 174 175 fMaskFormat = format; 176 177 #if GR_DEBUG 178 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); 179 gCounter += 1; 180 #endif 181 } 182 183 // these signatures are needed because they're used with 184 // SkTDArray::visitAll() (see destructor & removeUnusedAtlases()) 185 static void free_glyph(GrGlyph*& glyph) { glyph->free(); } 186 187 static void invalidate_glyph(GrGlyph*& glyph) { 188 if (glyph->fAtlas && !glyph->fAtlas->used()) { 189 glyph->fAtlas = NULL; 190 } 191 } 192 193 GrTextStrike::~GrTextStrike() { 194 GrAtlas::FreeLList(fAtlas); 195 fFontScalerKey->unref(); 196 fCache.getArray().visitAll(free_glyph); 197 198 #if GR_DEBUG 199 gCounter -= 1; 200 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter); 201 #endif 202 } 203 204 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, 205 GrFontScaler* scaler) { 206 SkIRect bounds; 207 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { 208 return NULL; 209 } 210 211 GrGlyph* glyph = fPool.alloc(); 212 glyph->init(packed, bounds); 213 fCache.insert(packed, glyph); 214 return glyph; 215 } 216 217 bool GrTextStrike::removeUnusedAtlases() { 218 fCache.getArray().visitAll(invalidate_glyph); 219 return GrAtlas::RemoveUnusedAtlases(fAtlasMgr, &fAtlas); 220 221 return false; 222 } 223 224 bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { 225 #if 0 // testing hack to force us to flush our cache often 226 static int gCounter; 227 if ((++gCounter % 10) == 0) return false; 228 #endif 229 230 GrAssert(glyph); 231 GrAssert(scaler); 232 GrAssert(fCache.contains(glyph)); 233 if (glyph->fAtlas) { 234 glyph->fAtlas->setUsed(true); 235 return true; 236 } 237 238 GrAutoRef ar(scaler); 239 240 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); 241 size_t size = glyph->fBounds.area() * bytesPerPixel; 242 SkAutoSMalloc<1024> storage(size); 243 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), 244 glyph->height(), 245 glyph->width() * bytesPerPixel, 246 storage.get())) { 247 return false; 248 } 249 250 GrAtlas* atlas = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), 251 glyph->height(), storage.get(), 252 fMaskFormat, 253 &glyph->fAtlasLocation); 254 if (NULL == atlas) { 255 return false; 256 } 257 258 glyph->fAtlas = atlas; 259 atlas->setUsed(true); 260 return true; 261 } 262