blob: 39c6d5a0fd62d2456329d76f18ca310310929bba [file] [log] [blame]
joshualitt7c3a2f82015-03-31 13:32:05 -07001/*
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
19static 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
bsalomond309e7a2015-04-30 14:18:54 -070030 GrTexture* texture = context->textureProvider()->refScratchTexture(
31 desc, GrTextureProvider::kApprox_ScratchTexMatch, true);
joshualitt7c3a2f82015-03-31 13:32:05 -070032 if (!texture) {
33 return NULL;
34 }
35 return SkNEW_ARGS(GrBatchAtlas, (texture, numPlotsX, numPlotsY));
36}
37
joshualitt62db8ba2015-04-09 08:22:37 -070038bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
39 int index = MaskFormatToAtlasIndex(format);
40 if (!fAtlases[index]) {
joshualitt7c3a2f82015-03-31 13:32:05 -070041 GrPixelConfig config = this->getPixelConfig(format);
joshualitt7c3a2f82015-03-31 13:32:05 -070042 if (kA8_GrMaskFormat == format) {
joshualitt62db8ba2015-04-09 08:22:37 -070043 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);
joshualitt7c3a2f82015-03-31 13:32:05 -070048 } else {
joshualitt62db8ba2015-04-09 08:22:37 -070049 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);
joshualitt7c3a2f82015-03-31 13:32:05 -070054 }
55
joshualitt62db8ba2015-04-09 08:22:37 -070056 // Atlas creation can fail
57 if (fAtlases[index]) {
58 fAtlases[index]->registerEvictionCallback(&GrBatchFontCache::HandleEviction,
59 (void*)this);
60 } else {
61 return false;
joshualitt7c3a2f82015-03-31 13:32:05 -070062 }
63 }
joshualitt62db8ba2015-04-09 08:22:37 -070064 return true;
65}
66
67GrBatchFontCache::GrBatchFontCache(GrContext* context)
68 : fContext(context)
69 , fPreserveStrike(NULL) {
70 for (int i = 0; i < kMaskFormatCount; ++i) {
71 fAtlases[i] = NULL;
72 }
joshualitt7c3a2f82015-03-31 13:32:05 -070073}
74
75GrBatchFontCache::~GrBatchFontCache() {
76 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
77 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070078 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070079 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070080 ++iter;
81 }
82 for (int i = 0; i < kMaskFormatCount; ++i) {
83 SkDELETE(fAtlases[i]);
84 }
85}
86
joshualitt7c3a2f82015-03-31 13:32:05 -070087void GrBatchFontCache::freeAll() {
88 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
89 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070090 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070091 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070092 ++iter;
93 }
94 fCache.rewind();
95 for (int i = 0; i < kMaskFormatCount; ++i) {
96 SkDELETE(fAtlases[i]);
97 fAtlases[i] = NULL;
98 }
99}
100
joshualitt7c3a2f82015-03-31 13:32:05 -0700101GrPixelConfig GrBatchFontCache::getPixelConfig(GrMaskFormat format) const {
102 static const GrPixelConfig kPixelConfigs[] = {
103 kAlpha_8_GrPixelConfig,
104 kRGB_565_GrPixelConfig,
105 kSkia8888_GrPixelConfig
106 };
107 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kPixelConfigs) == kMaskFormatCount, array_size_mismatch);
108
109 return kPixelConfigs[format];
110}
111
112void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
113 GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
114
115 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
116 for (; !iter.done(); ++iter) {
117 GrBatchTextStrike* strike = &*iter;
118 strike->removeID(id);
119
120 // clear out any empty strikes. We will preserve the strike whose call to addToAtlas
121 // triggered the eviction
122 if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
123 fontCache->fCache.remove(*(strike->fFontScalerKey));
joshualittae32c102015-04-21 09:37:57 -0700124 strike->fIsAbandoned = true;
125 strike->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -0700126 }
127 }
128}
129
130void GrBatchFontCache::dump() const {
131 static int gDumpCount = 0;
132 for (int i = 0; i < kMaskFormatCount; ++i) {
133 if (fAtlases[i]) {
134 GrTexture* texture = fAtlases[i]->getTexture();
135 if (texture) {
136 SkString filename;
137#ifdef SK_BUILD_FOR_ANDROID
138 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
139#else
140 filename.printf("fontcache_%d%d.png", gDumpCount, i);
141#endif
142 texture->surfacePriv().savePixels(filename.c_str());
143 }
144 }
145 }
146 ++gDumpCount;
147}
148
149///////////////////////////////////////////////////////////////////////////////
150
151/*
152 The text strike is specific to a given font/style/matrix setup, which is
153 represented by the GrHostFontScaler object we are given in getGlyph().
154
155 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
156 atlas and a position within that texture.
157 */
158
159GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
160 : fFontScalerKey(SkRef(key))
161 , fPool(9/*start allocations at 512 bytes*/)
joshualittae32c102015-04-21 09:37:57 -0700162 , fAtlasedGlyphs(0)
163 , fIsAbandoned(false) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700164
165 fBatchFontCache = cache; // no need to ref, it won't go away before we do
166}
167
168GrBatchTextStrike::~GrBatchTextStrike() {
169 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
170 while (!iter.done()) {
171 (*iter).free();
172 ++iter;
173 }
174}
175
176GrGlyph* GrBatchTextStrike::generateGlyph(GrGlyph::PackedID packed,
177 GrFontScaler* scaler) {
178 SkIRect bounds;
179 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
180 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) {
181 return NULL;
182 }
183 } else {
184 if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
185 return NULL;
186 }
187 }
188 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed);
189
190 GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
191 glyph->init(packed, bounds, format);
192 fCache.add(glyph);
193 return glyph;
194}
195
196void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
197 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
198 while (!iter.done()) {
199 if (id == (*iter).fID) {
200 (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
201 fAtlasedGlyphs--;
202 SkASSERT(fAtlasedGlyphs >= 0);
203 }
204 ++iter;
205 }
206}
207
joshualitt7c3a2f82015-03-31 13:32:05 -0700208bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
209 GrFontScaler* scaler) {
210 SkASSERT(glyph);
211 SkASSERT(scaler);
212 SkASSERT(fCache.find(glyph->fPackedID));
213 SkASSERT(NULL == glyph->fPlot);
214
215 SkAutoUnref ar(SkSafeRef(scaler));
216
217 int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat);
218
219 size_t size = glyph->fBounds.area() * bytesPerPixel;
joshualitt29f86792015-05-29 08:06:48 -0700220 SkAutoSMalloc<1024> storage(size);
joshualitt7c3a2f82015-03-31 13:32:05 -0700221
222 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
223 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(),
224 glyph->height(),
225 storage.get())) {
226 return false;
227 }
228 } else {
229 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
230 glyph->height(),
231 glyph->width() * bytesPerPixel,
232 storage.get())) {
233 return false;
234 }
235 }
236
237 bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, glyph->fMaskFormat,
238 glyph->width(), glyph->height(),
239 storage.get(), &glyph->fAtlasLocation);
240 if (success) {
241 fAtlasedGlyphs++;
242 }
243 return success;
244}