Home | History | Annotate | Download | only in gpu
      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 "GrTextStrike.h"
     11 #include "GrTextStrike_impl.h"
     12 #include "SkString.h"
     13 
     14 #include "SkDistanceFieldGen.h"
     15 
     16 ///////////////////////////////////////////////////////////////////////////////
     17 
     18 #define GR_ATLAS_TEXTURE_WIDTH 1024
     19 #define GR_ATLAS_TEXTURE_HEIGHT 2048
     20 
     21 #define GR_PLOT_WIDTH  256
     22 #define GR_PLOT_HEIGHT 256
     23 
     24 #define GR_NUM_PLOTS_X   (GR_ATLAS_TEXTURE_WIDTH / GR_PLOT_WIDTH)
     25 #define GR_NUM_PLOTS_Y   (GR_ATLAS_TEXTURE_HEIGHT / GR_PLOT_HEIGHT)
     26 
     27 #define FONT_CACHE_STATS 0
     28 #if FONT_CACHE_STATS
     29 static int g_PurgeCount = 0;
     30 #endif
     31 
     32 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
     33     gpu->ref();
     34     for (int i = 0; i < kAtlasCount; ++i) {
     35         fAtlases[i] = NULL;
     36     }
     37 
     38     fHead = fTail = NULL;
     39 }
     40 
     41 GrFontCache::~GrFontCache() {
     42     SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
     43     while (!iter.done()) {
     44         SkDELETE(&(*iter));
     45         ++iter;
     46     }
     47     for (int i = 0; i < kAtlasCount; ++i) {
     48         delete fAtlases[i];
     49     }
     50     fGpu->unref();
     51 #if FONT_CACHE_STATS
     52       GrPrintf("Num purges: %d\n", g_PurgeCount);
     53 #endif
     54 }
     55 
     56 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) {
     57     static const GrPixelConfig sPixelConfigs[] = {
     58         kAlpha_8_GrPixelConfig,
     59         kRGB_565_GrPixelConfig,
     60         kSkia8888_GrPixelConfig,
     61         kSkia8888_GrPixelConfig
     62     };
     63     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_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         GrFontCache::k8888_AtlasType
     74     };
     75     SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch);
     76 
     77     SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount);
     78     return sAtlasIndices[format];
     79 }
     80 
     81 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) {
     82     GrMaskFormat format = scaler->getMaskFormat();
     83     GrPixelConfig config = mask_format_to_pixel_config(format);
     84     int atlasIndex = mask_format_to_atlas_index(format);
     85     if (NULL == fAtlases[atlasIndex]) {
     86         SkISize textureSize = SkISize::Make(GR_ATLAS_TEXTURE_WIDTH,
     87                                             GR_ATLAS_TEXTURE_HEIGHT);
     88         fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrTextureFlags,
     89                                                     textureSize,
     90                                                     GR_NUM_PLOTS_X,
     91                                                     GR_NUM_PLOTS_Y,
     92                                                     true));
     93     }
     94     GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
     95                                       (this, scaler->getKey(), format, fAtlases[atlasIndex]));
     96     fCache.add(strike);
     97 
     98     if (fHead) {
     99         fHead->fPrev = strike;
    100     } else {
    101         SkASSERT(NULL == fTail);
    102         fTail = strike;
    103     }
    104     strike->fPrev = NULL;
    105     strike->fNext = fHead;
    106     fHead = strike;
    107 
    108     return strike;
    109 }
    110 
    111 void GrFontCache::freeAll() {
    112     SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache);
    113     while (!iter.done()) {
    114         SkDELETE(&(*iter));
    115         ++iter;
    116     }
    117     fCache.rewind();
    118     for (int i = 0; i < kAtlasCount; ++i) {
    119         delete fAtlases[i];
    120         fAtlases[i] = NULL;
    121     }
    122     fHead = NULL;
    123     fTail = NULL;
    124 }
    125 
    126 void GrFontCache::purgeStrike(GrTextStrike* strike) {
    127     fCache.remove(*(strike->fFontScalerKey));
    128     this->detachStrikeFromList(strike);
    129     delete strike;
    130 }
    131 
    132 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) {
    133     SkASSERT(preserveStrike);
    134 
    135     GrAtlas* atlas = preserveStrike->fAtlas;
    136     GrPlot* plot = atlas->getUnusedPlot();
    137     if (NULL == plot) {
    138         return false;
    139     }
    140     plot->resetRects();
    141 
    142     GrTextStrike* strike = fHead;
    143     GrMaskFormat maskFormat = preserveStrike->fMaskFormat;
    144     while (strike) {
    145         if (maskFormat != strike->fMaskFormat) {
    146             strike = strike->fNext;
    147             continue;
    148         }
    149 
    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->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                            GrMaskFormat format,
    232                            GrAtlas* atlas) : fPool(64) {
    233     fFontScalerKey = key;
    234     fFontScalerKey->ref();
    235 
    236     fFontCache = cache;     // no need to ref, it won't go away before we do
    237     fAtlas = atlas;         // no need to ref, it won't go away before we do
    238 
    239     fMaskFormat = format;
    240 
    241 #ifdef SK_DEBUG
    242 //    GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
    243     gCounter += 1;
    244 #endif
    245 }
    246 
    247 GrTextStrike::~GrTextStrike() {
    248     fFontScalerKey->unref();
    249     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    250     while (!iter.done()) {
    251         (*iter).free();
    252         ++iter;
    253     }
    254 
    255 #ifdef SK_DEBUG
    256     gCounter -= 1;
    257 //    GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
    258 #endif
    259 }
    260 
    261 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
    262                                      GrFontScaler* scaler) {
    263     SkIRect bounds;
    264     if (fUseDistanceField) {
    265         if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
    266             return NULL;
    267         }
    268     } else {
    269         if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
    270             return NULL;
    271         }
    272     }
    273 
    274     GrGlyph* glyph = fPool.alloc();
    275     glyph->init(packed, bounds);
    276     fCache.add(glyph);
    277     return glyph;
    278 }
    279 
    280 void GrTextStrike::removePlot(const GrPlot* plot) {
    281     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    282     while (!iter.done()) {
    283         if (plot == (*iter).fPlot) {
    284             (*iter).fPlot = NULL;
    285         }
    286         ++iter;
    287     }
    288 
    289     GrAtlas::RemovePlot(&fPlotUsage, plot);
    290 }
    291 
    292 bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) {
    293     int width = glyph->fBounds.width();
    294     int height = glyph->fBounds.height();
    295     int pad = fUseDistanceField ? 2 * SK_DistanceFieldPad : 0;
    296     if (width + pad > GR_PLOT_WIDTH) {
    297         return true;
    298     }
    299     if (height + pad > GR_PLOT_HEIGHT) {
    300         return true;
    301     }
    302 
    303     return false;
    304 }
    305 
    306 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
    307 #if 0   // testing hack to force us to flush our cache often
    308     static int gCounter;
    309     if ((++gCounter % 10) == 0) return false;
    310 #endif
    311 
    312     SkASSERT(glyph);
    313     SkASSERT(scaler);
    314     SkASSERT(fCache.find(glyph->fPackedID));
    315     SkASSERT(NULL == glyph->fPlot);
    316 
    317     SkAutoUnref ar(SkSafeRef(scaler));
    318 
    319     int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
    320 
    321     size_t size = glyph->fBounds.area() * bytesPerPixel;
    322     GrAutoMalloc<1024> storage(size);
    323 
    324     if (fUseDistanceField) {
    325         if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
    326                                            glyph->height(),
    327                                            storage.get())) {
    328             return false;
    329         }
    330     } else {
    331         if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
    332                                          glyph->height(),
    333                                          glyph->width() * bytesPerPixel,
    334                                          storage.get())) {
    335             return false;
    336         }
    337     }
    338 
    339     GrPlot* plot  = fAtlas->addToAtlas(&fPlotUsage, glyph->width(),
    340                                        glyph->height(), storage.get(),
    341                                        &glyph->fAtlasLocation);
    342 
    343     if (NULL == plot) {
    344         return false;
    345     }
    346 
    347     glyph->fPlot = plot;
    348     return true;
    349 }
    350