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/gpu/GrGlyph.h"
11 #include "src/gpu/GrImageInfo.h"
12 #include "src/gpu/text/GrStrikeCache.h"
13 
GrAtlasManager(GrProxyProvider * proxyProvider,GrStrikeCache * glyphCache,size_t maxTextureBytes,GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)14 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, GrStrikeCache* glyphCache,
15                                size_t maxTextureBytes,
16                                GrDrawOpAtlas::AllowMultitexturing allowMultitexturing)
17             : fAllowMultitexturing{allowMultitexturing}
18             , fProxyProvider{proxyProvider}
19             , fCaps{fProxyProvider->refCaps()}
20             , fGlyphCache{glyphCache}
21             , fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
22 
23 GrAtlasManager::~GrAtlasManager() = default;
24 
mask_format_to_gr_color_type(GrMaskFormat format)25 static GrColorType mask_format_to_gr_color_type(GrMaskFormat format) {
26     switch (format) {
27         case kA8_GrMaskFormat:
28             return GrColorType::kAlpha_8;
29         case kA565_GrMaskFormat:
30             return GrColorType::kBGR_565;
31         case kARGB_GrMaskFormat:
32             return GrColorType::kRGBA_8888;
33         default:
34             SkDEBUGFAIL("unsupported GrMaskFormat");
35             return GrColorType::kAlpha_8;
36     }
37 }
38 
freeAll()39 void GrAtlasManager::freeAll() {
40     for (int i = 0; i < kMaskFormatCount; ++i) {
41         fAtlases[i] = nullptr;
42     }
43 }
44 
hasGlyph(GrGlyph * glyph)45 bool GrAtlasManager::hasGlyph(GrGlyph* glyph) {
46     SkASSERT(glyph);
47     return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID);
48 }
49 
50 // add to texture atlas that matches this format
addToAtlas(GrResourceProvider * resourceProvider,GrStrikeCache * glyphCache,GrTextStrike * strike,GrDrawOpAtlas::AtlasID * id,GrDeferredUploadTarget * target,GrMaskFormat format,int width,int height,const void * image,SkIPoint16 * loc)51 GrDrawOpAtlas::ErrorCode GrAtlasManager::addToAtlas(
52                                 GrResourceProvider* resourceProvider,
53                                 GrStrikeCache* glyphCache,
54                                 GrTextStrike* strike, GrDrawOpAtlas::AtlasID* id,
55                                 GrDeferredUploadTarget* target, GrMaskFormat format,
56                                 int width, int height, const void* image, SkIPoint16* loc) {
57     glyphCache->setStrikeToPreserve(strike);
58     return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, width, height,
59                                               image, loc);
60 }
61 
addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater * updater,GrGlyph * glyph,GrDeferredUploadToken token)62 void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater,
63                                                   GrGlyph* glyph,
64                                                   GrDeferredUploadToken token) {
65     SkASSERT(glyph);
66     if (updater->add(glyph->fID)) {
67         this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token);
68     }
69 }
70 
71 #ifdef SK_DEBUG
72 #include "src/gpu/GrContextPriv.h"
73 #include "src/gpu/GrSurfaceContext.h"
74 #include "src/gpu/GrSurfaceProxy.h"
75 #include "src/gpu/GrTextureProxy.h"
76 
77 #include "include/core/SkBitmap.h"
78 #include "include/core/SkImageEncoder.h"
79 #include "include/core/SkStream.h"
80 #include <stdio.h>
81 
82 /**
83   * Write the contents of the surface proxy to a PNG. Returns true if successful.
84   * @param filename      Full path to desired file
85   */
save_pixels(GrContext * context,GrSurfaceProxy * sProxy,GrColorType colorType,const char * filename)86 static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, GrColorType colorType,
87                         const char* filename) {
88     if (!sProxy) {
89         return false;
90     }
91 
92     SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(),
93                                        kRGBA_8888_SkColorType, kPremul_SkAlphaType);
94     SkBitmap bm;
95     if (!bm.tryAllocPixels(ii)) {
96         return false;
97     }
98 
99     auto sContext = context->priv().makeWrappedSurfaceContext(sk_ref_sp(sProxy), colorType,
100                                                               kUnknown_SkAlphaType);
101     if (!sContext || !sContext->asTextureProxy()) {
102         return false;
103     }
104 
105     bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), {0, 0});
106     if (!result) {
107         SkDebugf("------ failed to read pixels for %s\n", filename);
108         return false;
109     }
110 
111     // remove any previous version of this file
112     remove(filename);
113 
114     SkFILEWStream file(filename);
115     if (!file.isValid()) {
116         SkDebugf("------ failed to create file: %s\n", filename);
117         remove(filename);   // remove any partial file
118         return false;
119     }
120 
121     if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) {
122         SkDebugf("------ failed to encode %s\n", filename);
123         remove(filename);   // remove any partial file
124         return false;
125     }
126 
127     return true;
128 }
129 
dump(GrContext * context) const130 void GrAtlasManager::dump(GrContext* context) const {
131     static int gDumpCount = 0;
132     for (int i = 0; i < kMaskFormatCount; ++i) {
133         if (fAtlases[i]) {
134             const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies();
135             for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) {
136                 SkASSERT(proxies[pageIdx]);
137                 SkString filename;
138 #ifdef SK_BUILD_FOR_ANDROID
139                 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
140 #else
141                 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx);
142 #endif
143                 auto ct = mask_format_to_gr_color_type(AtlasIndexToMaskFormat(i));
144                 save_pixels(context, proxies[pageIdx].get(), ct, filename.c_str());
145             }
146         }
147     }
148     ++gDumpCount;
149 }
150 #endif
151 
setAtlasSizesToMinimum_ForTesting()152 void GrAtlasManager::setAtlasSizesToMinimum_ForTesting() {
153     // Delete any old atlases.
154     // This should be safe to do as long as we are not in the middle of a flush.
155     for (int i = 0; i < kMaskFormatCount; i++) {
156         fAtlases[i] = nullptr;
157     }
158 
159     // Set all the atlas sizes to 1x1 plot each.
160     new (&fAtlasConfig) GrDrawOpAtlasConfig{};
161 }
162 
initAtlas(GrMaskFormat format)163 bool GrAtlasManager::initAtlas(GrMaskFormat format) {
164     int index = MaskFormatToAtlasIndex(format);
165     if (fAtlases[index] == nullptr) {
166         GrColorType grColorType = mask_format_to_gr_color_type(format);
167         SkISize atlasDimensions = fAtlasConfig.atlasDimensions(format);
168         SkISize plotDimensions = fAtlasConfig.plotDimensions(format);
169 
170         const GrBackendFormat format = fCaps->getDefaultBackendFormat(grColorType,
171                                                                       GrRenderable::kNo);
172 
173         fAtlases[index] = GrDrawOpAtlas::Make(
174                 fProxyProvider, format, grColorType,
175                 atlasDimensions.width(), atlasDimensions.height(),
176                 plotDimensions.width(), plotDimensions.height(),
177                 fAllowMultitexturing, &GrStrikeCache::HandleEviction, fGlyphCache);
178         if (!fAtlases[index]) {
179             return false;
180         }
181     }
182     return true;
183 }
184