• 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 "GrStrikeCache.h"
9 #include "GrAtlasManager.h"
10 #include "GrCaps.h"
11 #include "GrColor.h"
12 #include "GrDistanceFieldGenFromVector.h"
13 
14 #include "SkAutoMalloc.h"
15 #include "SkDistanceFieldGen.h"
16 
GrStrikeCache(const GrCaps * caps,size_t maxTextureBytes)17 GrStrikeCache::GrStrikeCache(const GrCaps* caps, size_t maxTextureBytes)
18         : fPreserveStrike(nullptr)
19         , f565Masks(SkMasks::CreateMasks({0xF800, 0x07E0, 0x001F, 0},
20                     GrMaskFormatBytesPerPixel(kA565_GrMaskFormat) * 8)) { }
21 
~GrStrikeCache()22 GrStrikeCache::~GrStrikeCache() {
23     StrikeHash::Iter iter(&fCache);
24     while (!iter.done()) {
25         (*iter).fIsAbandoned = true;
26         (*iter).unref();
27         ++iter;
28     }
29 }
30 
freeAll()31 void GrStrikeCache::freeAll() {
32     StrikeHash::Iter iter(&fCache);
33     while (!iter.done()) {
34         (*iter).fIsAbandoned = true;
35         (*iter).unref();
36         ++iter;
37     }
38     fCache.rewind();
39 }
40 
HandleEviction(GrDrawOpAtlas::AtlasID id,void * ptr)41 void GrStrikeCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) {
42     GrStrikeCache* glyphCache = reinterpret_cast<GrStrikeCache*>(ptr);
43 
44     StrikeHash::Iter iter(&glyphCache->fCache);
45     for (; !iter.done(); ++iter) {
46         GrTextStrike* strike = &*iter;
47         strike->removeID(id);
48 
49         // clear out any empty strikes.  We will preserve the strike whose call to addToAtlas
50         // triggered the eviction
51         if (strike != glyphCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
52             glyphCache->fCache.remove(GrTextStrike::GetKey(*strike));
53             strike->fIsAbandoned = true;
54             strike->unref();
55         }
56     }
57 }
58 
59 // expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
60 // A8, RGB565, or RGBA8888.
61 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)62 static void expand_bits(INT_TYPE* dst,
63                         const uint8_t* src,
64                         int width,
65                         int height,
66                         int dstRowBytes,
67                         int srcRowBytes) {
68     for (int i = 0; i < height; ++i) {
69         int rowWritesLeft = width;
70         const uint8_t* s = src;
71         INT_TYPE* d = dst;
72         while (rowWritesLeft > 0) {
73             unsigned mask = *s++;
74             for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
75                 *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
76             }
77         }
78         dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
79         src += srcRowBytes;
80     }
81 }
82 
get_packed_glyph_image(SkStrike * cache,const SkGlyph & glyph,int width,int height,int dstRB,GrMaskFormat expectedMaskFormat,void * dst,const SkMasks & masks)83 static bool get_packed_glyph_image(SkStrike* cache, const SkGlyph& glyph, int width,
84                                    int height, int dstRB, GrMaskFormat expectedMaskFormat,
85                                    void* dst, const SkMasks& masks) {
86     SkASSERT(glyph.fWidth == width);
87     SkASSERT(glyph.fHeight == height);
88     const void* src = cache->findImage(glyph);
89     if (nullptr == src) {
90         return false;
91     }
92 
93     // Convert if the glyph uses a 565 mask format since it is using LCD text rendering but the
94     // expected format is 8888 (will happen on macOS with Metal since that combination does not
95     // support 565).
96     if (kA565_GrMaskFormat == GrGlyph::FormatFromSkGlyph(glyph) &&
97         kARGB_GrMaskFormat == expectedMaskFormat) {
98         const int a565Bpp = GrMaskFormatBytesPerPixel(kA565_GrMaskFormat);
99         const int argbBpp = GrMaskFormatBytesPerPixel(kARGB_GrMaskFormat);
100         for (int y = 0; y < height; y++) {
101             for (int x = 0; x < width; x++) {
102                 uint16_t color565 = 0;
103                 memcpy(&color565, src, a565Bpp);
104                 uint32_t colorRGBA = GrColorPackRGBA(masks.getRed(color565),
105                                                      masks.getGreen(color565),
106                                                      masks.getBlue(color565),
107                                                      0xFF);
108                 memcpy(dst, &colorRGBA, argbBpp);
109                 src = (char*)src + a565Bpp;
110                 dst = (char*)dst + argbBpp;
111             }
112         }
113         return true;
114     }
115 
116     // crbug:510931
117     // Retrieving the image from the cache can actually change the mask format.  This case is very
118     // uncommon so for now we just draw a clear box for these glyphs.
119     if (GrGlyph::FormatFromSkGlyph(glyph) != expectedMaskFormat) {
120         const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
121         for (int y = 0; y < height; y++) {
122             sk_bzero(dst, width * bpp);
123             dst = (char*)dst + dstRB;
124         }
125         return true;
126     }
127 
128     int srcRB = glyph.rowBytes();
129     // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
130     // check the glyph's format, not the strike's format, and to be able to convert to any of the
131     // GrMaskFormats.
132     if (SkMask::kBW_Format == glyph.fMaskFormat) {
133         // expand bits to our mask type
134         const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
135         switch (expectedMaskFormat) {
136             case kA8_GrMaskFormat:{
137                 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
138                 expand_bits(bytes, bits, width, height, dstRB, srcRB);
139                 break;
140             }
141             case kA565_GrMaskFormat: {
142                 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
143                 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
144                 break;
145             }
146             default:
147                 SK_ABORT("Invalid GrMaskFormat");
148         }
149     } else if (srcRB == dstRB) {
150         memcpy(dst, src, dstRB * height);
151     } else {
152         const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
153         for (int y = 0; y < height; y++) {
154             memcpy(dst, src, width * bbp);
155             src = (const char*)src + srcRB;
156             dst = (char*)dst + dstRB;
157         }
158     }
159     return true;
160 }
161 
162 ///////////////////////////////////////////////////////////////////////////////
163 
164 /*
165     The text strike is specific to a given font/style/matrix setup, which is
166     represented by the GrHostFontScaler object we are given in getGlyph().
167 
168     We map a 32bit glyphID to a GrGlyph record, which in turn points to a
169     atlas and a position within that texture.
170  */
171 
GrTextStrike(const SkDescriptor & key)172 GrTextStrike::GrTextStrike(const SkDescriptor& key)
173     : fFontScalerKey(key) {}
174 
generateGlyph(const SkGlyph & skGlyph)175 GrGlyph* GrTextStrike::generateGlyph(const SkGlyph& skGlyph) {
176     GrGlyph* grGlyph = fAlloc.make<GrGlyph>(skGlyph);
177     fCache.add(grGlyph);
178     return grGlyph;
179 }
180 
removeID(GrDrawOpAtlas::AtlasID id)181 void GrTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
182     SkTDynamicHash<GrGlyph, SkPackedGlyphID>::Iter iter(&fCache);
183     while (!iter.done()) {
184         if (id == (*iter).fID) {
185             (*iter).fID = GrDrawOpAtlas::kInvalidAtlasID;
186             fAtlasedGlyphs--;
187             SkASSERT(fAtlasedGlyphs >= 0);
188         }
189         ++iter;
190     }
191 }
192 
addGlyphToAtlas(GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,GrStrikeCache * glyphCache,GrAtlasManager * fullAtlasManager,GrGlyph * glyph,SkStrike * cache,GrMaskFormat expectedMaskFormat,bool isScaledGlyph)193 GrDrawOpAtlas::ErrorCode GrTextStrike::addGlyphToAtlas(
194                                    GrResourceProvider* resourceProvider,
195                                    GrDeferredUploadTarget* target,
196                                    GrStrikeCache* glyphCache,
197                                    GrAtlasManager* fullAtlasManager,
198                                    GrGlyph* glyph,
199                                    SkStrike* cache,
200                                    GrMaskFormat expectedMaskFormat,
201                                    bool isScaledGlyph) {
202     SkASSERT(glyph);
203     SkASSERT(cache);
204     SkASSERT(fCache.find(glyph->fPackedID));
205 
206     expectedMaskFormat = fullAtlasManager->resolveMaskFormat(expectedMaskFormat);
207     int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
208     int width = glyph->width();
209     int height = glyph->height();
210     int rowBytes = width * bytesPerPixel;
211 
212     size_t size = glyph->fBounds.area() * bytesPerPixel;
213     bool isSDFGlyph = GrGlyph::kDistance_MaskStyle == glyph->maskStyle();
214     bool addPad = isScaledGlyph && !isSDFGlyph;
215     if (addPad) {
216         width += 2;
217         rowBytes += 2*bytesPerPixel;
218         size += 2 * rowBytes;
219         height += 2;
220         size += 2 * (height + 2) * bytesPerPixel;
221     }
222     SkAutoSMalloc<1024> storage(size);
223 
224     const SkGlyph& skGlyph = GrToSkGlyph(cache, glyph->fPackedID);
225     void* dataPtr = storage.get();
226     if (addPad) {
227         sk_bzero(dataPtr, size);
228         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
229     }
230     if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
231                                 rowBytes, expectedMaskFormat,
232                                 dataPtr, glyphCache->getMasks())) {
233         return GrDrawOpAtlas::ErrorCode::kError;
234     }
235 
236     GrDrawOpAtlas::ErrorCode result = fullAtlasManager->addToAtlas(
237                                                 resourceProvider, glyphCache, this,
238                                                 &glyph->fID, target, expectedMaskFormat,
239                                                 width, height,
240                                                 storage.get(), &glyph->fAtlasLocation);
241     if (GrDrawOpAtlas::ErrorCode::kSucceeded == result) {
242         if (addPad) {
243             glyph->fAtlasLocation.fX += 1;
244             glyph->fAtlasLocation.fY += 1;
245         }
246         SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID);
247         fAtlasedGlyphs++;
248     }
249     return result;
250 }
251