1 2 /* 3 * Copyright 2010 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 11 #include "GrAtlas.h" 12 #include "GrGpu.h" 13 #include "GrRectanizer.h" 14 #include "GrTextStrike.h" 15 #include "GrTextStrike_impl.h" 16 #include "GrRect.h" 17 18 SK_DEFINE_INST_COUNT(GrFontScaler) 19 SK_DEFINE_INST_COUNT(GrKey) 20 21 /////////////////////////////////////////////////////////////////////////////// 22 23 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { 24 gpu->ref(); 25 fAtlasMgr = NULL; 26 27 fHead = fTail = NULL; 28 } 29 30 GrFontCache::~GrFontCache() { 31 fCache.deleteAll(); 32 delete fAtlasMgr; 33 fGpu->unref(); 34 } 35 36 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, 37 const Key& key) { 38 if (NULL == fAtlasMgr) { 39 fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu)); 40 } 41 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, 42 (this, scaler->getKey(), 43 scaler->getMaskFormat(), fAtlasMgr)); 44 fCache.insert(key, strike); 45 46 if (fHead) { 47 fHead->fPrev = strike; 48 } else { 49 GrAssert(NULL == fTail); 50 fTail = strike; 51 } 52 strike->fPrev = NULL; 53 strike->fNext = fHead; 54 fHead = strike; 55 56 return strike; 57 } 58 59 void GrFontCache::freeAll() { 60 fCache.deleteAll(); 61 delete fAtlasMgr; 62 fAtlasMgr = NULL; 63 fHead = NULL; 64 fTail = NULL; 65 } 66 67 void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) { 68 GrTextStrike* strike = fTail; 69 while (strike) { 70 if (strike == preserveStrike) { 71 strike = strike->fPrev; 72 continue; 73 } 74 GrTextStrike* strikeToPurge = strike; 75 // keep going if we won't free up any atlases with this strike. 76 strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL; 77 int index = fCache.slowFindIndex(strikeToPurge); 78 GrAssert(index >= 0); 79 fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash()); 80 this->detachStrikeFromList(strikeToPurge); 81 delete strikeToPurge; 82 } 83 } 84 85 #if GR_DEBUG 86 void GrFontCache::validate() const { 87 int count = fCache.count(); 88 if (0 == count) { 89 GrAssert(!fHead); 90 GrAssert(!fTail); 91 } else if (1 == count) { 92 GrAssert(fHead == fTail); 93 } else { 94 GrAssert(fHead != fTail); 95 } 96 97 int count2 = 0; 98 const GrTextStrike* strike = fHead; 99 while (strike) { 100 count2 += 1; 101 strike = strike->fNext; 102 } 103 GrAssert(count == count2); 104 105 count2 = 0; 106 strike = fTail; 107 while (strike) { 108 count2 += 1; 109 strike = strike->fPrev; 110 } 111 GrAssert(count == count2); 112 } 113 #endif 114 115 /////////////////////////////////////////////////////////////////////////////// 116 117 #if GR_DEBUG 118 static int gCounter; 119 #endif 120 121 /* 122 The text strike is specific to a given font/style/matrix setup, which is 123 represented by the GrHostFontScaler object we are given in getGlyph(). 124 125 We map a 32bit glyphID to a GrGlyph record, which in turn points to a 126 atlas and a position within that texture. 127 */ 128 129 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, 130 GrMaskFormat format, 131 GrAtlasMgr* atlasMgr) : fPool(64) { 132 fFontScalerKey = key; 133 fFontScalerKey->ref(); 134 135 fFontCache = cache; // no need to ref, it won't go away before we do 136 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do 137 fAtlas = NULL; 138 139 fMaskFormat = format; 140 141 #if GR_DEBUG 142 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); 143 gCounter += 1; 144 #endif 145 } 146 147 static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); } 148 149 GrTextStrike::~GrTextStrike() { 150 GrAtlas::FreeLList(fAtlas); 151 fFontScalerKey->unref(); 152 fCache.getArray().visitAll(FreeGlyph); 153 154 #if GR_DEBUG 155 gCounter -= 1; 156 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter); 157 #endif 158 } 159 160 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, 161 GrFontScaler* scaler) { 162 GrIRect bounds; 163 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { 164 return NULL; 165 } 166 167 GrGlyph* glyph = fPool.alloc(); 168 glyph->init(packed, bounds); 169 fCache.insert(packed, glyph); 170 return glyph; 171 } 172 173 bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) { 174 #if 0 // testing hack to force us to flush our cache often 175 static int gCounter; 176 if ((++gCounter % 10) == 0) return false; 177 #endif 178 179 GrAssert(glyph); 180 GrAssert(scaler); 181 GrAssert(fCache.contains(glyph)); 182 if (glyph->fAtlas) { 183 return true; 184 } 185 186 GrAutoRef ar(scaler); 187 188 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); 189 size_t size = glyph->fBounds.area() * bytesPerPixel; 190 SkAutoSMalloc<1024> storage(size); 191 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), 192 glyph->height(), 193 glyph->width() * bytesPerPixel, 194 storage.get())) { 195 return false; 196 } 197 198 GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(), 199 glyph->height(), storage.get(), 200 fMaskFormat, 201 &glyph->fAtlasLocation); 202 if (NULL == atlas) { 203 return false; 204 } 205 206 // update fAtlas as well, since they may be chained in a linklist 207 glyph->fAtlas = fAtlas = atlas; 208 return true; 209 } 210