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