Home | History | Annotate | Download | only in text
      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