• 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 "src/gpu/GrCaps.h"
9 #include "src/gpu/GrColor.h"
10 #include "src/gpu/GrDistanceFieldGenFromVector.h"
11 #include "src/gpu/text/GrAtlasManager.h"
12 #include "src/gpu/text/GrStrikeCache.h"
13 
14 #include "src/core/SkAutoMalloc.h"
15 #include "src/core/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))) { }
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* grStrikeCache = reinterpret_cast<GrStrikeCache*>(ptr);
43 
44     StrikeHash::Iter iter(&grStrikeCache->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 != grStrikeCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
52             grStrikeCache->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,SkGlyph * glyph,int width,int height,int dstRB,GrMaskFormat expectedMaskFormat,void * dst,const SkMasks & masks)83 static bool get_packed_glyph_image(SkStrike* cache, SkGlyph* glyph, int width,
84                                    int height, int dstRB, GrMaskFormat expectedMaskFormat,
85                                    void* dst, const SkMasks& masks) {
86     SkASSERT(glyph->width() == width);
87     SkASSERT(glyph->height() == height);
88     const void* src = cache->prepareImage(glyph);
89     if (src == nullptr) {
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->maskFormat()) &&
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->maskFormat()) != 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 (glyph->maskFormat() == SkMask::kBW_Format) {
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 
removeID(GrDrawOpAtlas::AtlasID id)175 void GrTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
176     SkTDynamicHash<GrGlyph, SkPackedGlyphID>::Iter iter(&fCache);
177     while (!iter.done()) {
178         if (id == (*iter).fID) {
179             (*iter).fID = GrDrawOpAtlas::kInvalidAtlasID;
180             fAtlasedGlyphs--;
181             SkASSERT(fAtlasedGlyphs >= 0);
182         }
183         ++iter;
184     }
185 }
186 
addGlyphToAtlas(GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,GrStrikeCache * glyphCache,GrAtlasManager * fullAtlasManager,GrGlyph * glyph,SkStrike * skStrikeCache,GrMaskFormat expectedMaskFormat,bool isScaledGlyph)187 GrDrawOpAtlas::ErrorCode GrTextStrike::addGlyphToAtlas(
188                                    GrResourceProvider* resourceProvider,
189                                    GrDeferredUploadTarget* target,
190                                    GrStrikeCache* glyphCache,
191                                    GrAtlasManager* fullAtlasManager,
192                                    GrGlyph* glyph,
193                                    SkStrike* skStrikeCache,
194                                    GrMaskFormat expectedMaskFormat,
195                                    bool isScaledGlyph) {
196     SkASSERT(glyph);
197     SkASSERT(skStrikeCache);
198     SkASSERT(fCache.find(glyph->fPackedID));
199 
200     expectedMaskFormat = fullAtlasManager->resolveMaskFormat(expectedMaskFormat);
201     int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
202     int width = glyph->width();
203     int height = glyph->height();
204     int rowBytes = width * bytesPerPixel;
205 
206     size_t size = glyph->fBounds.area() * bytesPerPixel;
207     bool isSDFGlyph = GrGlyph::kDistance_MaskStyle == glyph->maskStyle();
208     bool addPad = isScaledGlyph && !isSDFGlyph;
209     if (addPad) {
210         width += 2;
211         rowBytes += 2*bytesPerPixel;
212         size += 2 * rowBytes;
213         height += 2;
214         size += 2 * (height + 2) * bytesPerPixel;
215     }
216     SkAutoSMalloc<1024> storage(size);
217 
218     SkGlyph* skGlyph = skStrikeCache->glyph(glyph->fPackedID);
219     void* dataPtr = storage.get();
220     if (addPad) {
221         sk_bzero(dataPtr, size);
222         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
223     }
224     if (!get_packed_glyph_image(skStrikeCache, skGlyph, glyph->width(), glyph->height(),
225                                 rowBytes, expectedMaskFormat,
226                                 dataPtr, glyphCache->getMasks())) {
227         return GrDrawOpAtlas::ErrorCode::kError;
228     }
229 
230     GrDrawOpAtlas::ErrorCode result = fullAtlasManager->addToAtlas(
231                                                 resourceProvider, glyphCache, this,
232                                                 &glyph->fID, target, expectedMaskFormat,
233                                                 width, height,
234                                                 storage.get(), &glyph->fAtlasLocation);
235     if (GrDrawOpAtlas::ErrorCode::kSucceeded == result) {
236         if (addPad) {
237             glyph->fAtlasLocation.fX += 1;
238             glyph->fAtlasLocation.fY += 1;
239         }
240         SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID);
241         fAtlasedGlyphs++;
242     }
243     return result;
244 }
245