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