• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
initAtlas(GrMaskFormat format)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 
GrBatchFontCache(GrContext * context)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 
~GrBatchFontCache()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 
freeAll()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 
HandleEviction(GrBatchAtlas::AtlasID id,void * ptr)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 
dump() const115 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 
setAtlasSizes_ForTesting(const GrBatchAtlasConfig configs[3])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 
GrBatchTextStrike(GrBatchFontCache * cache,const GrFontDescKey * key)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 
~GrBatchTextStrike()165 GrBatchTextStrike::~GrBatchTextStrike() {
166     SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
167     while (!iter.done()) {
168         (*iter).free();
169         ++iter;
170     }
171 }
172 
generateGlyph(const SkGlyph & skGlyph,GrGlyph::PackedID packed,GrFontScaler * scaler)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 
removeID(GrBatchAtlas::AtlasID id)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 
addGlyphToAtlas(GrDrawBatch::Target * target,GrGlyph * glyph,GrFontScaler * scaler,GrMaskFormat expectedMaskFormat)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