blob: b85c5bd59569bf91cacb673caa768a413fb9c02f [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"
kkinnunencabe20c2015-06-01 01:37:26 -07009#include "GrContext.h"
joshualitt7c3a2f82015-03-31 13:32:05 -070010#include "GrFontAtlasSizes.h"
11#include "GrGpu.h"
12#include "GrRectanizer.h"
bsalomoneae62002015-07-31 13:59:30 -070013#include "GrResourceProvider.h"
joshualitt7c3a2f82015-03-31 13:32:05 -070014#include "GrSurfacePriv.h"
15#include "SkString.h"
16
17#include "SkDistanceFieldGen.h"
18
19///////////////////////////////////////////////////////////////////////////////
20
21static GrBatchAtlas* make_atlas(GrContext* context, GrPixelConfig config,
22 int textureWidth, int textureHeight,
23 int numPlotsX, int numPlotsY) {
24 GrSurfaceDesc desc;
25 desc.fFlags = kNone_GrSurfaceFlags;
26 desc.fWidth = textureWidth;
27 desc.fHeight = textureHeight;
28 desc.fConfig = config;
29
30 // We don't want to flush the context so we claim we're in the middle of flushing so as to
31 // guarantee we do not recieve a texture with pending IO
bsalomoneae62002015-07-31 13:59:30 -070032 // TODO: Determine how to avoid having to do this. (http://skbug.com/4156)
33 static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag;
34 GrTexture* texture = context->resourceProvider()->createApproxTexture(desc, kFlags);
joshualitt7c3a2f82015-03-31 13:32:05 -070035 if (!texture) {
36 return NULL;
37 }
38 return SkNEW_ARGS(GrBatchAtlas, (texture, numPlotsX, numPlotsY));
39}
40
joshualitt62db8ba2015-04-09 08:22:37 -070041bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
42 int index = MaskFormatToAtlasIndex(format);
43 if (!fAtlases[index]) {
bsalomon265697d2015-07-22 10:17:26 -070044 GrPixelConfig config = MaskFormatToPixelConfig(format);
joshualitt7c3a2f82015-03-31 13:32:05 -070045 if (kA8_GrMaskFormat == format) {
joshualitt62db8ba2015-04-09 08:22:37 -070046 fAtlases[index] = make_atlas(fContext, config,
47 GR_FONT_ATLAS_A8_TEXTURE_WIDTH,
48 GR_FONT_ATLAS_TEXTURE_HEIGHT,
49 GR_FONT_ATLAS_A8_NUM_PLOTS_X,
50 GR_FONT_ATLAS_NUM_PLOTS_Y);
joshualitt7c3a2f82015-03-31 13:32:05 -070051 } else {
joshualitt62db8ba2015-04-09 08:22:37 -070052 fAtlases[index] = make_atlas(fContext, config,
53 GR_FONT_ATLAS_TEXTURE_WIDTH,
54 GR_FONT_ATLAS_TEXTURE_HEIGHT,
55 GR_FONT_ATLAS_NUM_PLOTS_X,
56 GR_FONT_ATLAS_NUM_PLOTS_Y);
joshualitt7c3a2f82015-03-31 13:32:05 -070057 }
58
joshualitt62db8ba2015-04-09 08:22:37 -070059 // Atlas creation can fail
60 if (fAtlases[index]) {
61 fAtlases[index]->registerEvictionCallback(&GrBatchFontCache::HandleEviction,
62 (void*)this);
63 } else {
64 return false;
joshualitt7c3a2f82015-03-31 13:32:05 -070065 }
66 }
joshualitt62db8ba2015-04-09 08:22:37 -070067 return true;
68}
69
70GrBatchFontCache::GrBatchFontCache(GrContext* context)
71 : fContext(context)
72 , fPreserveStrike(NULL) {
73 for (int i = 0; i < kMaskFormatCount; ++i) {
74 fAtlases[i] = NULL;
75 }
joshualitt7c3a2f82015-03-31 13:32:05 -070076}
77
78GrBatchFontCache::~GrBatchFontCache() {
79 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
80 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070081 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070082 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070083 ++iter;
84 }
85 for (int i = 0; i < kMaskFormatCount; ++i) {
86 SkDELETE(fAtlases[i]);
87 }
88}
89
joshualitt7c3a2f82015-03-31 13:32:05 -070090void GrBatchFontCache::freeAll() {
91 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
92 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070093 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070094 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070095 ++iter;
96 }
97 fCache.rewind();
98 for (int i = 0; i < kMaskFormatCount; ++i) {
99 SkDELETE(fAtlases[i]);
100 fAtlases[i] = NULL;
101 }
102}
103
joshualitt7c3a2f82015-03-31 13:32:05 -0700104void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
105 GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
106
107 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
108 for (; !iter.done(); ++iter) {
109 GrBatchTextStrike* strike = &*iter;
110 strike->removeID(id);
111
112 // clear out any empty strikes. We will preserve the strike whose call to addToAtlas
113 // triggered the eviction
114 if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
115 fontCache->fCache.remove(*(strike->fFontScalerKey));
joshualittae32c102015-04-21 09:37:57 -0700116 strike->fIsAbandoned = true;
117 strike->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -0700118 }
119 }
120}
121
122void GrBatchFontCache::dump() const {
123 static int gDumpCount = 0;
124 for (int i = 0; i < kMaskFormatCount; ++i) {
125 if (fAtlases[i]) {
126 GrTexture* texture = fAtlases[i]->getTexture();
127 if (texture) {
128 SkString filename;
129#ifdef SK_BUILD_FOR_ANDROID
130 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
131#else
132 filename.printf("fontcache_%d%d.png", gDumpCount, i);
133#endif
134 texture->surfacePriv().savePixels(filename.c_str());
135 }
136 }
137 }
138 ++gDumpCount;
139}
140
141///////////////////////////////////////////////////////////////////////////////
142
143/*
144 The text strike is specific to a given font/style/matrix setup, which is
145 represented by the GrHostFontScaler object we are given in getGlyph().
146
147 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
148 atlas and a position within that texture.
149 */
150
151GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
152 : fFontScalerKey(SkRef(key))
153 , fPool(9/*start allocations at 512 bytes*/)
joshualittae32c102015-04-21 09:37:57 -0700154 , fAtlasedGlyphs(0)
155 , fIsAbandoned(false) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700156
157 fBatchFontCache = cache; // no need to ref, it won't go away before we do
158}
159
160GrBatchTextStrike::~GrBatchTextStrike() {
161 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
162 while (!iter.done()) {
163 (*iter).free();
164 ++iter;
165 }
166}
167
joshualitt6c2c2b02015-07-24 10:37:00 -0700168GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
joshualitt7c3a2f82015-03-31 13:32:05 -0700169 GrFontScaler* scaler) {
170 SkIRect bounds;
171 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
joshualitt6c2c2b02015-07-24 10:37:00 -0700172 if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700173 return NULL;
174 }
175 } else {
joshualitt6c2c2b02015-07-24 10:37:00 -0700176 if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700177 return NULL;
178 }
179 }
joshualitt6c2c2b02015-07-24 10:37:00 -0700180 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);
181
joshualitt7c3a2f82015-03-31 13:32:05 -0700182 GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
183 glyph->init(packed, bounds, format);
184 fCache.add(glyph);
185 return glyph;
186}
187
188void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
189 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
190 while (!iter.done()) {
191 if (id == (*iter).fID) {
192 (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
193 fAtlasedGlyphs--;
194 SkASSERT(fAtlasedGlyphs >= 0);
195 }
196 ++iter;
197 }
198}
199
joshualitt7c3a2f82015-03-31 13:32:05 -0700200bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
joshualitt4f19ca32015-07-30 07:59:20 -0700201 GrFontScaler* scaler, const SkGlyph& skGlyph,
202 GrMaskFormat expectedMaskFormat) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700203 SkASSERT(glyph);
204 SkASSERT(scaler);
205 SkASSERT(fCache.find(glyph->fPackedID));
joshualitt7c3a2f82015-03-31 13:32:05 -0700206
207 SkAutoUnref ar(SkSafeRef(scaler));
208
joshualitt4f19ca32015-07-30 07:59:20 -0700209 int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
joshualitt7c3a2f82015-03-31 13:32:05 -0700210
211 size_t size = glyph->fBounds.area() * bytesPerPixel;
joshualitt29f86792015-05-29 08:06:48 -0700212 SkAutoSMalloc<1024> storage(size);
joshualitt7c3a2f82015-03-31 13:32:05 -0700213
214 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
joshualitt6c2c2b02015-07-24 10:37:00 -0700215 if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
joshualitt7c3a2f82015-03-31 13:32:05 -0700216 storage.get())) {
217 return false;
218 }
219 } else {
joshualitt6c2c2b02015-07-24 10:37:00 -0700220 if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
joshualitt4f19ca32015-07-30 07:59:20 -0700221 glyph->width() * bytesPerPixel, expectedMaskFormat,
222 storage.get())) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700223 return false;
224 }
225 }
226
joshualitt4f19ca32015-07-30 07:59:20 -0700227 bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, expectedMaskFormat,
joshualitt7c3a2f82015-03-31 13:32:05 -0700228 glyph->width(), glyph->height(),
229 storage.get(), &glyph->fAtlasLocation);
230 if (success) {
joshualitt65e96b42015-07-31 11:45:22 -0700231 SkASSERT(GrBatchAtlas::kInvalidAtlasID != glyph->fID);
joshualitt7c3a2f82015-03-31 13:32:05 -0700232 fAtlasedGlyphs++;
233 }
234 return success;
235}