1 /*
2 * Copyright 2015 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 <cstdio>
9 #include <cstdlib>
10 #include <sstream>
11 #include <string>
12
13 #include "src/core/SkAutoPixmapStorage.h"
14 #include "src/core/SkMipMap.h"
15 #include "src/core/SkUtils.h"
16 #include "tools/flags/CommandLineFlags.h"
17
18 #include "tools/fiddle/fiddle_main.h"
19
20 static DEFINE_double(duration, 1.0,
21 "The total duration, in seconds, of the animation we are drawing.");
22 static DEFINE_double(frame, 1.0,
23 "A double value in [0, 1] that specifies the point in animation to draw.");
24
25 #include "include/gpu/GrBackendSurface.h"
26 #include "src/gpu/GrContextPriv.h"
27 #include "src/gpu/GrGpu.h"
28 #include "src/gpu/GrRenderTarget.h"
29 #include "tools/gpu/gl/GLTestContext.h"
30
31 // Globals externed in fiddle_main.h
32 sk_sp<GrTexture> backingTexture; // not externed
33 GrBackendTexture backEndTexture;
34
35 sk_sp<GrRenderTarget> backingRenderTarget; // not externed
36 GrBackendRenderTarget backEndRenderTarget;
37
38 sk_sp<GrTexture> backingTextureRenderTarget; // not externed
39 GrBackendTexture backEndTextureRenderTarget;
40
41 SkBitmap source;
42 sk_sp<SkImage> image;
43 double duration; // The total duration of the animation in seconds.
44 double frame; // A value in [0, 1] of where we are in the animation.
45
46 // Global used by the local impl of SkDebugf.
47 std::ostringstream gTextOutput;
48
49 // Global to record the GL driver info via create_grcontext().
50 std::ostringstream gGLDriverInfo;
51
SkDebugf(const char * fmt,...)52 void SkDebugf(const char * fmt, ...) {
53 va_list args;
54 va_start(args, fmt);
55 char formatbuffer[1024];
56 int n = vsnprintf(formatbuffer, sizeof(formatbuffer), fmt, args);
57 va_end(args);
58 if (n>=0 && n<=int(sizeof(formatbuffer))) {
59 gTextOutput.write(formatbuffer, n);
60 }
61 }
62
encode_to_base64(const void * data,size_t size,FILE * out)63 static void encode_to_base64(const void* data, size_t size, FILE* out) {
64 const uint8_t* input = reinterpret_cast<const uint8_t*>(data);
65 const uint8_t* end = &input[size];
66 static const char codes[] =
67 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
68 "abcdefghijklmnopqrstuvwxyz0123456789+/";
69 while (input != end) {
70 uint8_t b = (*input & 0xFC) >> 2;
71 fputc(codes[b], out);
72 b = (*input & 0x03) << 4;
73 ++input;
74 if (input == end) {
75 fputc(codes[b], out);
76 fputs("==", out);
77 return;
78 }
79 b |= (*input & 0xF0) >> 4;
80 fputc(codes[b], out);
81 b = (*input & 0x0F) << 2;
82 ++input;
83 if (input == end) {
84 fputc(codes[b], out);
85 fputc('=', out);
86 return;
87 }
88 b |= (*input & 0xC0) >> 6;
89 fputc(codes[b], out);
90 b = *input & 0x3F;
91 fputc(codes[b], out);
92 ++input;
93 }
94 }
95
96
dump_output(const void * data,size_t size,const char * name,bool last=true)97 static void dump_output(const void* data, size_t size,
98 const char* name, bool last = true) {
99 printf("\t\"%s\": \"", name);
100 encode_to_base64(data, size, stdout);
101 fputs(last ? "\"\n" : "\",\n", stdout);
102 }
103
dump_output(const sk_sp<SkData> & data,const char * name,bool last=true)104 static void dump_output(const sk_sp<SkData>& data,
105 const char* name, bool last = true) {
106 if (data) {
107 dump_output(data->data(), data->size(), name, last);
108 }
109 }
110
encode_snapshot(const sk_sp<SkSurface> & surface)111 static sk_sp<SkData> encode_snapshot(const sk_sp<SkSurface>& surface) {
112 sk_sp<SkImage> img(surface->makeImageSnapshot());
113 return img ? img->encodeToData() : nullptr;
114 }
115
prepare_canvas(SkCanvas * canvas)116 static SkCanvas* prepare_canvas(SkCanvas * canvas) {
117 canvas->clear(SK_ColorWHITE);
118 return canvas;
119 }
120
setup_backend_objects(GrContext * context,const SkBitmap & bm,const DrawOptions & options)121 static bool setup_backend_objects(GrContext* context,
122 const SkBitmap& bm,
123 const DrawOptions& options) {
124 if (!context) {
125 fputs("Context is null.\n", stderr);
126 return false;
127 }
128
129 auto resourceProvider = context->priv().resourceProvider();
130
131 // This config must match the SkColorType used in draw.cpp in the SkImage and Surface factories
132 auto format = resourceProvider->caps()->getDefaultBackendFormat(
133 SkColorTypeToGrColorType(kRGBA_8888_SkColorType), GrRenderable::kNo);
134 auto renderableFormat = resourceProvider->caps()->getDefaultBackendFormat(
135 SkColorTypeToGrColorType(kRGBA_8888_SkColorType), GrRenderable::kYes);
136
137 if (!bm.empty()) {
138 SkPixmap originalPixmap;
139 SkPixmap* pixmap = &originalPixmap;
140 if (!bm.peekPixels(&originalPixmap)) {
141 fputs("Unable to peekPixels.\n", stderr);
142 return false;
143 }
144
145 SkAutoPixmapStorage rgbaPixmap;
146 if (kN32_SkColorType != kRGBA_8888_SkColorType) {
147 if (!rgbaPixmap.tryAlloc(bm.info().makeColorType(kRGBA_8888_SkColorType))) {
148 fputs("Unable to alloc rgbaPixmap.\n", stderr);
149 return false;
150 }
151 if (!bm.readPixels(rgbaPixmap)) {
152 fputs("Unable to read rgbaPixmap.\n", stderr);
153 return false;
154 }
155 pixmap = &rgbaPixmap;
156 }
157 int mipLevelCount = GrMipMapped::kYes == options.fMipMapping
158 ? SkMipMap::ComputeLevelCount(bm.width(), bm.height())
159 : 1;
160 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
161
162 texels[0].fPixels = pixmap->addr();
163 texels[0].fRowBytes = pixmap->rowBytes();
164
165 for (int i = 1; i < mipLevelCount; i++) {
166 texels[i].fPixels = nullptr;
167 texels[i].fRowBytes = 0;
168 }
169
170 backingTexture = resourceProvider->createTexture(
171 bm.dimensions(), format, GrColorType::kRGBA_8888, GrRenderable::kNo, 1,
172 SkBudgeted::kNo, GrProtected::kNo, texels.get(), mipLevelCount);
173 if (!backingTexture) {
174 fputs("Failed to create backingTexture.\n", stderr);
175 return false;
176 }
177
178 backEndTexture = backingTexture->getBackendTexture();
179 if (!backEndTexture.isValid()) {
180 fputs("BackingTexture is invalid.\n", stderr);
181 return false;
182 }
183 }
184
185 SkISize offscreenDims = {options.fOffScreenWidth, options.fOffScreenHeight};
186 SkAutoTMalloc<uint32_t> data(offscreenDims.area());
187 sk_memset32(data.get(), 0, offscreenDims.area());
188
189 {
190 // This backend object should be renderable but not textureable. Given the limitations
191 // of how we're creating it though it will wind up being secretly textureable.
192 // We use this fact to initialize it with data but don't allow mipmaps
193 GrMipLevel level0 = { data.get(), offscreenDims.width()*sizeof(uint32_t) };
194
195 sk_sp<GrTexture> tmp = resourceProvider->createTexture(
196 offscreenDims, renderableFormat, GrColorType::kRGBA_8888, GrRenderable::kYes,
197 options.fOffScreenSampleCount, SkBudgeted::kNo, GrProtected::kNo, &level0, 1);
198 if (!tmp || !tmp->asRenderTarget()) {
199 fputs("GrTexture is invalid.\n", stderr);
200 return false;
201 }
202
203 backingRenderTarget = sk_ref_sp(tmp->asRenderTarget());
204
205 backEndRenderTarget = backingRenderTarget->getBackendRenderTarget();
206 if (!backEndRenderTarget.isValid()) {
207 fputs("BackEndRenderTarget is invalid.\n", stderr);
208 return false;
209 }
210 }
211
212 {
213 int mipLevelCount =
214 GrMipMapped::kYes == options.fOffScreenMipMapping
215 ? SkMipMap::ComputeLevelCount(offscreenDims.width(), offscreenDims.height())
216 : 1;
217 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
218
219 texels[0].fPixels = data.get();
220 texels[0].fRowBytes = offscreenDims.width()*sizeof(uint32_t);
221
222 for (int i = 1; i < mipLevelCount; i++) {
223 texels[i].fPixels = nullptr;
224 texels[i].fRowBytes = 0;
225 }
226
227 backingTextureRenderTarget = resourceProvider->createTexture(
228 offscreenDims, renderableFormat, GrColorType::kRGBA_8888, GrRenderable::kYes,
229 options.fOffScreenSampleCount, SkBudgeted::kNo, GrProtected::kNo, texels.get(),
230 mipLevelCount);
231 if (!backingTextureRenderTarget || !backingTextureRenderTarget->asRenderTarget()) {
232 fputs("backingTextureRenderTarget is invalid.\n", stderr);
233 return false;
234 }
235
236 backEndTextureRenderTarget = backingTextureRenderTarget->getBackendTexture();
237 if (!backEndTextureRenderTarget.isValid()) {
238 fputs("backEndTextureRenderTarget is invalid.\n", stderr);
239 return false;
240 }
241 }
242
243
244 return true;
245 }
246
main(int argc,char ** argv)247 int main(int argc, char** argv) {
248 CommandLineFlags::Parse(argc, argv);
249 duration = FLAGS_duration;
250 frame = FLAGS_frame;
251 DrawOptions options = GetDrawOptions();
252 // If textOnly then only do one type of image, otherwise the text
253 // output is duplicated for each type.
254 if (options.textOnly) {
255 options.raster = true;
256 options.gpu = false;
257 options.pdf = false;
258 options.skp = false;
259 }
260 if (options.source) {
261 sk_sp<SkData> data(SkData::MakeFromFileName(options.source));
262 if (!data) {
263 perror(options.source);
264 return 1;
265 } else {
266 image = SkImage::MakeFromEncoded(std::move(data));
267 if (!image) {
268 perror("Unable to decode the source image.");
269 return 1;
270 }
271 SkAssertResult(image->asLegacyBitmap(&source));
272 }
273 }
274 sk_sp<SkData> rasterData, gpuData, pdfData, skpData;
275 SkColorType colorType = kN32_SkColorType;
276 sk_sp<SkColorSpace> colorSpace = nullptr;
277 if (options.f16) {
278 SkASSERT(options.srgb);
279 colorType = kRGBA_F16_SkColorType;
280 colorSpace = SkColorSpace::MakeSRGBLinear();
281 } else if (options.srgb) {
282 colorSpace = SkColorSpace::MakeSRGB();
283 }
284 SkImageInfo info = SkImageInfo::Make(options.size.width(), options.size.height(), colorType,
285 kPremul_SkAlphaType, colorSpace);
286 if (options.raster) {
287 auto rasterSurface = SkSurface::MakeRaster(info);
288 srand(0);
289 draw(prepare_canvas(rasterSurface->getCanvas()));
290 rasterData = encode_snapshot(rasterSurface);
291 }
292 if (options.gpu) {
293 std::unique_ptr<sk_gpu_test::GLTestContext> glContext;
294 sk_sp<GrContext> grContext = create_grcontext(gGLDriverInfo, &glContext);
295 if (!grContext) {
296 fputs("Unable to get GrContext.\n", stderr);
297 } else {
298 if (!setup_backend_objects(grContext.get(), source, options)) {
299 fputs("Unable to create backend objects.\n", stderr);
300 exit(1);
301 }
302
303 auto surface = SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kNo, info);
304 if (!surface) {
305 fputs("Unable to get render surface.\n", stderr);
306 exit(1);
307 }
308 srand(0);
309 draw(prepare_canvas(surface->getCanvas()));
310 gpuData = encode_snapshot(surface);
311 }
312 }
313 if (options.pdf) {
314 SkDynamicMemoryWStream pdfStream;
315 auto document = SkPDF::MakeDocument(&pdfStream);
316 if (document) {
317 srand(0);
318 draw(prepare_canvas(document->beginPage(options.size.width(), options.size.height())));
319 document->close();
320 pdfData = pdfStream.detachAsData();
321 }
322 }
323 if (options.skp) {
324 auto size = SkSize::Make(options.size);
325 SkPictureRecorder recorder;
326 srand(0);
327 draw(prepare_canvas(recorder.beginRecording(size.width(), size.height())));
328 auto picture = recorder.finishRecordingAsPicture();
329 SkDynamicMemoryWStream skpStream;
330 picture->serialize(&skpStream);
331 skpData = skpStream.detachAsData();
332 }
333
334 printf("{\n");
335 if (!options.textOnly) {
336 dump_output(rasterData, "Raster", false);
337 dump_output(gpuData, "Gpu", false);
338 dump_output(pdfData, "Pdf", false);
339 dump_output(skpData, "Skp", false);
340 } else {
341 std::string textoutput = gTextOutput.str();
342 dump_output(textoutput.c_str(), textoutput.length(), "Text", false);
343 }
344 std::string glinfo = gGLDriverInfo.str();
345 dump_output(glinfo.c_str(), glinfo.length(), "GLInfo", true);
346 printf("}\n");
347
348 return 0;
349 }
350