• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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