1 /*
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 "GrAtlasManager.h"
9 #include "GrDistanceFieldGenFromVector.h"
10 #include "GrGlyphCache.h"
11
12 #include "SkAutoMalloc.h"
13 #include "SkDistanceFieldGen.h"
14
GrGlyphCache()15 GrGlyphCache::GrGlyphCache()
16 : fPreserveStrike(nullptr)
17 , fGlyphSizeLimit(0) {
18 }
19
~GrGlyphCache()20 GrGlyphCache::~GrGlyphCache() {
21 StrikeHash::Iter iter(&fCache);
22 while (!iter.done()) {
23 (*iter).fIsAbandoned = true;
24 (*iter).unref();
25 ++iter;
26 }
27 }
28
freeAll()29 void GrGlyphCache::freeAll() {
30 StrikeHash::Iter iter(&fCache);
31 while (!iter.done()) {
32 (*iter).fIsAbandoned = true;
33 (*iter).unref();
34 ++iter;
35 }
36 fCache.rewind();
37 }
38
HandleEviction(GrDrawOpAtlas::AtlasID id,void * ptr)39 void GrGlyphCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) {
40 GrGlyphCache* glyphCache = reinterpret_cast<GrGlyphCache*>(ptr);
41
42 StrikeHash::Iter iter(&glyphCache->fCache);
43 for (; !iter.done(); ++iter) {
44 GrTextStrike* strike = &*iter;
45 strike->removeID(id);
46
47 // clear out any empty strikes. We will preserve the strike whose call to addToAtlas
48 // triggered the eviction
49 if (strike != glyphCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
50 glyphCache->fCache.remove(GrTextStrike::GetKey(*strike));
51 strike->fIsAbandoned = true;
52 strike->unref();
53 }
54 }
55 }
56
get_packed_glyph_mask_format(const SkGlyph & glyph)57 static inline GrMaskFormat get_packed_glyph_mask_format(const SkGlyph& glyph) {
58 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
59 switch (format) {
60 case SkMask::kBW_Format:
61 // fall through to kA8 -- we store BW glyphs in our 8-bit cache
62 case SkMask::kA8_Format:
63 return kA8_GrMaskFormat;
64 case SkMask::k3D_Format:
65 return kA8_GrMaskFormat; // ignore the mul and add planes, just use the mask
66 case SkMask::kLCD16_Format:
67 return kA565_GrMaskFormat;
68 case SkMask::kARGB32_Format:
69 return kARGB_GrMaskFormat;
70 default:
71 SkDEBUGFAIL("unsupported SkMask::Format");
72 return kA8_GrMaskFormat;
73 }
74 }
75
get_packed_glyph_bounds(SkGlyphCache * cache,const SkGlyph & glyph,SkIRect * bounds)76 static inline bool get_packed_glyph_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
77 SkIRect* bounds) {
78 #if 1
79 // crbug:510931
80 // Retrieving the image from the cache can actually change the mask format.
81 cache->findImage(glyph);
82 #endif
83 bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
84
85 return true;
86 }
87
get_packed_glyph_df_bounds(SkGlyphCache * cache,const SkGlyph & glyph,SkIRect * bounds)88 static inline bool get_packed_glyph_df_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
89 SkIRect* bounds) {
90 #if 1
91 // crbug:510931
92 // Retrieving the image from the cache can actually change the mask format.
93 cache->findImage(glyph);
94 #endif
95 bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
96 bounds->outset(SK_DistanceFieldPad, SK_DistanceFieldPad);
97
98 return true;
99 }
100
101 // expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
102 // A8, RGB565, or RGBA8888.
103 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)104 static void expand_bits(INT_TYPE* dst,
105 const uint8_t* src,
106 int width,
107 int height,
108 int dstRowBytes,
109 int srcRowBytes) {
110 for (int i = 0; i < height; ++i) {
111 int rowWritesLeft = width;
112 const uint8_t* s = src;
113 INT_TYPE* d = dst;
114 while (rowWritesLeft > 0) {
115 unsigned mask = *s++;
116 for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
117 *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
118 }
119 }
120 dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
121 src += srcRowBytes;
122 }
123 }
124
get_packed_glyph_image(SkGlyphCache * cache,const SkGlyph & glyph,int width,int height,int dstRB,GrMaskFormat expectedMaskFormat,void * dst)125 static bool get_packed_glyph_image(SkGlyphCache* cache, const SkGlyph& glyph, int width,
126 int height, int dstRB, GrMaskFormat expectedMaskFormat,
127 void* dst) {
128 SkASSERT(glyph.fWidth == width);
129 SkASSERT(glyph.fHeight == height);
130 const void* src = cache->findImage(glyph);
131 if (nullptr == src) {
132 return false;
133 }
134
135 // crbug:510931
136 // Retrieving the image from the cache can actually change the mask format. This case is very
137 // uncommon so for now we just draw a clear box for these glyphs.
138 if (get_packed_glyph_mask_format(glyph) != expectedMaskFormat) {
139 const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
140 for (int y = 0; y < height; y++) {
141 sk_bzero(dst, width * bpp);
142 dst = (char*)dst + dstRB;
143 }
144 return true;
145 }
146
147 int srcRB = glyph.rowBytes();
148 // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
149 // check the glyph's format, not the strike's format, and to be able to convert to any of the
150 // GrMaskFormats.
151 if (SkMask::kBW_Format == glyph.fMaskFormat) {
152 // expand bits to our mask type
153 const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
154 switch (expectedMaskFormat) {
155 case kA8_GrMaskFormat:{
156 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
157 expand_bits(bytes, bits, width, height, dstRB, srcRB);
158 break;
159 }
160 case kA565_GrMaskFormat: {
161 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
162 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
163 break;
164 }
165 default:
166 SK_ABORT("Invalid GrMaskFormat");
167 }
168 } else if (srcRB == dstRB) {
169 memcpy(dst, src, dstRB * height);
170 } else {
171 const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
172 for (int y = 0; y < height; y++) {
173 memcpy(dst, src, width * bbp);
174 src = (const char*)src + srcRB;
175 dst = (char*)dst + dstRB;
176 }
177 }
178 return true;
179 }
180
get_packed_glyph_df_image(SkGlyphCache * cache,const SkGlyph & glyph,int width,int height,void * dst)181 static bool get_packed_glyph_df_image(SkGlyphCache* cache, const SkGlyph& glyph,
182 int width, int height, void* dst) {
183 SkASSERT(glyph.fWidth + 2*SK_DistanceFieldPad == width);
184 SkASSERT(glyph.fHeight + 2*SK_DistanceFieldPad == height);
185
186 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
187 const SkPath* path = cache->findPath(glyph);
188 if (nullptr == path) {
189 return false;
190 }
191
192 SkDEBUGCODE(SkRect glyphBounds = SkRect::MakeXYWH(glyph.fLeft,
193 glyph.fTop,
194 glyph.fWidth,
195 glyph.fHeight));
196 SkASSERT(glyphBounds.contains(path->getBounds()));
197
198 // now generate the distance field
199 SkASSERT(dst);
200 SkMatrix drawMatrix;
201 drawMatrix.setTranslate((SkScalar)-glyph.fLeft, (SkScalar)-glyph.fTop);
202
203 // Generate signed distance field directly from SkPath
204 bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dst,
205 *path, drawMatrix,
206 width, height, width * sizeof(unsigned char));
207
208 if (!succeed) {
209 #endif
210 const void* image = cache->findImage(glyph);
211 if (nullptr == image) {
212 return false;
213 }
214
215 // now generate the distance field
216 SkASSERT(dst);
217 SkMask::Format maskFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
218 if (SkMask::kA8_Format == maskFormat) {
219 // make the distance field from the image
220 SkGenerateDistanceFieldFromA8Image((unsigned char*)dst,
221 (unsigned char*)image,
222 glyph.fWidth, glyph.fHeight,
223 glyph.rowBytes());
224 } else if (SkMask::kBW_Format == maskFormat) {
225 // make the distance field from the image
226 SkGenerateDistanceFieldFromBWImage((unsigned char*)dst,
227 (unsigned char*)image,
228 glyph.fWidth, glyph.fHeight,
229 glyph.rowBytes());
230 } else {
231 return false;
232 }
233 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
234 }
235 #endif
236 return true;
237 }
238
239 ///////////////////////////////////////////////////////////////////////////////
240
241 /*
242 The text strike is specific to a given font/style/matrix setup, which is
243 represented by the GrHostFontScaler object we are given in getGlyph().
244
245 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
246 atlas and a position within that texture.
247 */
248
GrTextStrike(const SkDescriptor & key)249 GrTextStrike::GrTextStrike(const SkDescriptor& key)
250 : fFontScalerKey(key)
251 , fPool(9/*start allocations at 512 bytes*/)
252 , fAtlasedGlyphs(0)
253 , fIsAbandoned(false) {}
254
~GrTextStrike()255 GrTextStrike::~GrTextStrike() {
256 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
257 while (!iter.done()) {
258 (*iter).reset();
259 ++iter;
260 }
261 }
262
generateGlyph(const SkGlyph & skGlyph,GrGlyph::PackedID packed,SkGlyphCache * cache)263 GrGlyph* GrTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
264 SkGlyphCache* cache) {
265 SkIRect bounds;
266 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(packed)) {
267 if (!get_packed_glyph_df_bounds(cache, skGlyph, &bounds)) {
268 return nullptr;
269 }
270 } else {
271 if (!get_packed_glyph_bounds(cache, skGlyph, &bounds)) {
272 return nullptr;
273 }
274 }
275 GrMaskFormat format = get_packed_glyph_mask_format(skGlyph);
276
277 GrGlyph* glyph = fPool.make<GrGlyph>();
278 glyph->init(packed, bounds, format);
279 fCache.add(glyph);
280 return glyph;
281 }
282
removeID(GrDrawOpAtlas::AtlasID id)283 void GrTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
284 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
285 while (!iter.done()) {
286 if (id == (*iter).fID) {
287 (*iter).fID = GrDrawOpAtlas::kInvalidAtlasID;
288 fAtlasedGlyphs--;
289 SkASSERT(fAtlasedGlyphs >= 0);
290 }
291 ++iter;
292 }
293 }
294
addGlyphToAtlas(GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,GrGlyphCache * glyphCache,GrAtlasManager * fullAtlasManager,GrGlyph * glyph,SkGlyphCache * cache,GrMaskFormat expectedMaskFormat)295 bool GrTextStrike::addGlyphToAtlas(GrResourceProvider* resourceProvider,
296 GrDeferredUploadTarget* target,
297 GrGlyphCache* glyphCache,
298 GrAtlasManager* fullAtlasManager,
299 GrGlyph* glyph,
300 SkGlyphCache* cache,
301 GrMaskFormat expectedMaskFormat) {
302 SkASSERT(glyph);
303 SkASSERT(cache);
304 SkASSERT(fCache.find(glyph->fPackedID));
305
306 int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
307
308 size_t size = glyph->fBounds.area() * bytesPerPixel;
309 SkAutoSMalloc<1024> storage(size);
310
311 const SkGlyph& skGlyph = GrToSkGlyph(cache, glyph->fPackedID);
312 if (GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID)) {
313 if (!get_packed_glyph_df_image(cache, skGlyph, glyph->width(), glyph->height(),
314 storage.get())) {
315 return false;
316 }
317 } else {
318 if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
319 glyph->width() * bytesPerPixel, expectedMaskFormat,
320 storage.get())) {
321 return false;
322 }
323 }
324
325 bool success = fullAtlasManager->addToAtlas(resourceProvider, glyphCache, this,
326 &glyph->fID, target, expectedMaskFormat,
327 glyph->width(), glyph->height(),
328 storage.get(), &glyph->fAtlasLocation);
329 if (success) {
330 SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID);
331 fAtlasedGlyphs++;
332 }
333 return success;
334 }
335