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
16GrAtlasManager::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
24GrAtlasManager::~GrAtlasManager() = default;
25
26void GrAtlasManager::freeAll() {
27 for (int i = 0; i < kMaskFormatCount; ++i) {
28 fAtlases[i] = nullptr;
29 }
30}
31
32bool GrAtlasManager::hasGlyph(GrMaskFormat format, GrGlyph* glyph) {
33 SkASSERT(glyph);
34 return this->getAtlas(format)->hasID(glyph->fAtlasLocator.plotLocator());
35}
36
37template <typename INT_TYPE>
38static 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
59static 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.
141GrDrawOpAtlas::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 grGlyph->fAtlasLocator.insetSrc(srcPadding);
183
184 return errorCode;
185}
186
187// add to texture atlas that matches this format
188GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider,
189 GrDeferredUploadTarget* target,
190 GrMaskFormat format,
191 int width, int height, const void* image,
192 GrDrawOpAtlas::AtlasLocator* atlasLocator) {
193 return this->getAtlas(format)->addToAtlas(resourceProvider, target, width, height, image,
194 atlasLocator);
195}
196
197void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater,
198 GrMaskFormat format, GrGlyph* glyph,
199 GrDeferredUploadToken token) {
200 SkASSERT(glyph);
201 if (updater->add(glyph->fAtlasLocator)) {
202 this->getAtlas(format)->setLastUseToken(glyph->fAtlasLocator, token);
203 }
204}
205
206#ifdef SK_DEBUG
207#include "include/gpu/GrDirectContext.h"
208#include "src/gpu/GrContextPriv.h"
209#include "src/gpu/GrSurfaceContext.h"
210#include "src/gpu/GrSurfaceProxy.h"
211#include "src/gpu/GrTextureProxy.h"
212
213#include "include/core/SkBitmap.h"
214#include "include/core/SkImageEncoder.h"
215#include "include/core/SkStream.h"
216#include <stdio.h>
217
218/**
219 * Write the contents of the surface proxy to a PNG. Returns true if successful.
220 * @param filename Full path to desired file
221 */
222static bool save_pixels(GrDirectContext* dContext, GrSurfaceProxyView view, GrColorType colorType,
223 const char* filename) {
224 if (!view.proxy()) {
225 return false;
226 }
227
228 auto ii = SkImageInfo::Make(view.proxy()->dimensions(), kRGBA_8888_SkColorType,
229 kPremul_SkAlphaType);
230 SkBitmap bm;
231 if (!bm.tryAllocPixels(ii)) {
232 return false;
233 }
234
235 auto sContext = GrSurfaceContext::Make(dContext, std::move(view), colorType,
236 kUnknown_SkAlphaType, nullptr);
237 if (!sContext || !sContext->asTextureProxy()) {
238 return false;
239 }
240
241 bool result = sContext->readPixels(dContext, ii, bm.getPixels(), bm.rowBytes(), {0, 0});
242 if (!result) {
243 SkDebugf("------ failed to read pixels for %s\n", filename);
244 return false;
245 }
246
247 // remove any previous version of this file
248 remove(filename);
249
250 SkFILEWStream file(filename);
251 if (!file.isValid()) {
252 SkDebugf("------ failed to create file: %s\n", filename);
253 remove(filename); // remove any partial file
254 return false;
255 }
256
257 if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
258 SkDebugf("------ failed to encode %s\n", filename);
259 remove(filename); // remove any partial file
260 return false;
261 }
262
263 return true;
264}
265
266void GrAtlasManager::dump(GrDirectContext* context) const {
267 static int gDumpCount = 0;
268 for (int i = 0; i < kMaskFormatCount; ++i) {
269 if (fAtlases[i]) {
270 const GrSurfaceProxyView* views = fAtlases[i]->getViews();
271 for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
272 SkASSERT(views[pageIdx].proxy());
273 SkString filename;
274#ifdef SK_BUILD_FOR_ANDROID
275 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
276#else
277 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
278#endif
279 auto ct = GrMaskFormatToColorType(AtlasIndexToMaskFormat(i));
280 save_pixels(context, views[pageIdx], ct, filename.c_str());
281 }
282 }
283 }
284 ++gDumpCount;
285}
286#endif
287
288void GrAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
289 // Delete any old atlases.
290 // This should be safe to do as long as we are not in the middle of a flush.
291 for (int i = 0; i < kMaskFormatCount; i++) {
292 fAtlases[i] = nullptr;
293 }
294
295 // Set all the atlas sizes to 1x1 plot each.
296 new (&fAtlasConfig) GrDrawOpAtlasConfig{};
297}
298
299bool GrAtlasManager::initAtlas(GrMaskFormat format) {
300 int index = MaskFormatToAtlasIndex(format);
301 if (fAtlases[index] == nullptr) {
302 GrColorType grColorType = GrMaskFormatToColorType(format);
303 SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
304 SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
305
306 const GrBackendFormat format = fCaps->getDefaultBackendFormat(grColorType,
307 GrRenderable::kNo);
308
309 fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, format, grColorType,
310 atlasDimensions.width(), atlasDimensions.height(),
311 plotDimensions.width(), plotDimensions.height(),
312 this, fAllowMultitexturing, nullptr);
313 if (!fAtlases[index]) {
314 return false;
315 }
316 }
317 return true;
318}
319