1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/GrTextStrike.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,357 @@ 1.4 +/* 1.5 + * Copyright 2010 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "GrAtlas.h" 1.12 +#include "GrGpu.h" 1.13 +#include "GrRectanizer.h" 1.14 +#include "GrTextStrike.h" 1.15 +#include "GrTextStrike_impl.h" 1.16 +#include "SkString.h" 1.17 + 1.18 +#include "SkDistanceFieldGen.h" 1.19 + 1.20 +/////////////////////////////////////////////////////////////////////////////// 1.21 + 1.22 +#define FONT_CACHE_STATS 0 1.23 +#if FONT_CACHE_STATS 1.24 +static int g_PurgeCount = 0; 1.25 +#endif 1.26 + 1.27 +GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { 1.28 + gpu->ref(); 1.29 + for (int i = 0; i < kAtlasCount; ++i) { 1.30 + fAtlasMgr[i] = NULL; 1.31 + } 1.32 + 1.33 + fHead = fTail = NULL; 1.34 +} 1.35 + 1.36 +GrFontCache::~GrFontCache() { 1.37 + fCache.deleteAll(); 1.38 + for (int i = 0; i < kAtlasCount; ++i) { 1.39 + delete fAtlasMgr[i]; 1.40 + } 1.41 + fGpu->unref(); 1.42 +#if FONT_CACHE_STATS 1.43 + GrPrintf("Num purges: %d\n", g_PurgeCount); 1.44 +#endif 1.45 +} 1.46 + 1.47 +static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) { 1.48 + static const GrPixelConfig sPixelConfigs[] = { 1.49 + kAlpha_8_GrPixelConfig, 1.50 + kRGB_565_GrPixelConfig, 1.51 + kSkia8888_GrPixelConfig, 1.52 + kSkia8888_GrPixelConfig 1.53 + }; 1.54 + SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch); 1.55 + 1.56 + return sPixelConfigs[format]; 1.57 +} 1.58 + 1.59 +static int mask_format_to_atlas_index(GrMaskFormat format) { 1.60 + static const int sAtlasIndices[] = { 1.61 + GrFontCache::kA8_AtlasType, 1.62 + GrFontCache::k565_AtlasType, 1.63 + GrFontCache::k8888_AtlasType, 1.64 + GrFontCache::k8888_AtlasType 1.65 + }; 1.66 + SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch); 1.67 + 1.68 + SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); 1.69 + return sAtlasIndices[format]; 1.70 +} 1.71 + 1.72 +GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, 1.73 + const Key& key) { 1.74 + GrMaskFormat format = scaler->getMaskFormat(); 1.75 + GrPixelConfig config = mask_format_to_pixel_config(format); 1.76 + int atlasIndex = mask_format_to_atlas_index(format); 1.77 + if (NULL == fAtlasMgr[atlasIndex]) { 1.78 + fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config)); 1.79 + } 1.80 + GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, 1.81 + (this, scaler->getKey(), format, fAtlasMgr[atlasIndex])); 1.82 + fCache.insert(key, strike); 1.83 + 1.84 + if (fHead) { 1.85 + fHead->fPrev = strike; 1.86 + } else { 1.87 + SkASSERT(NULL == fTail); 1.88 + fTail = strike; 1.89 + } 1.90 + strike->fPrev = NULL; 1.91 + strike->fNext = fHead; 1.92 + fHead = strike; 1.93 + 1.94 + return strike; 1.95 +} 1.96 + 1.97 +void GrFontCache::freeAll() { 1.98 + fCache.deleteAll(); 1.99 + for (int i = 0; i < kAtlasCount; ++i) { 1.100 + delete fAtlasMgr[i]; 1.101 + fAtlasMgr[i] = NULL; 1.102 + } 1.103 + fHead = NULL; 1.104 + fTail = NULL; 1.105 +} 1.106 + 1.107 +void GrFontCache::purgeStrike(GrTextStrike* strike) { 1.108 + const GrFontCache::Key key(strike->fFontScalerKey); 1.109 + fCache.remove(key, strike); 1.110 + this->detachStrikeFromList(strike); 1.111 + delete strike; 1.112 +} 1.113 + 1.114 +bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { 1.115 + SkASSERT(NULL != preserveStrike); 1.116 + 1.117 + GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr; 1.118 + GrPlot* plot = atlasMgr->getUnusedPlot(); 1.119 + if (NULL == plot) { 1.120 + return false; 1.121 + } 1.122 + plot->resetRects(); 1.123 + 1.124 + GrTextStrike* strike = fHead; 1.125 + GrMaskFormat maskFormat = preserveStrike->fMaskFormat; 1.126 + while (strike) { 1.127 + if (maskFormat != strike->fMaskFormat) { 1.128 + strike = strike->fNext; 1.129 + continue; 1.130 + } 1.131 + 1.132 + GrTextStrike* strikeToPurge = strike; 1.133 + strike = strikeToPurge->fNext; 1.134 + strikeToPurge->removePlot(plot); 1.135 + 1.136 + // clear out any empty strikes (except this one) 1.137 + if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) { 1.138 + this->purgeStrike(strikeToPurge); 1.139 + } 1.140 + } 1.141 + 1.142 +#if FONT_CACHE_STATS 1.143 + ++g_PurgeCount; 1.144 +#endif 1.145 + 1.146 + return true; 1.147 +} 1.148 + 1.149 +#ifdef SK_DEBUG 1.150 +void GrFontCache::validate() const { 1.151 + int count = fCache.count(); 1.152 + if (0 == count) { 1.153 + SkASSERT(!fHead); 1.154 + SkASSERT(!fTail); 1.155 + } else if (1 == count) { 1.156 + SkASSERT(fHead == fTail); 1.157 + } else { 1.158 + SkASSERT(fHead != fTail); 1.159 + } 1.160 + 1.161 + int count2 = 0; 1.162 + const GrTextStrike* strike = fHead; 1.163 + while (strike) { 1.164 + count2 += 1; 1.165 + strike = strike->fNext; 1.166 + } 1.167 + SkASSERT(count == count2); 1.168 + 1.169 + count2 = 0; 1.170 + strike = fTail; 1.171 + while (strike) { 1.172 + count2 += 1; 1.173 + strike = strike->fPrev; 1.174 + } 1.175 + SkASSERT(count == count2); 1.176 +} 1.177 +#endif 1.178 + 1.179 +#ifdef SK_DEVELOPER 1.180 +void GrFontCache::dump() const { 1.181 + static int gDumpCount = 0; 1.182 + for (int i = 0; i < kAtlasCount; ++i) { 1.183 + if (NULL != fAtlasMgr[i]) { 1.184 + GrTexture* texture = fAtlasMgr[i]->getTexture(); 1.185 + if (NULL != texture) { 1.186 + SkString filename; 1.187 + filename.printf("fontcache_%d%d.png", gDumpCount, i); 1.188 + texture->savePixels(filename.c_str()); 1.189 + } 1.190 + } 1.191 + } 1.192 + ++gDumpCount; 1.193 +} 1.194 +#endif 1.195 + 1.196 +/////////////////////////////////////////////////////////////////////////////// 1.197 + 1.198 +#ifdef SK_DEBUG 1.199 + static int gCounter; 1.200 +#endif 1.201 + 1.202 +// this acts as the max magnitude for the distance field, 1.203 +// as well as the pad we need around the glyph 1.204 +#define DISTANCE_FIELD_RANGE 4 1.205 + 1.206 +/* 1.207 + The text strike is specific to a given font/style/matrix setup, which is 1.208 + represented by the GrHostFontScaler object we are given in getGlyph(). 1.209 + 1.210 + We map a 32bit glyphID to a GrGlyph record, which in turn points to a 1.211 + atlas and a position within that texture. 1.212 + */ 1.213 + 1.214 +GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, 1.215 + GrMaskFormat format, 1.216 + GrAtlasMgr* atlasMgr) : fPool(64) { 1.217 + fFontScalerKey = key; 1.218 + fFontScalerKey->ref(); 1.219 + 1.220 + fFontCache = cache; // no need to ref, it won't go away before we do 1.221 + fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do 1.222 + 1.223 + fMaskFormat = format; 1.224 + 1.225 +#ifdef SK_DEBUG 1.226 +// GrPrintf(" GrTextStrike %p %d\n", this, gCounter); 1.227 + gCounter += 1; 1.228 +#endif 1.229 +} 1.230 + 1.231 +// this signature is needed because it's used with 1.232 +// SkTDArray::visitAll() (see destructor) 1.233 +static void free_glyph(GrGlyph*& glyph) { glyph->free(); } 1.234 + 1.235 +GrTextStrike::~GrTextStrike() { 1.236 + fFontScalerKey->unref(); 1.237 + fCache.getArray().visitAll(free_glyph); 1.238 + 1.239 +#ifdef SK_DEBUG 1.240 + gCounter -= 1; 1.241 +// GrPrintf("~GrTextStrike %p %d\n", this, gCounter); 1.242 +#endif 1.243 +} 1.244 + 1.245 +GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, 1.246 + GrFontScaler* scaler) { 1.247 + SkIRect bounds; 1.248 + if (!scaler->getPackedGlyphBounds(packed, &bounds)) { 1.249 + return NULL; 1.250 + } 1.251 + 1.252 + GrGlyph* glyph = fPool.alloc(); 1.253 + // expand bounds to hold full distance field data 1.254 + if (fUseDistanceField) { 1.255 + bounds.fLeft -= DISTANCE_FIELD_RANGE; 1.256 + bounds.fRight += DISTANCE_FIELD_RANGE; 1.257 + bounds.fTop -= DISTANCE_FIELD_RANGE; 1.258 + bounds.fBottom += DISTANCE_FIELD_RANGE; 1.259 + } 1.260 + glyph->init(packed, bounds); 1.261 + fCache.insert(packed, glyph); 1.262 + return glyph; 1.263 +} 1.264 + 1.265 +void GrTextStrike::removePlot(const GrPlot* plot) { 1.266 + SkTDArray<GrGlyph*>& glyphArray = fCache.getArray(); 1.267 + for (int i = 0; i < glyphArray.count(); ++i) { 1.268 + if (plot == glyphArray[i]->fPlot) { 1.269 + glyphArray[i]->fPlot = NULL; 1.270 + } 1.271 + } 1.272 + 1.273 + fAtlasMgr->removePlot(&fAtlas, plot); 1.274 +} 1.275 + 1.276 + 1.277 +bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { 1.278 +#if 0 // testing hack to force us to flush our cache often 1.279 + static int gCounter; 1.280 + if ((++gCounter % 10) == 0) return false; 1.281 +#endif 1.282 + 1.283 + SkASSERT(glyph); 1.284 + SkASSERT(scaler); 1.285 + SkASSERT(fCache.contains(glyph)); 1.286 + SkASSERT(NULL == glyph->fPlot); 1.287 + 1.288 + SkAutoRef ar(scaler); 1.289 + 1.290 + int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); 1.291 + 1.292 + GrPlot* plot; 1.293 + if (fUseDistanceField) { 1.294 + // we've already expanded the glyph dimensions to match the final size 1.295 + // but must shrink back down to get the packed glyph data 1.296 + int dfWidth = glyph->width(); 1.297 + int dfHeight = glyph->height(); 1.298 + int width = dfWidth - 2*DISTANCE_FIELD_RANGE; 1.299 + int height = dfHeight - 2*DISTANCE_FIELD_RANGE; 1.300 + int stride = width*bytesPerPixel; 1.301 + 1.302 + size_t size = width * height * bytesPerPixel; 1.303 + SkAutoSMalloc<1024> storage(size); 1.304 + if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) { 1.305 + return false; 1.306 + } 1.307 + 1.308 + // alloc storage for distance field glyph 1.309 + size_t dfSize = dfWidth * dfHeight * bytesPerPixel; 1.310 + SkAutoSMalloc<1024> dfStorage(dfSize); 1.311 + 1.312 + if (1 == bytesPerPixel) { 1.313 + (void) SkGenerateDistanceFieldFromImage((unsigned char*)dfStorage.get(), 1.314 + (unsigned char*)storage.get(), 1.315 + width, height, DISTANCE_FIELD_RANGE); 1.316 + } else { 1.317 + // TODO: Fix color emoji 1.318 + // for now, copy glyph into distance field storage 1.319 + // this is not correct, but it won't crash 1.320 + sk_bzero(dfStorage.get(), dfSize); 1.321 + unsigned char* ptr = (unsigned char*) storage.get(); 1.322 + unsigned char* dfPtr = (unsigned char*) dfStorage.get(); 1.323 + size_t dfStride = dfWidth*bytesPerPixel; 1.324 + dfPtr += DISTANCE_FIELD_RANGE*dfStride; 1.325 + dfPtr += DISTANCE_FIELD_RANGE*bytesPerPixel; 1.326 + 1.327 + for (int i = 0; i < height; ++i) { 1.328 + memcpy(dfPtr, ptr, stride); 1.329 + 1.330 + dfPtr += dfStride; 1.331 + ptr += stride; 1.332 + } 1.333 + } 1.334 + 1.335 + // copy to atlas 1.336 + plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(), 1.337 + &glyph->fAtlasLocation); 1.338 + 1.339 + } else { 1.340 + size_t size = glyph->fBounds.area() * bytesPerPixel; 1.341 + SkAutoSMalloc<1024> storage(size); 1.342 + if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), 1.343 + glyph->height(), 1.344 + glyph->width() * bytesPerPixel, 1.345 + storage.get())) { 1.346 + return false; 1.347 + } 1.348 + 1.349 + plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), 1.350 + glyph->height(), storage.get(), 1.351 + &glyph->fAtlasLocation); 1.352 + } 1.353 + 1.354 + if (NULL == plot) { 1.355 + return false; 1.356 + } 1.357 + 1.358 + glyph->fPlot = plot; 1.359 + return true; 1.360 +}