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 "GrCaps.h" 11 #include "GrGlyph.h" 12 #include "GrGlyphCache.h" 13 #include "GrProxyProvider.h" 14 15 GrRestrictedAtlasManager::GrRestrictedAtlasManager( 16 sk_sp<const GrCaps> caps, 17 float maxTextureBytes, 18 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) 19 : fCaps(std::move(caps)) 20 , fAllowMultitexturing(allowMultitexturing) { 21 // Calculate RGBA size. Must be between 512 x 256 and MaxTextureSize x MaxTextureSize / 2 22 int log2MaxTextureSize = SkPrevLog2(fCaps->maxTextureSize()); 23 int log2MaxDim = 9; 24 for (; log2MaxDim <= log2MaxTextureSize; ++log2MaxDim) { 25 int maxDim = 1 << log2MaxDim; 26 int minDim = 1 << (log2MaxDim - 1); 27 28 if (maxDim * minDim * 4 >= maxTextureBytes) break; 29 } 30 31 int log2MinDim = log2MaxDim - 1; 32 int maxDim = 1 << log2MaxDim; 33 int minDim = 1 << log2MinDim; 34 // Plots are either 256 or 512. 35 int maxPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 2))); 36 int minPlot = SkTMin(512, SkTMax(256, 1 << (log2MaxDim - 3))); 37 38 // Setup default atlas configs. The A8 atlas uses maxDim for both width and height, as the A8 39 // format is already very compact. 40 fAtlasConfigs[kA8_GrMaskFormat].fWidth = maxDim; 41 fAtlasConfigs[kA8_GrMaskFormat].fHeight = maxDim; 42 fAtlasConfigs[kA8_GrMaskFormat].fPlotWidth = maxPlot; 43 fAtlasConfigs[kA8_GrMaskFormat].fPlotHeight = minPlot; 44 45 // A565 and ARGB use maxDim x minDim. 46 fAtlasConfigs[kA565_GrMaskFormat].fWidth = minDim; 47 fAtlasConfigs[kA565_GrMaskFormat].fHeight = maxDim; 48 fAtlasConfigs[kA565_GrMaskFormat].fPlotWidth = minPlot; 49 fAtlasConfigs[kA565_GrMaskFormat].fPlotHeight = minPlot; 50 51 fAtlasConfigs[kARGB_GrMaskFormat].fWidth = minDim; 52 fAtlasConfigs[kARGB_GrMaskFormat].fHeight = maxDim; 53 fAtlasConfigs[kARGB_GrMaskFormat].fPlotWidth = minPlot; 54 fAtlasConfigs[kARGB_GrMaskFormat].fPlotHeight = minPlot; 55 56 fGlyphSizeLimit = minPlot; 57 } 58 59 GrRestrictedAtlasManager::~GrRestrictedAtlasManager() { 60 } 61 62 static GrPixelConfig mask_format_to_pixel_config(GrMaskFormat format, const GrCaps& caps) { 63 switch (format) { 64 case kA8_GrMaskFormat: 65 return kAlpha_8_GrPixelConfig; 66 case kA565_GrMaskFormat: 67 return kRGB_565_GrPixelConfig; 68 case kARGB_GrMaskFormat: 69 return caps.srgbSupport() ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig; 70 default: 71 SkDEBUGFAIL("unsupported GrMaskFormat"); 72 return kAlpha_8_GrPixelConfig; 73 } 74 } 75 76 ////////////////////////////////////////////////////////////////////////////////////////////////// 77 GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider, GrGlyphCache* glyphCache, 78 float maxTextureBytes, 79 GrDrawOpAtlas::AllowMultitexturing allowMultitexturing) 80 : INHERITED(proxyProvider->refCaps(), maxTextureBytes, allowMultitexturing) 81 , fProxyProvider(proxyProvider) 82 , fGlyphCache(glyphCache) { 83 } 84 85 void GrAtlasManager::freeAll() { 86 for (int i = 0; i < kMaskFormatCount; ++i) { 87 fAtlases[i] = nullptr; 88 } 89 } 90 91 bool GrAtlasManager::hasGlyph(GrGlyph* glyph) { 92 SkASSERT(glyph); 93 return this->getAtlas(glyph->fMaskFormat)->hasID(glyph->fID); 94 } 95 96 // add to texture atlas that matches this format 97 bool GrAtlasManager::addToAtlas(GrResourceProvider* resourceProvider, 98 GrGlyphCache* glyphCache, 99 GrTextStrike* strike, GrDrawOpAtlas::AtlasID* id, 100 GrDeferredUploadTarget* target, GrMaskFormat format, 101 int width, int height, const void* image, SkIPoint16* loc) { 102 glyphCache->setStrikeToPreserve(strike); 103 return this->getAtlas(format)->addToAtlas(resourceProvider, id, target, width, height, 104 image, loc); 105 } 106 107 void GrAtlasManager::addGlyphToBulkAndSetUseToken(GrDrawOpAtlas::BulkUseTokenUpdater* updater, 108 GrGlyph* glyph, 109 GrDeferredUploadToken token) { 110 SkASSERT(glyph); 111 updater->add(glyph->fID); 112 this->getAtlas(glyph->fMaskFormat)->setLastUseToken(glyph->fID, token); 113 } 114 115 #ifdef SK_DEBUG 116 #include "GrContextPriv.h" 117 #include "GrSurfaceProxy.h" 118 #include "GrSurfaceContext.h" 119 #include "GrTextureProxy.h" 120 121 #include "SkBitmap.h" 122 #include "SkImageEncoder.h" 123 #include "SkStream.h" 124 #include <stdio.h> 125 126 /** 127 * Write the contents of the surface proxy to a PNG. Returns true if successful. 128 * @param filename Full path to desired file 129 */ 130 static bool save_pixels(GrContext* context, GrSurfaceProxy* sProxy, const char* filename) { 131 if (!sProxy) { 132 return false; 133 } 134 135 SkImageInfo ii = SkImageInfo::Make(sProxy->width(), sProxy->height(), 136 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 137 SkBitmap bm; 138 if (!bm.tryAllocPixels(ii)) { 139 return false; 140 } 141 142 sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext( 143 sk_ref_sp(sProxy))); 144 if (!sContext || !sContext->asTextureProxy()) { 145 return false; 146 } 147 148 bool result = sContext->readPixels(ii, bm.getPixels(), bm.rowBytes(), 0, 0); 149 if (!result) { 150 SkDebugf("------ failed to read pixels for %s\n", filename); 151 return false; 152 } 153 154 // remove any previous version of this file 155 remove(filename); 156 157 SkFILEWStream file(filename); 158 if (!file.isValid()) { 159 SkDebugf("------ failed to create file: %s\n", filename); 160 remove(filename); // remove any partial file 161 return false; 162 } 163 164 if (!SkEncodeImage(&file, bm, SkEncodedImageFormat::kPNG, 100)) { 165 SkDebugf("------ failed to encode %s\n", filename); 166 remove(filename); // remove any partial file 167 return false; 168 } 169 170 return true; 171 } 172 173 void GrAtlasManager::dump(GrContext* context) const { 174 static int gDumpCount = 0; 175 for (int i = 0; i < kMaskFormatCount; ++i) { 176 if (fAtlases[i]) { 177 const sk_sp<GrTextureProxy>* proxies = fAtlases[i]->getProxies(); 178 for (uint32_t pageIdx = 0; pageIdx < fAtlases[i]->numActivePages(); ++pageIdx) { 179 SkASSERT(proxies[pageIdx]); 180 SkString filename; 181 #ifdef SK_BUILD_FOR_ANDROID 182 filename.printf("/sdcard/fontcache_%d%d%d.png", gDumpCount, i, pageIdx); 183 #else 184 filename.printf("fontcache_%d%d%d.png", gDumpCount, i, pageIdx); 185 #endif 186 187 save_pixels(context, proxies[pageIdx].get(), filename.c_str()); 188 } 189 } 190 } 191 ++gDumpCount; 192 } 193 #endif 194 195 void GrAtlasManager::setAtlasSizes_ForTesting(const GrDrawOpAtlasConfig configs[3]) { 196 // Delete any old atlases. 197 // This should be safe to do as long as we are not in the middle of a flush. 198 for (int i = 0; i < kMaskFormatCount; i++) { 199 fAtlases[i] = nullptr; 200 } 201 memcpy(fAtlasConfigs, configs, sizeof(fAtlasConfigs)); 202 } 203 204 bool GrAtlasManager::initAtlas(GrMaskFormat format) { 205 int index = MaskFormatToAtlasIndex(format); 206 if (!fAtlases[index]) { 207 GrPixelConfig config = mask_format_to_pixel_config(format, *fCaps); 208 int width = fAtlasConfigs[index].fWidth; 209 int height = fAtlasConfigs[index].fHeight; 210 int numPlotsX = fAtlasConfigs[index].numPlotsX(); 211 int numPlotsY = fAtlasConfigs[index].numPlotsY(); 212 213 fAtlases[index] = GrDrawOpAtlas::Make(fProxyProvider, config, width, height, 214 numPlotsX, numPlotsY, fAllowMultitexturing, 215 &GrGlyphCache::HandleEviction, 216 fGlyphCache); 217 if (!fAtlases[index]) { 218 return false; 219 } 220 } 221 return true; 222 } 223