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