blob: 7fcd30866f085975a1a9f4f44bdc84f5c9b22baf [file] [log] [blame]
joshualitt7c3a2f82015-03-31 13:32:05 -07001/*
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
Robert Phillipsc4039ea2018-03-01 11:36:45 -05008#include "GrGlyphCache.h"
Khushalfa8ff092018-06-06 17:46:38 -07009#include "GrAtlasManager.h"
10#include "GrCaps.h"
Timothy Liang91e260f2018-06-15 13:28:35 -040011#include "GrColor.h"
Khushalfa8ff092018-06-06 17:46:38 -070012#include "GrDistanceFieldGenFromVector.h"
Robert Phillipsf95b1752017-08-31 08:56:07 -040013
Hal Canary95e3c052017-01-11 12:44:43 -050014#include "SkAutoMalloc.h"
Robert Phillipsf95b1752017-08-31 08:56:07 -040015#include "SkDistanceFieldGen.h"
joshualitt7c3a2f82015-03-31 13:32:05 -070016
Khushalfa8ff092018-06-06 17:46:38 -070017GrGlyphCache::GrGlyphCache(const GrCaps* caps, size_t maxTextureBytes)
Timothy Liang91e260f2018-06-15 13:28:35 -040018 : fPreserveStrike(nullptr)
Timothy Liang91e260f2018-06-15 13:28:35 -040019 , f565Masks(SkMasks::CreateMasks({0xF800, 0x07E0, 0x001F, 0},
Herb Derby96519f22018-09-12 17:15:26 -040020 GrMaskFormatBytesPerPixel(kA565_GrMaskFormat) * 8)) { }
joshualitt62db8ba2015-04-09 08:22:37 -070021
Robert Phillipsc4039ea2018-03-01 11:36:45 -050022GrGlyphCache::~GrGlyphCache() {
bsalomonc5fd5c42016-05-17 11:58:24 -070023 StrikeHash::Iter iter(&fCache);
joshualitt7c3a2f82015-03-31 13:32:05 -070024 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070025 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070026 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070027 ++iter;
28 }
joshualitt7c3a2f82015-03-31 13:32:05 -070029}
30
Robert Phillipsc4039ea2018-03-01 11:36:45 -050031void GrGlyphCache::freeAll() {
bsalomonc5fd5c42016-05-17 11:58:24 -070032 StrikeHash::Iter iter(&fCache);
joshualitt7c3a2f82015-03-31 13:32:05 -070033 while (!iter.done()) {
joshualitta5f1d5a2015-05-22 13:09:57 -070034 (*iter).fIsAbandoned = true;
joshualittae32c102015-04-21 09:37:57 -070035 (*iter).unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070036 ++iter;
37 }
38 fCache.rewind();
joshualitt7c3a2f82015-03-31 13:32:05 -070039}
40
Robert Phillipsc4039ea2018-03-01 11:36:45 -050041void GrGlyphCache::HandleEviction(GrDrawOpAtlas::AtlasID id, void* ptr) {
42 GrGlyphCache* glyphCache = reinterpret_cast<GrGlyphCache*>(ptr);
joshualitt7c3a2f82015-03-31 13:32:05 -070043
Robert Phillipsc4039ea2018-03-01 11:36:45 -050044 StrikeHash::Iter iter(&glyphCache->fCache);
joshualitt7c3a2f82015-03-31 13:32:05 -070045 for (; !iter.done(); ++iter) {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -050046 GrTextStrike* strike = &*iter;
joshualitt7c3a2f82015-03-31 13:32:05 -070047 strike->removeID(id);
48
49 // clear out any empty strikes. We will preserve the strike whose call to addToAtlas
50 // triggered the eviction
Robert Phillipsc4039ea2018-03-01 11:36:45 -050051 if (strike != glyphCache->fPreserveStrike && 0 == strike->fAtlasedGlyphs) {
Robert Phillipscaf1ebb2018-03-01 14:28:44 -050052 glyphCache->fCache.remove(GrTextStrike::GetKey(*strike));
joshualittae32c102015-04-21 09:37:57 -070053 strike->fIsAbandoned = true;
54 strike->unref();
joshualitt7c3a2f82015-03-31 13:32:05 -070055 }
56 }
57}
58
bsalomonc2878e22016-05-17 13:18:03 -070059static inline GrMaskFormat get_packed_glyph_mask_format(const SkGlyph& glyph) {
60 SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
61 switch (format) {
62 case SkMask::kBW_Format:
Jim Van Verthd401da62018-05-03 10:40:30 -040063 case SkMask::kSDF_Format:
64 // fall through to kA8 -- we store BW and SDF glyphs in our 8-bit cache
bsalomonc2878e22016-05-17 13:18:03 -070065 case SkMask::kA8_Format:
66 return kA8_GrMaskFormat;
Ben Wagner339b84e2017-11-10 16:24:50 -050067 case SkMask::k3D_Format:
68 return kA8_GrMaskFormat; // ignore the mul and add planes, just use the mask
bsalomonc2878e22016-05-17 13:18:03 -070069 case SkMask::kLCD16_Format:
70 return kA565_GrMaskFormat;
71 case SkMask::kARGB32_Format:
72 return kARGB_GrMaskFormat;
73 default:
74 SkDEBUGFAIL("unsupported SkMask::Format");
75 return kA8_GrMaskFormat;
76 }
77}
78
79static inline bool get_packed_glyph_bounds(SkGlyphCache* cache, const SkGlyph& glyph,
80 SkIRect* bounds) {
81#if 1
82 // crbug:510931
83 // Retrieving the image from the cache can actually change the mask format.
84 cache->findImage(glyph);
85#endif
86 bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
87
88 return true;
89}
90
bsalomonc2878e22016-05-17 13:18:03 -070091// expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
92// A8, RGB565, or RGBA8888.
93template <typename INT_TYPE>
94static void expand_bits(INT_TYPE* dst,
95 const uint8_t* src,
96 int width,
97 int height,
98 int dstRowBytes,
99 int srcRowBytes) {
100 for (int i = 0; i < height; ++i) {
101 int rowWritesLeft = width;
102 const uint8_t* s = src;
103 INT_TYPE* d = dst;
104 while (rowWritesLeft > 0) {
105 unsigned mask = *s++;
106 for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
107 *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
108 }
109 }
110 dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
111 src += srcRowBytes;
112 }
113}
114
115static bool get_packed_glyph_image(SkGlyphCache* cache, const SkGlyph& glyph, int width,
116 int height, int dstRB, GrMaskFormat expectedMaskFormat,
Timothy Liang91e260f2018-06-15 13:28:35 -0400117 void* dst, const SkMasks& masks) {
bsalomonc2878e22016-05-17 13:18:03 -0700118 SkASSERT(glyph.fWidth == width);
119 SkASSERT(glyph.fHeight == height);
120 const void* src = cache->findImage(glyph);
121 if (nullptr == src) {
122 return false;
123 }
124
Timothy Liang91e260f2018-06-15 13:28:35 -0400125 // Convert if the glyph uses a 565 mask format since it is using LCD text rendering but the
126 // expected format is 8888 (will happen on macOS with Metal since that combination does not
127 // support 565).
128 if (kA565_GrMaskFormat == get_packed_glyph_mask_format(glyph) &&
129 kARGB_GrMaskFormat == expectedMaskFormat) {
130 const int a565Bpp = GrMaskFormatBytesPerPixel(kA565_GrMaskFormat);
131 const int argbBpp = GrMaskFormatBytesPerPixel(kARGB_GrMaskFormat);
132 for (int y = 0; y < height; y++) {
133 for (int x = 0; x < width; x++) {
134 uint16_t color565 = 0;
135 memcpy(&color565, src, a565Bpp);
136 uint32_t colorRGBA = GrColorPackRGBA(masks.getRed(color565),
137 masks.getGreen(color565),
138 masks.getBlue(color565),
139 0xFF);
140 memcpy(dst, &colorRGBA, argbBpp);
141 src = (char*)src + a565Bpp;
142 dst = (char*)dst + argbBpp;
143 }
144 }
145 return true;
146 }
147
bsalomonc2878e22016-05-17 13:18:03 -0700148 // crbug:510931
149 // Retrieving the image from the cache can actually change the mask format. This case is very
150 // uncommon so for now we just draw a clear box for these glyphs.
151 if (get_packed_glyph_mask_format(glyph) != expectedMaskFormat) {
152 const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
153 for (int y = 0; y < height; y++) {
154 sk_bzero(dst, width * bpp);
155 dst = (char*)dst + dstRB;
156 }
157 return true;
158 }
159
160 int srcRB = glyph.rowBytes();
161 // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
162 // check the glyph's format, not the strike's format, and to be able to convert to any of the
163 // GrMaskFormats.
164 if (SkMask::kBW_Format == glyph.fMaskFormat) {
165 // expand bits to our mask type
166 const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
167 switch (expectedMaskFormat) {
168 case kA8_GrMaskFormat:{
169 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
170 expand_bits(bytes, bits, width, height, dstRB, srcRB);
171 break;
172 }
173 case kA565_GrMaskFormat: {
174 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
175 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
176 break;
177 }
178 default:
Ben Wagnerb4aab9a2017-08-16 10:53:04 -0400179 SK_ABORT("Invalid GrMaskFormat");
bsalomonc2878e22016-05-17 13:18:03 -0700180 }
181 } else if (srcRB == dstRB) {
182 memcpy(dst, src, dstRB * height);
183 } else {
184 const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
185 for (int y = 0; y < height; y++) {
186 memcpy(dst, src, width * bbp);
187 src = (const char*)src + srcRB;
188 dst = (char*)dst + dstRB;
189 }
190 }
191 return true;
192}
193
bsalomonc2878e22016-05-17 13:18:03 -0700194///////////////////////////////////////////////////////////////////////////////
195
joshualitt7c3a2f82015-03-31 13:32:05 -0700196/*
197 The text strike is specific to a given font/style/matrix setup, which is
198 represented by the GrHostFontScaler object we are given in getGlyph().
199
200 We map a 32bit glyphID to a GrGlyph record, which in turn points to a
201 atlas and a position within that texture.
202 */
203
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500204GrTextStrike::GrTextStrike(const SkDescriptor& key)
bsalomonc5fd5c42016-05-17 11:58:24 -0700205 : fFontScalerKey(key)
joshualitt7c3a2f82015-03-31 13:32:05 -0700206 , fPool(9/*start allocations at 512 bytes*/)
joshualittae32c102015-04-21 09:37:57 -0700207 , fAtlasedGlyphs(0)
bsalomonc5fd5c42016-05-17 11:58:24 -0700208 , fIsAbandoned(false) {}
joshualitt7c3a2f82015-03-31 13:32:05 -0700209
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500210GrTextStrike::~GrTextStrike() {
joshualitt7c3a2f82015-03-31 13:32:05 -0700211 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
212 while (!iter.done()) {
mtklein852f15d2016-03-17 10:51:27 -0700213 (*iter).reset();
joshualitt7c3a2f82015-03-31 13:32:05 -0700214 ++iter;
215 }
216}
217
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500218GrGlyph* GrTextStrike::generateGlyph(const SkGlyph& skGlyph, GrGlyph::PackedID packed,
219 SkGlyphCache* cache) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700220 SkIRect bounds;
Jim Van Verthd401da62018-05-03 10:40:30 -0400221 if (!get_packed_glyph_bounds(cache, skGlyph, &bounds)) {
222 return nullptr;
joshualitt7c3a2f82015-03-31 13:32:05 -0700223 }
bsalomonc2878e22016-05-17 13:18:03 -0700224 GrMaskFormat format = get_packed_glyph_mask_format(skGlyph);
joshualitt6c2c2b02015-07-24 10:37:00 -0700225
Herb Derby9428a372017-04-10 11:25:30 -0400226 GrGlyph* glyph = fPool.make<GrGlyph>();
joshualitt7c3a2f82015-03-31 13:32:05 -0700227 glyph->init(packed, bounds, format);
228 fCache.add(glyph);
229 return glyph;
230}
231
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500232void GrTextStrike::removeID(GrDrawOpAtlas::AtlasID id) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700233 SkTDynamicHash<GrGlyph, GrGlyph::PackedID>::Iter iter(&fCache);
234 while (!iter.done()) {
235 if (id == (*iter).fID) {
Brian Salomon2ee084e2016-12-16 18:59:19 -0500236 (*iter).fID = GrDrawOpAtlas::kInvalidAtlasID;
joshualitt7c3a2f82015-03-31 13:32:05 -0700237 fAtlasedGlyphs--;
238 SkASSERT(fAtlasedGlyphs >= 0);
239 }
240 ++iter;
241 }
242}
243
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500244GrDrawOpAtlas::ErrorCode GrTextStrike::addGlyphToAtlas(
245 GrResourceProvider* resourceProvider,
Robert Phillipscaf1ebb2018-03-01 14:28:44 -0500246 GrDeferredUploadTarget* target,
247 GrGlyphCache* glyphCache,
248 GrAtlasManager* fullAtlasManager,
249 GrGlyph* glyph,
250 SkGlyphCache* cache,
Jim Van Verthcf838c72018-03-05 14:40:36 -0500251 GrMaskFormat expectedMaskFormat,
252 bool isScaledGlyph) {
joshualitt7c3a2f82015-03-31 13:32:05 -0700253 SkASSERT(glyph);
bsalomonc2878e22016-05-17 13:18:03 -0700254 SkASSERT(cache);
joshualitt7c3a2f82015-03-31 13:32:05 -0700255 SkASSERT(fCache.find(glyph->fPackedID));
joshualitt7c3a2f82015-03-31 13:32:05 -0700256
Timothy Liang91e260f2018-06-15 13:28:35 -0400257 expectedMaskFormat = fullAtlasManager->resolveMaskFormat(expectedMaskFormat);
joshualitt4f19ca32015-07-30 07:59:20 -0700258 int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
Jim Van Verthcf838c72018-03-05 14:40:36 -0500259 int width = glyph->width();
260 int height = glyph->height();
261 int rowBytes = width * bytesPerPixel;
joshualitt7c3a2f82015-03-31 13:32:05 -0700262
263 size_t size = glyph->fBounds.area() * bytesPerPixel;
Jim Van Verthcf838c72018-03-05 14:40:36 -0500264 bool isSDFGlyph = GrGlyph::kDistance_MaskStyle == GrGlyph::UnpackMaskStyle(glyph->fPackedID);
265 bool addPad = isScaledGlyph && !isSDFGlyph;
266 if (addPad) {
267 width += 2;
268 rowBytes += 2*bytesPerPixel;
269 size += 2 * rowBytes;
270 height += 2;
271 size += 2 * (height + 2) * bytesPerPixel;
272 }
joshualitt29f86792015-05-29 08:06:48 -0700273 SkAutoSMalloc<1024> storage(size);
joshualitt7c3a2f82015-03-31 13:32:05 -0700274
bsalomonc2878e22016-05-17 13:18:03 -0700275 const SkGlyph& skGlyph = GrToSkGlyph(cache, glyph->fPackedID);
Jim Van Verthd401da62018-05-03 10:40:30 -0400276 void* dataPtr = storage.get();
277 if (addPad) {
278 sk_bzero(dataPtr, size);
279 dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
280 }
281 if (!get_packed_glyph_image(cache, skGlyph, glyph->width(), glyph->height(),
282 rowBytes, expectedMaskFormat,
Timothy Liang91e260f2018-06-15 13:28:35 -0400283 dataPtr, glyphCache->getMasks())) {
Jim Van Verthd401da62018-05-03 10:40:30 -0400284 return GrDrawOpAtlas::ErrorCode::kError;
joshualitt7c3a2f82015-03-31 13:32:05 -0700285 }
286
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500287 GrDrawOpAtlas::ErrorCode result = fullAtlasManager->addToAtlas(
288 resourceProvider, glyphCache, this,
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500289 &glyph->fID, target, expectedMaskFormat,
Jim Van Verthcf838c72018-03-05 14:40:36 -0500290 width, height,
Robert Phillipsc4039ea2018-03-01 11:36:45 -0500291 storage.get(), &glyph->fAtlasLocation);
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500292 if (GrDrawOpAtlas::ErrorCode::kSucceeded == result) {
Jim Van Verthcf838c72018-03-05 14:40:36 -0500293 if (addPad) {
294 glyph->fAtlasLocation.fX += 1;
295 glyph->fAtlasLocation.fY += 1;
296 }
Brian Salomon2ee084e2016-12-16 18:59:19 -0500297 SkASSERT(GrDrawOpAtlas::kInvalidAtlasID != glyph->fID);
joshualitt7c3a2f82015-03-31 13:32:05 -0700298 fAtlasedGlyphs++;
299 }
Robert Phillipsd2e9f762018-03-07 11:54:37 -0500300 return result;
joshualitt7c3a2f82015-03-31 13:32:05 -0700301}