1 /*
2 * Copyright 2018 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/text/GrAtlasManager.h"
9
10 #include "src/codec/SkMasks.h"
11 #include "src/core/SkAutoMalloc.h"
12 #include "src/gpu/GrGlyph.h"
13 #include "src/gpu/GrImageInfo.h"
14 #include "src/gpu/text/GrStrikeCache.h"
15
GrAtlasManager(GrProxyProvider * proxyProvider,size_t maxTextureBytes,GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)16 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider,
17 size_t maxTextureBytes,
18 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
19 : fAllowMultitexturing{allowMultitexturing}
20 , fProxyProvider{proxyProvider}
21 , fCaps{fProxyProvider->refCaps()}
22 , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
23
24 GrAtlasManager::~GrAtlasManager() = default;
25
freeAll()26 void GrAtlasManager::freeAll() {
27 for (int i = 0; i < kMaskFormatCount; ++i) {
28 fAtlases[i] = nullptr;
29 }
30 }
31
hasGlyph(GrMaskFormat format,GrGlyph * glyph)32 bool GrAtlasManager::hasGlyph(GrMaskFormat format, GrGlyph* glyph) {
33 SkASSERT(glyph);
34 return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
35 }
36
37 template <typename INT_TYPE>
expand_bits(INT_TYPE * dst,const uint8_t * src,int width,int height,int dstRowBytes,int srcRowBytes)38 static void expand_bits(INT_TYPE* dst,
39 const uint8_t* src,
40 int width,
41 int height,
42 int dstRowBytes,
43 int srcRowBytes) {
44 for (int i = 0; i < height; ++i) {
45 int rowWritesLeft = width;
46 const uint8_t* s = src;
47 INT_TYPE* d = dst;
48 while (rowWritesLeft > 0) {
49 unsigned mask = *s++;
50 for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
51 *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
52 }
53 }
54 dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
55 src += srcRowBytes;
56 }
57 }
58
get_packed_glyph_image(const SkGlyph & glyph,int dstRB,GrMaskFormat expectedMaskFormat,void * dst)59 static void get_packed_glyph_image(
60 const SkGlyph& glyph, int dstRB, GrMaskFormat expectedMaskFormat, void* dst) {
61 const int width = glyph.width();
62 const int height = glyph.height();
63 const void* src = glyph.image();
64 SkASSERT(src != nullptr);
65
66 GrMaskFormat grMaskFormat = GrGlyph::FormatFromSkGlyph(glyph.maskFormat());
67 if (grMaskFormat == expectedMaskFormat) {
68 int srcRB = glyph.rowBytes();
69 // Notice this comparison is with the glyphs raw mask format, and not its GrMaskFormat.
70 if (glyph.maskFormat() != SkMask::kBW_Format) {
71 if (srcRB != dstRB) {
72 const int bbp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
73 for (int y = 0; y < height; y++) {
74 memcpy(dst, src, width * bbp);
75 src = (const char*) src + srcRB;
76 dst = (char*) dst + dstRB;
77 }
78 } else {
79 memcpy(dst, src, dstRB * height);
80 }
81 } else {
82 // Handle 8-bit format by expanding the mask to the expected format.
83 const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
84 switch (expectedMaskFormat) {
85 case kA8_GrMaskFormat: {
86 uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
87 expand_bits(bytes, bits, width, height, dstRB, srcRB);
88 break;
89 }
90 case kA565_GrMaskFormat: {
91 uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
92 expand_bits(rgb565, bits, width, height, dstRB, srcRB);
93 break;
94 }
95 default:
96 SK_ABORT("Invalid GrMaskFormat");
97 }
98 }
99 } else if (grMaskFormat == kA565_GrMaskFormat && expectedMaskFormat == kARGB_GrMaskFormat) {
100 // Convert if the glyph uses a 565 mask format since it is using LCD text rendering
101 // but the expected format is 8888 (will happen on macOS with Metal since that
102 // combination does not support 565).
103 static constexpr SkMasks masks{
104 {0b1111'1000'0000'0000, 11, 5}, // Red
105 {0b0000'0111'1110'0000, 5, 6}, // Green
106 {0b0000'0000'0001'1111, 0, 5}, // Blue
107 {0, 0, 0} // Alpha
108 };
109 constexpr int a565Bpp = GrMaskFormatBytesPerPixel(kA565_GrMaskFormat);
110 constexpr int argbBpp = GrMaskFormatBytesPerPixel(kARGB_GrMaskFormat);
111 for (int y = 0; y < height; y++) {
112 for (int x = 0; x < width; x++) {
113 uint16_t color565 = 0;
114 memcpy(&color565, src, a565Bpp);
115 uint32_t colorRGBA = GrColorPackRGBA(masks.getRed(color565),
116 masks.getGreen(color565),
117 masks.getBlue(color565),
118 0xFF);
119 memcpy(dst, &colorRGBA, argbBpp);
120 src = (char*)src + a565Bpp;
121 dst = (char*)dst + argbBpp;
122 }
123 }
124 } else {
125 // crbug:510931
126 // Retrieving the image from the cache can actually change the mask format. This case is
127 // very uncommon so for now we just draw a clear box for these glyphs.
128 const int bpp = GrMaskFormatBytesPerPixel(expectedMaskFormat);
129 for (int y = 0; y < height; y++) {
130 sk_bzero(dst, width * bpp);
131 dst = (char*)dst + dstRB;
132 }
133 }
134 }
135
136 // returns true if glyph successfully added to texture atlas, false otherwise. If the glyph's
137 // mask format has changed, then addGlyphToAtlas will draw a clear box. This will almost never
138 // happen.
139 // TODO we can handle some of these cases if we really want to, but the long term solution is to
140 // get the actual glyph image itself when we get the glyph metrics.
addGlyphToAtlas(const SkGlyph & skGlyph,GrGlyph * grGlyph,int srcPadding,GrResourceProvider * resourceProvider,GrDeferredUploadTarget * uploadTarget,bool bilerpPadding)141 GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
142 GrGlyph* grGlyph,
143 int srcPadding,
144 GrResourceProvider* resourceProvider,
145 GrDeferredUploadTarget* uploadTarget,
146 bool bilerpPadding) {
147 if (skGlyph.image() == nullptr) {
148 return GrDrawOpAtlas::ErrorCode::kError;
149 }
150 SkASSERT(grGlyph != nullptr);
151
152 GrMaskFormat glyphFormat = GrGlyph::FormatFromSkGlyph(skGlyph.maskFormat());
153 GrMaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
154 int bytesPerPixel = GrMaskFormatBytesPerPixel(expectedMaskFormat);
155
156 // Add 1 pixel padding around grGlyph if needed.
157 int padding = bilerpPadding ? 1 : 0;
158 const int width = skGlyph.width() + 2*padding;
159 const int height = skGlyph.height() + 2*padding;
160 int rowBytes = width * bytesPerPixel;
161 size_t size = height * rowBytes;
162
163 // Temporary storage for normalizing grGlyph image.
164 SkAutoSMalloc<1024> storage(size);
165 void* dataPtr = storage.get();
166 if (padding > 0) {
167 sk_bzero(dataPtr, size);
168 // Advance in one row and one column.
169 dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
170 }
171
172 get_packed_glyph_image(skGlyph, rowBytes, expectedMaskFormat, dataPtr);
173
174 auto errorCode = this->addToAtlas(resourceProvider,
175 uploadTarget,
176 expectedMaskFormat,
177 width,
178 height,
179 storage.get(),
180 &grGlyph->fAtlasLocator);
181
182 if (errorCode == GrDrawOpAtlas::ErrorCode::kSucceeded) {
183 grGlyph->fAtlasLocator.insetSrc(srcPadding);
184 }
185
186 return errorCode;
187 }
188
189 // add to texture atlas that matches this format
addToAtlas(GrResourceProvider * resourceProvider,GrDeferredUploadTarget * target,GrMaskFormat format,int width,int height,const void * image,GrDrawOpAtlas::AtlasLocator * atlasLocator)190 GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider,
191 GrDeferredUploadTarget* target,
192 GrMaskFormat format,
193 int width, int height, const void* image,
194 GrDrawOpAtlas::AtlasLocator* atlasLocator) {
195 return this->getAtlas(format)->addToAtlas(resourceProvider, target, width, height, image,
196 atlasLocator);
197 }
198
addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater * updater,GrMaskFormat format,GrGlyph * glyph,GrDeferredUploadToken token)199 void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater,
200 GrMaskFormat format, GrGlyph* glyph,
201 GrDeferredUploadToken token) {
202 SkASSERT(glyph);
203 if (updater->add(glyph->fAtlasLocator)) {
204 this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
205 }
206 }
207
208 #ifdef SK_DEBUG
209 #include "include/gpu/GrDirectContext.h"
210 #include "src/gpu/GrDirectContextPriv.h"
211 #include "src/gpu/GrSurfaceContext.h"
212 #include "src/gpu/GrSurfaceProxy.h"
213 #include "src/gpu/GrTextureProxy.h"
214
215 #include "include/core/SkBitmap.h"
216 #include "include/core/SkImageEncoder.h"
217 #include "include/core/SkStream.h"
218 #include <stdio.h>
219
220 /**
221 * Write the contents of the surface proxy to a PNG. Returns true if successful.
222 * @param filename Full path to desired file
223 */
save_pixels(GrDirectContext * dContext,GrSurfaceProxyView view,GrColorType colorType,const char * filename)224 static bool save_pixels(GrDirectContext* dContext, GrSurfaceProxyView view, GrColorType colorType,
225 const char* filename) {
226 if (!view.proxy()) {
227 return false;
228 }
229
230 auto ii = SkImageInfo::Make(view.proxy()->dimensions(), kRGBA_8888_SkColorType,
231 kPremul_SkAlphaType);
232 SkBitmap bm;
233 if (!bm.tryAllocPixels(ii)) {
234 return false;
235 }
236
237 auto sContext = GrSurfaceContext::Make(dContext,
238 std::move(view),
239 {colorType, kUnknown_SkAlphaType, nullptr});
240 if (!sContext || !sContext->asTextureProxy()) {
241 return false;
242 }
243
244 bool result = sContext->readPixels(dContext, bm.pixmap(), {0, 0});
245 if (!result) {
246 SkDebugf("------ failed to read pixels for %s\n", filename);
247 return false;
248 }
249
250 // remove any previous version of this file
251 remove(filename);
252
253 SkFILEWStream file(filename);
254 if (!file.isValid()) {
255 SkDebugf("------ failed to create file: %s\n", filename);
256 remove(filename); // remove any partial file
257 return false;
258 }
259
260 if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
261 SkDebugf("------ failed to encode %s\n", filename);
262 remove(filename); // remove any partial file
263 return false;
264 }
265
266 return true;
267 }
268
dump(GrDirectContext * context) const269 void GrAtlasManager::dump(GrDirectContext* context) const {
270 static int gDumpCount = 0;
271 for (int i = 0; i < kMaskFormatCount; ++i) {
272 if (fAtlases[i]) {
273 const GrSurfaceProxyView* views = fAtlases[i]->getViews();
274 for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
275 SkASSERT(views[pageIdx].proxy());
276 SkString filename;
277 #ifdef SK_BUILD_FOR_ANDROID
278 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
279 #else
280 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
281 #endif
282 auto ct = GrMaskFormatToColorType(AtlasIndexToMaskFormat(i));
283 save_pixels(context, views[pageIdx], ct, filename.c_str());
284 }
285 }
286 }
287 ++gDumpCount;
288 }
289 #endif
290
setAtlasDimensionsToMinimum_ForTesting()291 void GrAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
292 // Delete any old atlases.
293 // This should be safe to do as long as we are not in the middle of a flush.
294 for (int i = 0; i < kMaskFormatCount; i++) {
295 fAtlases[i] = nullptr;
296 }
297
298 // Set all the atlas sizes to 1x1 plot each.
299 new (&fAtlasConfig) GrDrawOpAtlasConfig{};
300 }
301
initAtlas(GrMaskFormat format)302 bool GrAtlasManager::initAtlas(GrMaskFormat format) {
303 int index = MaskFormatToAtlasIndex(format);
304 if (fAtlases[index] == nullptr) {
305 GrColorType grColorType = GrMaskFormatToColorType(format);
306 SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
307 SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
308
309 const GrBackendFormat format = fCaps->getDefaultBackendFormat(grColorType,
310 GrRenderable::kNo);
311
312 fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, format, grColorType,
313 atlasDimensions.width(), atlasDimensions.height(),
314 plotDimensions.width(), plotDimensions.height(),
315 this, fAllowMultitexturing, nullptr);
316 if (!fAtlases[index]) {
317 return false;
318 }
319 }
320 return true;
321 }
322