|
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 "GrAtlas.h" |
|
9 #include "GrGpu.h" |
|
10 #include "GrRectanizer.h" |
|
11 #include "GrTextStrike.h" |
|
12 #include "GrTextStrike_impl.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 fAtlasMgr[i] = NULL; |
|
28 } |
|
29 |
|
30 fHead = fTail = NULL; |
|
31 } |
|
32 |
|
33 GrFontCache::~GrFontCache() { |
|
34 fCache.deleteAll(); |
|
35 for (int i = 0; i < kAtlasCount; ++i) { |
|
36 delete fAtlasMgr[i]; |
|
37 } |
|
38 fGpu->unref(); |
|
39 #if FONT_CACHE_STATS |
|
40 GrPrintf("Num purges: %d\n", g_PurgeCount); |
|
41 #endif |
|
42 } |
|
43 |
|
44 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format) { |
|
45 static const GrPixelConfig sPixelConfigs[] = { |
|
46 kAlpha_8_GrPixelConfig, |
|
47 kRGB_565_GrPixelConfig, |
|
48 kSkia8888_GrPixelConfig, |
|
49 kSkia8888_GrPixelConfig |
|
50 }; |
|
51 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sPixelConfigs) == kMaskFormatCount, array_size_mismatch); |
|
52 |
|
53 return sPixelConfigs[format]; |
|
54 } |
|
55 |
|
56 static int mask_format_to_atlas_index(GrMaskFormat format) { |
|
57 static const int sAtlasIndices[] = { |
|
58 GrFontCache::kA8_AtlasType, |
|
59 GrFontCache::k565_AtlasType, |
|
60 GrFontCache::k8888_AtlasType, |
|
61 GrFontCache::k8888_AtlasType |
|
62 }; |
|
63 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(sAtlasIndices) == kMaskFormatCount, array_size_mismatch); |
|
64 |
|
65 SkASSERT(sAtlasIndices[format] < GrFontCache::kAtlasCount); |
|
66 return sAtlasIndices[format]; |
|
67 } |
|
68 |
|
69 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler, |
|
70 const Key& key) { |
|
71 GrMaskFormat format = scaler->getMaskFormat(); |
|
72 GrPixelConfig config = mask_format_to_pixel_config(format); |
|
73 int atlasIndex = mask_format_to_atlas_index(format); |
|
74 if (NULL == fAtlasMgr[atlasIndex]) { |
|
75 fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config)); |
|
76 } |
|
77 GrTextStrike* strike = SkNEW_ARGS(GrTextStrike, |
|
78 (this, scaler->getKey(), format, fAtlasMgr[atlasIndex])); |
|
79 fCache.insert(key, strike); |
|
80 |
|
81 if (fHead) { |
|
82 fHead->fPrev = strike; |
|
83 } else { |
|
84 SkASSERT(NULL == fTail); |
|
85 fTail = strike; |
|
86 } |
|
87 strike->fPrev = NULL; |
|
88 strike->fNext = fHead; |
|
89 fHead = strike; |
|
90 |
|
91 return strike; |
|
92 } |
|
93 |
|
94 void GrFontCache::freeAll() { |
|
95 fCache.deleteAll(); |
|
96 for (int i = 0; i < kAtlasCount; ++i) { |
|
97 delete fAtlasMgr[i]; |
|
98 fAtlasMgr[i] = NULL; |
|
99 } |
|
100 fHead = NULL; |
|
101 fTail = NULL; |
|
102 } |
|
103 |
|
104 void GrFontCache::purgeStrike(GrTextStrike* strike) { |
|
105 const GrFontCache::Key key(strike->fFontScalerKey); |
|
106 fCache.remove(key, strike); |
|
107 this->detachStrikeFromList(strike); |
|
108 delete strike; |
|
109 } |
|
110 |
|
111 bool GrFontCache::freeUnusedPlot(GrTextStrike* preserveStrike) { |
|
112 SkASSERT(NULL != preserveStrike); |
|
113 |
|
114 GrAtlasMgr* atlasMgr = preserveStrike->fAtlasMgr; |
|
115 GrPlot* plot = atlasMgr->getUnusedPlot(); |
|
116 if (NULL == plot) { |
|
117 return false; |
|
118 } |
|
119 plot->resetRects(); |
|
120 |
|
121 GrTextStrike* strike = fHead; |
|
122 GrMaskFormat maskFormat = preserveStrike->fMaskFormat; |
|
123 while (strike) { |
|
124 if (maskFormat != strike->fMaskFormat) { |
|
125 strike = strike->fNext; |
|
126 continue; |
|
127 } |
|
128 |
|
129 GrTextStrike* strikeToPurge = strike; |
|
130 strike = strikeToPurge->fNext; |
|
131 strikeToPurge->removePlot(plot); |
|
132 |
|
133 // clear out any empty strikes (except this one) |
|
134 if (strikeToPurge != preserveStrike && strikeToPurge->fAtlas.isEmpty()) { |
|
135 this->purgeStrike(strikeToPurge); |
|
136 } |
|
137 } |
|
138 |
|
139 #if FONT_CACHE_STATS |
|
140 ++g_PurgeCount; |
|
141 #endif |
|
142 |
|
143 return true; |
|
144 } |
|
145 |
|
146 #ifdef SK_DEBUG |
|
147 void GrFontCache::validate() const { |
|
148 int count = fCache.count(); |
|
149 if (0 == count) { |
|
150 SkASSERT(!fHead); |
|
151 SkASSERT(!fTail); |
|
152 } else if (1 == count) { |
|
153 SkASSERT(fHead == fTail); |
|
154 } else { |
|
155 SkASSERT(fHead != fTail); |
|
156 } |
|
157 |
|
158 int count2 = 0; |
|
159 const GrTextStrike* strike = fHead; |
|
160 while (strike) { |
|
161 count2 += 1; |
|
162 strike = strike->fNext; |
|
163 } |
|
164 SkASSERT(count == count2); |
|
165 |
|
166 count2 = 0; |
|
167 strike = fTail; |
|
168 while (strike) { |
|
169 count2 += 1; |
|
170 strike = strike->fPrev; |
|
171 } |
|
172 SkASSERT(count == count2); |
|
173 } |
|
174 #endif |
|
175 |
|
176 #ifdef SK_DEVELOPER |
|
177 void GrFontCache::dump() const { |
|
178 static int gDumpCount = 0; |
|
179 for (int i = 0; i < kAtlasCount; ++i) { |
|
180 if (NULL != fAtlasMgr[i]) { |
|
181 GrTexture* texture = fAtlasMgr[i]->getTexture(); |
|
182 if (NULL != texture) { |
|
183 SkString filename; |
|
184 filename.printf("fontcache_%d%d.png", gDumpCount, i); |
|
185 texture->savePixels(filename.c_str()); |
|
186 } |
|
187 } |
|
188 } |
|
189 ++gDumpCount; |
|
190 } |
|
191 #endif |
|
192 |
|
193 /////////////////////////////////////////////////////////////////////////////// |
|
194 |
|
195 #ifdef SK_DEBUG |
|
196 static int gCounter; |
|
197 #endif |
|
198 |
|
199 // this acts as the max magnitude for the distance field, |
|
200 // as well as the pad we need around the glyph |
|
201 #define DISTANCE_FIELD_RANGE 4 |
|
202 |
|
203 /* |
|
204 The text strike is specific to a given font/style/matrix setup, which is |
|
205 represented by the GrHostFontScaler object we are given in getGlyph(). |
|
206 |
|
207 We map a 32bit glyphID to a GrGlyph record, which in turn points to a |
|
208 atlas and a position within that texture. |
|
209 */ |
|
210 |
|
211 GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key, |
|
212 GrMaskFormat format, |
|
213 GrAtlasMgr* atlasMgr) : fPool(64) { |
|
214 fFontScalerKey = key; |
|
215 fFontScalerKey->ref(); |
|
216 |
|
217 fFontCache = cache; // no need to ref, it won't go away before we do |
|
218 fAtlasMgr = atlasMgr; // no need to ref, it won't go away before we do |
|
219 |
|
220 fMaskFormat = format; |
|
221 |
|
222 #ifdef SK_DEBUG |
|
223 // GrPrintf(" GrTextStrike %p %d\n", this, gCounter); |
|
224 gCounter += 1; |
|
225 #endif |
|
226 } |
|
227 |
|
228 // this signature is needed because it's used with |
|
229 // SkTDArray::visitAll() (see destructor) |
|
230 static void free_glyph(GrGlyph*& glyph) { glyph->free(); } |
|
231 |
|
232 GrTextStrike::~GrTextStrike() { |
|
233 fFontScalerKey->unref(); |
|
234 fCache.getArray().visitAll(free_glyph); |
|
235 |
|
236 #ifdef SK_DEBUG |
|
237 gCounter -= 1; |
|
238 // GrPrintf("~GrTextStrike %p %d\n", this, gCounter); |
|
239 #endif |
|
240 } |
|
241 |
|
242 GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed, |
|
243 GrFontScaler* scaler) { |
|
244 SkIRect bounds; |
|
245 if (!scaler->getPackedGlyphBounds(packed, &bounds)) { |
|
246 return NULL; |
|
247 } |
|
248 |
|
249 GrGlyph* glyph = fPool.alloc(); |
|
250 // expand bounds to hold full distance field data |
|
251 if (fUseDistanceField) { |
|
252 bounds.fLeft -= DISTANCE_FIELD_RANGE; |
|
253 bounds.fRight += DISTANCE_FIELD_RANGE; |
|
254 bounds.fTop -= DISTANCE_FIELD_RANGE; |
|
255 bounds.fBottom += DISTANCE_FIELD_RANGE; |
|
256 } |
|
257 glyph->init(packed, bounds); |
|
258 fCache.insert(packed, glyph); |
|
259 return glyph; |
|
260 } |
|
261 |
|
262 void GrTextStrike::removePlot(const GrPlot* plot) { |
|
263 SkTDArray<GrGlyph*>& glyphArray = fCache.getArray(); |
|
264 for (int i = 0; i < glyphArray.count(); ++i) { |
|
265 if (plot == glyphArray[i]->fPlot) { |
|
266 glyphArray[i]->fPlot = NULL; |
|
267 } |
|
268 } |
|
269 |
|
270 fAtlasMgr->removePlot(&fAtlas, plot); |
|
271 } |
|
272 |
|
273 |
|
274 bool GrTextStrike::addGlyphToAtlas(GrGlyph* glyph, GrFontScaler* scaler) { |
|
275 #if 0 // testing hack to force us to flush our cache often |
|
276 static int gCounter; |
|
277 if ((++gCounter % 10) == 0) return false; |
|
278 #endif |
|
279 |
|
280 SkASSERT(glyph); |
|
281 SkASSERT(scaler); |
|
282 SkASSERT(fCache.contains(glyph)); |
|
283 SkASSERT(NULL == glyph->fPlot); |
|
284 |
|
285 SkAutoRef ar(scaler); |
|
286 |
|
287 int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat); |
|
288 |
|
289 GrPlot* plot; |
|
290 if (fUseDistanceField) { |
|
291 // we've already expanded the glyph dimensions to match the final size |
|
292 // but must shrink back down to get the packed glyph data |
|
293 int dfWidth = glyph->width(); |
|
294 int dfHeight = glyph->height(); |
|
295 int width = dfWidth - 2*DISTANCE_FIELD_RANGE; |
|
296 int height = dfHeight - 2*DISTANCE_FIELD_RANGE; |
|
297 int stride = width*bytesPerPixel; |
|
298 |
|
299 size_t size = width * height * bytesPerPixel; |
|
300 SkAutoSMalloc<1024> storage(size); |
|
301 if (!scaler->getPackedGlyphImage(glyph->fPackedID, width, height, stride, storage.get())) { |
|
302 return false; |
|
303 } |
|
304 |
|
305 // alloc storage for distance field glyph |
|
306 size_t dfSize = dfWidth * dfHeight * bytesPerPixel; |
|
307 SkAutoSMalloc<1024> dfStorage(dfSize); |
|
308 |
|
309 if (1 == bytesPerPixel) { |
|
310 (void) SkGenerateDistanceFieldFromImage((unsigned char*)dfStorage.get(), |
|
311 (unsigned char*)storage.get(), |
|
312 width, height, DISTANCE_FIELD_RANGE); |
|
313 } else { |
|
314 // TODO: Fix color emoji |
|
315 // for now, copy glyph into distance field storage |
|
316 // this is not correct, but it won't crash |
|
317 sk_bzero(dfStorage.get(), dfSize); |
|
318 unsigned char* ptr = (unsigned char*) storage.get(); |
|
319 unsigned char* dfPtr = (unsigned char*) dfStorage.get(); |
|
320 size_t dfStride = dfWidth*bytesPerPixel; |
|
321 dfPtr += DISTANCE_FIELD_RANGE*dfStride; |
|
322 dfPtr += DISTANCE_FIELD_RANGE*bytesPerPixel; |
|
323 |
|
324 for (int i = 0; i < height; ++i) { |
|
325 memcpy(dfPtr, ptr, stride); |
|
326 |
|
327 dfPtr += dfStride; |
|
328 ptr += stride; |
|
329 } |
|
330 } |
|
331 |
|
332 // copy to atlas |
|
333 plot = fAtlasMgr->addToAtlas(&fAtlas, dfWidth, dfHeight, dfStorage.get(), |
|
334 &glyph->fAtlasLocation); |
|
335 |
|
336 } else { |
|
337 size_t size = glyph->fBounds.area() * bytesPerPixel; |
|
338 SkAutoSMalloc<1024> storage(size); |
|
339 if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(), |
|
340 glyph->height(), |
|
341 glyph->width() * bytesPerPixel, |
|
342 storage.get())) { |
|
343 return false; |
|
344 } |
|
345 |
|
346 plot = fAtlasMgr->addToAtlas(&fAtlas, glyph->width(), |
|
347 glyph->height(), storage.get(), |
|
348 &glyph->fAtlasLocation); |
|
349 } |
|
350 |
|
351 if (NULL == plot) { |
|
352 return false; |
|
353 } |
|
354 |
|
355 glyph->fPlot = plot; |
|
356 return true; |
|
357 } |