blob: dfab80af2663a4d26fd871f795b09be58a531f9d [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
joshualitt62db8ba2015-04-09 08:22:37 -070021bool GrBatchFontCache::initAtlas(GrMaskFormat format) {
22 int index = MaskFormatToAtlasIndex(format);
23 if (!fAtlases[index]) {
bsalomon265697d2015-07-22 10:17:26 -070024 GrPixelConfig config = MaskFormatToPixelConfig(format);
joshualittb356cbc2015-08-05 06:36:39 -070025 int width = GR_FONT_ATLAS_TEXTURE_WIDTH;
26 int height = GR_FONT_ATLAS_TEXTURE_HEIGHT;
27 int numPlotsX = GR_FONT_ATLAS_NUM_PLOTS_X;
28 int numPlotsY = GR_FONT_ATLAS_NUM_PLOTS_Y;
joshualitt7c3a2f82015-03-31 13:32:05 -070029
joshualittb356cbc2015-08-05 06:36:39 -070030 if (kA8_GrMaskFormat == format) {
31 width = GR_FONT_ATLAS_A8_TEXTURE_WIDTH;
32 numPlotsX = GR_FONT_ATLAS_A8_NUM_PLOTS_X;
33 }
34 fAtlases[index] =
35 fContext->resourceProvider()->createAtlas(config, width, height,
36 numPlotsX, numPlotsY,
37 &GrBatchFontCache::HandleEviction,
38 (void*)this);
39 if (!fAtlases[index]) {
joshualitt62db8ba2015-04-09 08:22:37 -070040 return false;
joshualitt7c3a2f82015-03-31 13:32:05 -070041 }
42 }
joshualitt62db8ba2015-04-09 08:22:37 -070043 return true;
44}
45
46GrBatchFontCache::GrBatchFontCache(GrContext* context)
47 : fContext(context)
48 , fPreserveStrike(NULL) {
49 for (int i = 0; i < kMaskFormatCount; ++i) {
50 fAtlases[i] = NULL;
51 }
joshualitt7c3a2f82015-03-31 13:32:05 -070052}
53
54GrBatchFontCache::~GrBatchFontCache() {
55 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
56 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070057 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070058 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070059 ++iter;
60 }
61 for (int i = 0; i < kMaskFormatCount; ++i) {
62 SkDELETE(fAtlases[i]);
63 }
64}
65
joshualitt7c3a2f82015-03-31 13:32:05 -070066void GrBatchFontCache::freeAll() {
67 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fCache);
68 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070069 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070070 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070071 ++iter;
72 }
73 fCache.rewind();
74 for (int i = 0; i < kMaskFormatCount; ++i) {
75 SkDELETE(fAtlases[i]);
76 fAtlases[i] = NULL;
77 }
78}
79
joshualitt7c3a2f82015-03-31 13:32:05 -070080void GrBatchFontCache::HandleEviction(GrBatchAtlas::AtlasID id, void* ptr) {
81 GrBatchFontCache* fontCache = reinterpret_cast<GrBatchFontCache*>(ptr);
82
83 SkTDynamicHash<GrBatchTextStrike, GrFontDescKey>::Iter iter(&fontCache->fCache);
84 for (; !iter.done(); ++iter) {
85 GrBatchTextStrike* strike = &*iter;
86 strike->removeID(id);
87
88 // clear out any empty strikes. We will preserve the strike whose call to addToAtlas
89 // triggered the eviction
90 if (strike != fontCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
91 fontCache->fCache.remove(*(strike->fFontScalerKey));
joshualittae32c102015-04-21 09:37:57 -070092 strike->fIsAbandoned = true;
93 strike->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070094 }
95 }
96}
97
98void GrBatchFontCache::dump() const {
99 static int gDumpCount = 0;
100 for (int i = 0; i < kMaskFormatCount; ++i) {
101 if (fAtlases[i]) {
102 GrTexture* texture = fAtlases[i]->getTexture();
103 if (texture) {
104 SkString filename;
105#ifdef SK_BUILD_FOR_ANDROID
106 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i);
107#else
108 filename.printf("fontcache_%d%d.png", gDumpCount, i);
109#endif
110 texture->surfacePriv().savePixels(filename.c_str());
111 }
112 }
113 }
114 ++gDumpCount;
115}
116
117///////////////////////////////////////////////////////////////////////////////
118
119/*
120 The text strike is specific to a given font/style/matrix setup, which is
121 represented by the GrHostFontScaler object we are given in getGlyph().
122
123 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
124 atlas and a position within that texture.
125 */
126
127GrBatchTextStrike::GrBatchTextStrike(GrBatchFontCache* cache, const GrFontDescKey* key)
128 : fFontScalerKey(SkRef(key))
129 , fPool(9/*start allocations at 512 bytes*/)
joshualittae32c102015-04-21 09:37:57 -0700130 , fAtlasedGlyphs(0)
131 , fIsAbandoned(false) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700132
133 fBatchFontCache = cache; // no need to ref, it won't go away before we do
134}
135
136GrBatchTextStrike::~GrBatchTextStrike() {
137 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
138 while (!iter.done()) {
139 (*iter).free();
140 ++iter;
141 }
142}
143
joshualitt6c2c2b02015-07-24 10:37:00 -0700144GrGlyph* GrBatchTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
joshualitt7c3a2f82015-03-31 13:32:05 -0700145 GrFontScaler* scaler) {
146 SkIRect bounds;
147 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
joshualitt6c2c2b02015-07-24 10:37:00 -0700148 if (!scaler->getPackedGlyphDFBounds(skGlyph, &bounds)) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700149 return NULL;
150 }
151 } else {
joshualitt6c2c2b02015-07-24 10:37:00 -0700152 if (!scaler->getPackedGlyphBounds(skGlyph, &bounds)) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700153 return NULL;
154 }
155 }
joshualitt6c2c2b02015-07-24 10:37:00 -0700156 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(skGlyph);
157
joshualitt7c3a2f82015-03-31 13:32:05 -0700158 GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW);
159 glyph->init(packed, bounds, format);
160 fCache.add(glyph);
161 return glyph;
162}
163
164void GrBatchTextStrike::removeID(GrBatchAtlas::AtlasID id) {
165 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
166 while (!iter.done()) {
167 if (id == (*iter).fID) {
168 (*iter).fID = GrBatchAtlas::kInvalidAtlasID;
169 fAtlasedGlyphs--;
170 SkASSERT(fAtlasedGlyphs >= 0);
171 }
172 ++iter;
173 }
174}
175
joshualitt7c3a2f82015-03-31 13:32:05 -0700176bool GrBatchTextStrike::addGlyphToAtlas(GrBatchTarget* batchTarget, GrGlyph* glyph,
joshualitt4f19ca32015-07-30 07:59:20 -0700177 GrFontScaler* scaler, const SkGlyph& skGlyph,
178 GrMaskFormat expectedMaskFormat) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700179 SkASSERT(glyph);
180 SkASSERT(scaler);
181 SkASSERT(fCache.find(glyph->fPackedID));
joshualitt7c3a2f82015-03-31 13:32:05 -0700182
183 SkAutoUnref ar(SkSafeRef(scaler));
184
joshualitt4f19ca32015-07-30 07:59:20 -0700185 int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
joshualitt7c3a2f82015-03-31 13:32:05 -0700186
187 size_t size = glyph->fBounds.area() * bytesPerPixel;
joshualitt29f86792015-05-29 08:06:48 -0700188 SkAutoSMalloc<1024> storage(size);
joshualitt7c3a2f82015-03-31 13:32:05 -0700189
190 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
joshualitt6c2c2b02015-07-24 10:37:00 -0700191 if (!scaler->getPackedGlyphDFImage(skGlyph, glyph->width(), glyph->height(),
joshualitt7c3a2f82015-03-31 13:32:05 -0700192 storage.get())) {
193 return false;
194 }
195 } else {
joshualitt6c2c2b02015-07-24 10:37:00 -0700196 if (!scaler->getPackedGlyphImage(skGlyph, glyph->width(), glyph->height(),
joshualitt4f19ca32015-07-30 07:59:20 -0700197 glyph->width() * bytesPerPixel, expectedMaskFormat,
198 storage.get())) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700199 return false;
200 }
201 }
202
joshualitt4f19ca32015-07-30 07:59:20 -0700203 bool success = fBatchFontCache->addToAtlas(this, &glyph->fID, batchTarget, expectedMaskFormat,
joshualitt7c3a2f82015-03-31 13:32:05 -0700204 glyph->width(), glyph->height(),
205 storage.get(), &glyph->fAtlasLocation);
206 if (success) {
joshualitt65e96b42015-07-31 11:45:22 -0700207 SkASSERT(GrBatchAtlas::kInvalidAtlasID != glyph->fID);
joshualitt7c3a2f82015-03-31 13:32:05 -0700208 fAtlasedGlyphs++;
209 }
210 return success;
211}