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