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
15GrRestrictedAtlasManager::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
59GrRestrictedAtlasManager::~GrRestrictedAtlasManager() {
60}
61
62static 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//////////////////////////////////////////////////////////////////////////////////////////////////
77GrAtlasManager::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
85void GrAtlasManager::freeAll() {
86    for (int i = 0; i < kMaskFormatCount; ++i) {
87        fAtlases[i] = nullptr;
88    }
89}
90
91bool 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
97bool 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
107void 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  */
130static 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
173void 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
195void 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
204bool 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