OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2010 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 "GrFontCache.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 | |
19 #define FONT_CACHE_STATS 0 | |
20 #if FONT_CACHE_STATS | |
21 static int g_PurgeCount = 0; | |
22 #endif | |
23 | |
24 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) { | |
25 gpu->ref(); | |
26 for (int i = 0; i < kAtlasCount; ++i) { | |
27 fAtlases[i] = NULL; | |
28 } | |
29 | |
30 fHead = fTail = NULL; | |
31 } | |
32 | |
33 GrFontCache::~GrFontCache() { | |
34 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache); | |
35 while (!iter.done()) { | |
36 SkDELETE(&(*iter)); | |
37 ++iter; | |
38 } | |
39 for (int i = 0; i < kAtlasCount; ++i) { | |
40 delete fAtlases[i]; | |
41 } | |
42 fGpu->unref(); | |
43 #if FONT_CACHE_STATS | |
44 SkDebugf("Num purges: %d\n", g_PurgeCount); | |
45 #endif | |
46 } | |
47 | |
48 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) { | |
49 static const GrPixelConfig sPixelConfigs[] = { | |
50 kAlpha_8_GrPixelConfig, | |
51 kRGB_565_GrPixelConfig, | |
52 kSkia8888_GrPixelConfig | |
53 }; | |
54 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_s
ize_mismatch); | |
55 | |
56 return sPixelConfigs[format]; | |
57 } | |
58 | |
59 static int mask_format_to_atlas_index(GrMaskFormat format) { | |
60 static const int sAtlasIndices[] = { | |
61 GrFontCache::kA8_AtlasType, | |
62 GrFontCache::k565_AtlasType, | |
63 GrFontCache::k8888_AtlasType | |
64 }; | |
65 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_s
ize_mismatch); | |
66 | |
67 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); | |
68 return sAtlasIndices[format]; | |
69 } | |
70 | |
71 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler) { | |
72 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, (this, scaler->getKey())); | |
73 fCache.add(strike); | |
74 | |
75 if (fHead) { | |
76 fHead->fPrev = strike; | |
77 } else { | |
78 SkASSERT(NULL == fTail); | |
79 fTail = strike; | |
80 } | |
81 strike->fPrev = NULL; | |
82 strike->fNext = fHead; | |
83 fHead = strike; | |
84 | |
85 return strike; | |
86 } | |
87 | |
88 void GrFontCache::freeAll() { | |
89 SkTDynamicHash<GrTextStrike, GrFontDescKey>::Iter iter(&fCache); | |
90 while (!iter.done()) { | |
91 SkDELETE(&(*iter)); | |
92 ++iter; | |
93 } | |
94 fCache.rewind(); | |
95 for (int i = 0; i < kAtlasCount; ++i) { | |
96 delete fAtlases[i]; | |
97 fAtlases[i] = NULL; | |
98 } | |
99 fHead = NULL; | |
100 fTail = NULL; | |
101 } | |
102 | |
103 void GrFontCache::purgeStrike(GrTextStrike* strike) { | |
104 fCache.remove(*(strike->fFontScalerKey)); | |
105 this->detachStrikeFromList(strike); | |
106 delete strike; | |
107 } | |
108 | |
109 | |
110 GrPlot* GrFontCache::addToAtlas(GrMaskFormat format, GrAtlas::ClientPlotUsage* u
sage, | |
111 int width, int height, const void* image, | |
112 SkIPoint16* loc) { | |
113 GrPixelConfig config = mask_format_to_pixel_config(format); | |
114 int atlasIndex = mask_format_to_atlas_index(format); | |
115 if (NULL == fAtlases[atlasIndex]) { | |
116 if (kA8_GrMaskFormat == format) { | |
117 SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_A8_TEXTURE_WIDTH, | |
118 GR_FONT_ATLAS_TEXTURE_HEIGHT); | |
119 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSu
rfaceFlags, | |
120 textureSize, | |
121 GR_FONT_ATLAS_A8_NUM_PLO
TS_X, | |
122 GR_FONT_ATLAS_NUM_PLOTS_
Y, | |
123 true)); | |
124 } else { | |
125 SkISize textureSize = SkISize::Make(GR_FONT_ATLAS_TEXTURE_WIDTH, | |
126 GR_FONT_ATLAS_TEXTURE_HEIGHT); | |
127 fAtlases[atlasIndex] = SkNEW_ARGS(GrAtlas, (fGpu, config, kNone_GrSu
rfaceFlags, | |
128 textureSize, | |
129 GR_FONT_ATLAS_NUM_PLOTS_
X, | |
130 GR_FONT_ATLAS_NUM_PLOTS_
Y, | |
131 true)); | |
132 } | |
133 } | |
134 return fAtlases[atlasIndex]->addToAtlas(usage, width, height, image, loc); | |
135 } | |
136 | |
137 | |
138 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike, const GrGlyph* gl
yph) { | |
139 SkASSERT(preserveStrike); | |
140 | |
141 int index = mask_format_to_atlas_index(glyph->fMaskFormat); | |
142 GrAtlas* atlas = fAtlases[index]; | |
143 GrPlot* plot = atlas->getUnusedPlot(); | |
144 if (NULL == plot) { | |
145 return false; | |
146 } | |
147 plot->resetRects(); | |
148 | |
149 GrTextStrike* strike = fHead; | |
150 while (strike) { | |
151 GrTextStrike* strikeToPurge = strike; | |
152 strike = strikeToPurge->fNext; | |
153 strikeToPurge->removePlot(plot); | |
154 | |
155 // clear out any empty strikes (except this one) | |
156 if (strikeToPurge != preserveStrike && strikeToPurge->fPlotUsage.isEmpty
()) { | |
157 this->purgeStrike(strikeToPurge); | |
158 } | |
159 } | |
160 | |
161 #if FONT_CACHE_STATS | |
162 ++g_PurgeCount; | |
163 #endif | |
164 | |
165 return true; | |
166 } | |
167 | |
168 #ifdef SK_DEBUG | |
169 void GrFontCache::validate() const { | |
170 int count = fCache.count(); | |
171 if (0 == count) { | |
172 SkASSERT(!fHead); | |
173 SkASSERT(!fTail); | |
174 } else if (1 == count) { | |
175 SkASSERT(fHead == fTail); | |
176 } else { | |
177 SkASSERT(fHead != fTail); | |
178 } | |
179 | |
180 int count2 = 0; | |
181 const GrTextStrike* strike = fHead; | |
182 while (strike) { | |
183 count2 += 1; | |
184 strike = strike->fNext; | |
185 } | |
186 SkASSERT(count == count2); | |
187 | |
188 count2 = 0; | |
189 strike = fTail; | |
190 while (strike) { | |
191 count2 += 1; | |
192 strike = strike->fPrev; | |
193 } | |
194 SkASSERT(count == count2); | |
195 } | |
196 #endif | |
197 | |
198 void GrFontCache::dump() const { | |
199 static int gDumpCount = 0; | |
200 for (int i = 0; i < kAtlasCount; ++i) { | |
201 if (fAtlases[i]) { | |
202 GrTexture* texture = fAtlases[i]->getTexture(); | |
203 if (texture) { | |
204 SkString filename; | |
205 #ifdef SK_BUILD_FOR_ANDROID | |
206 filename.printf("/sdcard/fontcache_%d%d.png", gDumpCount, i); | |
207 #else | |
208 filename.printf("fontcache_%d%d.png", gDumpCount, i); | |
209 #endif | |
210 texture->surfacePriv().savePixels(filename.c_str()); | |
211 } | |
212 } | |
213 } | |
214 ++gDumpCount; | |
215 } | |
216 | |
217 /////////////////////////////////////////////////////////////////////////////// | |
218 | |
219 #ifdef SK_DEBUG | |
220 static int gCounter; | |
221 #endif | |
222 | |
223 /* | |
224 The text strike is specific to a given font/style/matrix setup, which is | |
225 represented by the GrHostFontScaler object we are given in getGlyph(). | |
226 | |
227 We map a 32bit glyphID to a GrGlyph record, which in turn points to a | |
228 atlas and a position within that texture. | |
229 */ | |
230 | |
231 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrFontDescKey* key) | |
232 : fPool(9/*start allocations at 512 bytes*/) { | |
233 fFontScalerKey = key; | |
234 fFontScalerKey->ref(); | |
235 | |
236 fFontCache = cache; // no need to ref, it won't go away before we do | |
237 | |
238 #ifdef SK_DEBUG | |
239 // SkDebugf(" GrTextStrike %p %d\n", this, gCounter); | |
240 gCounter += 1; | |
241 #endif | |
242 } | |
243 | |
244 GrTextStrike::~GrTextStrike() { | |
245 fFontScalerKey->unref(); | |
246 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache); | |
247 while (!iter.done()) { | |
248 (*iter).free(); | |
249 ++iter; | |
250 } | |
251 | |
252 #ifdef SK_DEBUG | |
253 gCounter -= 1; | |
254 // SkDebugf("~GrTextStrike %p %d\n", this, gCounter); | |
255 #endif | |
256 } | |
257 | |
258 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, | |
259 GrFontScaler* scaler) { | |
260 SkIRect bounds; | |
261 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) { | |
262 if (!scaler->getPackedGlyphDFBounds(packed, &bounds)) { | |
263 return NULL; | |
264 } | |
265 } else { | |
266 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { | |
267 return NULL; | |
268 } | |
269 } | |
270 GrMaskFormat format = scaler->getPackedGlyphMaskFormat(packed); | |
271 | |
272 GrGlyph* glyph = (GrGlyph*)fPool.alloc(sizeof(GrGlyph), SK_MALLOC_THROW); | |
273 glyph->init(packed, bounds, format); | |
274 fCache.add(glyph); | |
275 return glyph; | |
276 } | |
277 | |
278 void GrTextStrike::removePlot(const GrPlot* plot) { | |
279 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache); | |
280 while (!iter.done()) { | |
281 if (plot == (*iter).fPlot) { | |
282 (*iter).fPlot = NULL; | |
283 } | |
284 ++iter; | |
285 } | |
286 | |
287 GrAtlas::RemovePlot(&fPlotUsage, plot); | |
288 } | |
289 | |
290 bool GrTextStrike::glyphTooLargeForAtlas(GrGlyph* glyph) { | |
291 int width = glyph->fBounds.width(); | |
292 int height = glyph->fBounds.height(); | |
293 bool useDistanceField = | |
294 (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle
(glyph->fPackedID)); | |
295 int pad = useDistanceField ? 2 * SK_DistanceFieldPad : 0; | |
296 int plotWidth = (kA8_GrMaskFormat == glyph->fMaskFormat) ? GR_FONT_ATLAS_A8_
PLOT_WIDTH | |
297 : GR_FONT_ATLAS_PLO
T_WIDTH; | |
298 if (width + pad > plotWidth) { | |
299 return true; | |
300 } | |
301 if (height + pad > GR_FONT_ATLAS_PLOT_HEIGHT) { | |
302 return true; | |
303 } | |
304 | |
305 return false; | |
306 } | |
307 | |
308 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { | |
309 #if 0 // testing hack to force us to flush our cache often | |
310 static int gCounter; | |
311 if ((++gCounter % 10) == 0) return false; | |
312 #endif | |
313 | |
314 SkASSERT(glyph); | |
315 SkASSERT(scaler); | |
316 SkASSERT(fCache.find(glyph->fPackedID)); | |
317 SkASSERT(NULL == glyph->fPlot); | |
318 | |
319 SkAutoUnref ar(SkSafeRef(scaler)); | |
320 | |
321 int bytesPerPixel = GrMaskFormatBytesPerPixel(glyph->fMaskFormat); | |
322 | |
323 size_t size = glyph->fBounds.area() * bytesPerPixel; | |
324 GrAutoMalloc<1024> storage(size); | |
325 | |
326 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedI
D)) { | |
327 if (!scaler->getPackedGlyphDFImage(glyph->fPackedID, glyph->width(), | |
328 glyph->height(), | |
329 storage.get())) { | |
330 return false; | |
331 } | |
332 } else { | |
333 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), | |
334 glyph->height(), | |
335 glyph->width() * bytesPerPixel, | |
336 storage.get())) { | |
337 return false; | |
338 } | |
339 } | |
340 | |
341 GrPlot* plot = fFontCache->addToAtlas(glyph->fMaskFormat, &fPlotUsage, | |
342 glyph->width(), glyph->height(), | |
343 storage.get(), &glyph->fAtlasLocation)
; | |
344 | |
345 if (NULL == plot) { | |
346 return false; | |
347 } | |
348 | |
349 glyph->fPlot = plot; | |
350 return true; | |
351 } | |
OLD | NEW |