Home | History | Annotate | Download | only in text
      1 /*
      2  * Copyright 2015 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 "GrBatchFontCache.h"
      9 #include "GrContext.h"
     10 #include "GrGpu.h"
     11 #include "GrRectanizer.h"
     12 #include "GrResourceProvider.h"
     13 #include "GrSurfacePriv.h"
     14 #include "SkString.h"
     15 
     16 #include "SkDistanceFieldGen.h"
     17 
     18 ///////////////////////////////////////////////////////////////////////////////
     19 
     20 bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
     21     int index = MaskFormatToAtlasIndex(format);
     22     if (!fAtlases[index]) {
     23         GrPixelConfig config = MaskFormatToPixelConfig(format);
     24         int width = fAtlasConfigs[index].fWidth;
     25         int height = fAtlasConfigs[index].fHeight;
     26         int numPlotsX = fAtlasConfigs[index].numPlotsX();
     27         int numPlotsY = fAtlasConfigs[index].numPlotsY();
     28 
     29         fAtlases[index] =
     30                 fContext->resourceProvider()->createAtlas(config, width, height,
     31                                                           numPlotsX, numPlotsY,
     32                                                           &GrBatchFontCache::HandleEviction,
     33                                                           (void*)this);
     34         if (!fAtlases[index]) {
     35             return false;
     36         }
     37     }
     38     return true;
     39 }
     40 
     41 GrBatchFontCache::GrBatchFontCache(GrContext* context)
     42     : fContext(context)
     43     , fPreserveStrike(nullptr) {
     44     for (int i = 0; i < kMaskFormatCount; ++i) {
     45         fAtlases[i] = nullptr;
     46     }
     47 
     48     // setup default atlas configs
     49     fAtlasConfigs[kA8_GrMaskFormat].fWidth = 2048;
     50     fAtlasConfigs[kA8_GrMaskFormat].fHeight = 2048;
     51     fAtlasConfigs[kA8_GrMaskFormat].fLog2Width = 11;
     52     fAtlasConfigs[kA8_GrMaskFormat].fLog2Height = 11;
     53     fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = 512;
     54     fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = 256;
     55 
     56     fAtlasConfigs[kA565_GrMaskFormat].fWidth = 1024;
     57     fAtlasConfigs[kA565_GrMaskFormat].fHeight = 2048;
     58     fAtlasConfigs[kA565_GrMaskFormat].fLog2Width = 10;
     59     fAtlasConfigs[kA565_GrMaskFormat].fLog2Height = 11;
     60     fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = 256;
     61     fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = 256;
     62 
     63     fAtlasConfigs[kARGB_GrMaskFormat].fWidth = 1024;
     64     fAtlasConfigs[kARGB_GrMaskFormat].fHeight = 2048;
     65     fAtlasConfigs[kARGB_GrMaskFormat].fLog2Width = 10;
     66     fAtlasConfigs[kARGB_GrMaskFormat].fLog2Height = 11;
     67     fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = 256;
     68     fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = 256;
     69 }
     70 
     71 GrBatchFontCache::~GrBatchFontCache() {
     72     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
     73     while (!iter.done()) {
     74         (*iter).fIsAbandoned = true;
     75         (*iter).unref();
     76         ++iter;
     77     }
     78     for (int i = 0; i < kMaskFormatCount; ++i) {
     79         delete fAtlases[i];
     80     }
     81 }
     82 
     83 void GrBatchFontCache::freeAll() {
     84     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
     85     while (!iter.done()) {
     86         (*iter).fIsAbandoned = true;
     87         (*iter).unref();
     88         ++iter;
     89     }
     90     fCache.rewind();
     91     for (int i = 0; i < kMaskFormatCount; ++i) {
     92         delete fAtlases[i];
     93         fAtlases[i] = nullptr;
     94     }
     95 }
     96 
     97 void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
     98     GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
     99 
    100     SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
    101     for (; !iter.done(); ++iter) {
    102         GrBatchTextStrike* strike = &*iter;
    103         strike->removeID(id);
    104 
    105         // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
    106         // triggered the eviction
    107         if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
    108             fontCache->fCache.remove(*(strike->fFontScalerKey));
    109             strike->fIsAbandoned = true;
    110             strike->unref();
    111         }
    112     }
    113 }
    114 
    115 void GrBatchFontCache::dump() const {
    116     static int gDumpCount = 0;
    117     for (int i = 0; i < kMaskFormatCount; ++i) {
    118         if (fAtlases[i]) {
    119             GrTexture* texture = fAtlases[i]->getTexture();
    120             if (texture) {
    121                 SkString filename;
    122 #ifdef SK_BUILD_FOR_ANDROID
    123                 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
    124 #else
    125                 filename.printf("fontcache_%d%d.png", gDumpCount, i);
    126 #endif
    127                 texture->surfacePriv().savePixels(filename.c_str());
    128             }
    129         }
    130     }
    131     ++gDumpCount;
    132 }
    133 
    134 void GrBatchFontCache::setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3]) {
    135     // delete any old atlases, this should be safe to do as long as we are not in the middle of a
    136     // flush
    137     for (int i = 0; i < kMaskFormatCount; i++) {
    138         if (fAtlases[i]) {
    139             delete fAtlases[i];
    140             fAtlases[i] = nullptr;
    141         }
    142     }
    143     memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs));
    144 }
    145 
    146 ///////////////////////////////////////////////////////////////////////////////
    147 
    148 /*
    149     The text strike is specific to a given font/style/matrix setup, which is
    150     represented by the GrHostFontScaler object we are given in getGlyph().
    151 
    152     We map a 32bit glyphID to a GrGlyph record, which in turn points to a
    153     atlas and a position within that texture.
    154  */
    155 
    156 GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
    157     : fFontScalerKey(SkRef(key))
    158     , fPool(9/*start allocations at 512 bytes*/)
    159     , fAtlasedGlyphs(0)
    160     , fIsAbandoned(false) {
    161 
    162     fBatchFontCache = cache;     // no need to ref, it won't go away before we do
    163 }
    164 
    165 GrBatchTextStrike::~GrBatchTextStrike() {
    166     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    167     while (!iter.done()) {
    168         (*iter).free();
    169         ++iter;
    170     }
    171 }
    172 
    173 GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
    174                                           GrFontScaler* scaler) {
    175     SkIRect bounds;
    176     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
    177         if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
    178             return nullptr;
    179         }
    180     } else {
    181         if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
    182             return nullptr;
    183         }
    184     }
    185     GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);
    186 
    187     GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph));
    188     glyph->init(packed, bounds, format);
    189     fCache.add(glyph);
    190     return glyph;
    191 }
    192 
    193 void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
    194     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
    195     while (!iter.done()) {
    196         if (id == (*iter).fID) {
    197             (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
    198             fAtlasedGlyphs--;
    199             SkASSERT(fAtlasedGlyphs >= 0);
    200         }
    201         ++iter;
    202     }
    203 }
    204 
    205 bool GrBatchTextStrike::addGlyphToAtlas(GrDrawBatch::Target* target,
    206                                         GrGlyph* glyph,
    207                                         GrFontScaler* scaler,
    208                                         GrMaskFormat expectedMaskFormat) {
    209     SkASSERT(glyph);
    210     SkASSERT(scaler);
    211     SkASSERT(fCache.find(glyph->fPackedID));
    212 
    213     SkAutoUnref ar(SkSafeRef(scaler));
    214 
    215     int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
    216 
    217     size_t size = glyph->fBounds.area() * bytesPerPixel;
    218     SkAutoSMalloc<1024> storage(size);
    219 
    220     const SkGlyph& skGlyph = scaler->grToSkGlyph(glyph->fPackedID);
    221     if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
    222         if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
    223                                            storage.get())) {
    224             return false;
    225         }
    226     } else {
    227         if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
    228                                          glyph->width() * bytesPerPixel, expectedMaskFormat,
    229                                          storage.get())) {
    230             return false;
    231         }
    232     }
    233 
    234     bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, target, expectedMaskFormat,
    235                                                glyph->width(), glyph->height(),
    236                                                storage.get(), &glyph->fAtlasLocation);
    237     if (success) {
    238         SkASSERT(GrBatchAtlas::kInvalidAtlasID != glyph->fID);
    239         fAtlasedGlyphs++;
    240     }
    241     return success;
    242 }
    243