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