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