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