• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 y = 0; y < height; ++y) {
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 x = 7; x >= 0 && rowWritesLeft; --x, --rowWritesLeft) {
51                 *d++ = (mask & (1 << x)) ? (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/GrSurfaceProxy.h"
212 #include "src/gpu/GrTextureProxy.h"
213 #include "src/gpu/SurfaceContext.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 = dContext->priv().makeSC(std::move(view),
238                                             {colorType, kUnknown_SkAlphaType, nullptr});
239     if (!sContext || !sContext->asTextureProxy()) {
240         return false;
241     }
242 
243     bool result = sContext->readPixels(dContext, bm.pixmap(), {0, 0});
244     if (!result) {
245         SkDebugf("------ failed to read pixels for %s\n", filename);
246         return false;
247     }
248 
249     // remove any previous version of this file
250     remove(filename);
251 
252     SkFILEWStream file(filename);
253     if (!file.isValid()) {
254         SkDebugf("------ failed to create file: %s\n", filename);
255         remove(filename);   // remove any partial file
256         return false;
257     }
258 
259     if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
260         SkDebugf("------ failed to encode %s\n", filename);
261         remove(filename);   // remove any partial file
262         return false;
263     }
264 
265     return true;
266 }
267 
dump(GrDirectContext * context) const268 void GrAtlasManager::dump(GrDirectContext* context) const {
269     static int gDumpCount = 0;
270     for (int i = 0; i < kMaskFormatCount; ++i) {
271         if (fAtlases[i]) {
272             const GrSurfaceProxyView* views = fAtlases[i]->getViews();
273             for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
274                 SkASSERT(views[pageIdx].proxy());
275                 SkString filename;
276 #ifdef SK_BUILD_FOR_ANDROID
277                 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
278 #else
279                 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
280 #endif
281                 auto ct = GrMaskFormatToColorType(AtlasIndexToMaskFormat(i));
282                 save_pixels(context, views[pageIdx], ct, filename.c_str());
283             }
284         }
285     }
286     ++gDumpCount;
287 }
288 #endif
289 
setAtlasDimensionsToMinimum_ForTesting()290 void GrAtlasManager::setAtlasDimensionsToMinimum_ForTesting() {
291     // Delete any old atlases.
292     // This should be safe to do as long as we are not in the middle of a flush.
293     for (int i = 0; i < kMaskFormatCount; i++) {
294         fAtlases[i] = nullptr;
295     }
296 
297     // Set all the atlas sizes to 1x1 plot each.
298     new (&fAtlasConfig) GrDrawOpAtlasConfig{};
299 }
300 
initAtlas(GrMaskFormat format)301 bool GrAtlasManager::initAtlas(GrMaskFormat format) {
302     int index = MaskFormatToAtlasIndex(format);
303     if (fAtlases[index] == nullptr) {
304         GrColorType grColorType = GrMaskFormatToColorType(format);
305 #ifdef SK_ENABLE_SMALL_PAGE
306         int pageNum = 4; // The maximum number of texture pages in the original skia code is 4
307         if ((format == kA8_GrMaskFormat) && (fAtlasConfig.getARGBDimensions().width() > 512)) {
308             // reset fAtlasConfig to suit small page.
309             pageNum = fAtlasConfig.resetAsSmallPage();
310         }
311 #endif
312         SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
313         SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
314 
315         const GrBackendFormat backendFormat =
316                 fCaps->getDefaultBackendFormat(grColorType, GrRenderable::kNo);
317 
318         fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, backendFormat, grColorType,
319                                               atlasDimensions.width(), atlasDimensions.height(),
320                                               plotDimensions.width(), plotDimensions.height(),
321                                               this, fAllowMultitexturing,
322 #ifdef SK_ENABLE_SMALL_PAGE
323                                               pageNum,
324 #endif
325                                               nullptr);
326         if (!fAtlases[index]) {
327             return false;
328         }
329     }
330     return true;
331 }
332