render_pictures_main.cpp revision 5a7c6be72b940dde8ff6ad2485a09aecd56a2660
1/*
2 * Copyright 2012 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 "SkBitmap.h"
9#include "SkCanvas.h"
10#include "SkDevice.h"
11#include "SkGraphics.h"
12#include "SkImageDecoder.h"
13#include "SkMath.h"
14#include "SkOSFile.h"
15#include "SkPicture.h"
16#include "SkStream.h"
17#include "SkString.h"
18#include "SkTArray.h"
19#include "PictureRenderer.h"
20#include "picture_utils.h"
21
22static void usage(const char* argv0) {
23    SkDebugf("SkPicture rendering tool\n");
24    SkDebugf("\n"
25"Usage: \n"
26"     %s <input>... <outputDir> \n"
27"     [--mode pow2tile minWidth height[%] | simple\n"
28"         | tile width[%] height[%]]\n"
29"     [--pipe]\n"
30"     [--multi count]\n"
31"     [--device bitmap"
32#if SK_SUPPORT_GPU
33" | gpu"
34#endif
35"]"
36, argv0);
37    SkDebugf("\n\n");
38    SkDebugf(
39"     input:     A list of directories and files to use as input. Files are\n"
40"                expected to have the .skp extension.\n\n");
41    SkDebugf(
42"     outputDir: directory to write the rendered images.\n\n");
43    SkDebugf(
44"     --mode pow2tile minWidth height[%] | simple\n"
45"          | tile width[%] height[%]: Run in the corresponding mode.\n"
46"                                     Default is simple.\n");
47    SkDebugf(
48"                     pow2tile minWidth height[%], Creates tiles with widths\n"
49"                                                  that are all a power of two\n"
50"                                                  such that they minimize the\n"
51"                                                  amount of wasted tile space.\n"
52"                                                  minWidth is the minimum width\n"
53"                                                  of these tiles and must be a\n"
54"                                                  power of two. A simple render\n"
55"                                                  is done with these tiles.\n");
56    SkDebugf(
57"                     simple, Render using the default rendering method.\n");
58    SkDebugf(
59"                     tile width[%] height[%], Do a simple render using tiles\n"
60"                                              with the given dimensions.\n");
61    SkDebugf("\n");
62    SkDebugf(
63"     --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
64"                     than 1. Only works with tiled rendering.\n"
65"     --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n");
66    SkDebugf(
67"     --device bitmap"
68#if SK_SUPPORT_GPU
69" | gpu"
70#endif
71": Use the corresponding device. Default is bitmap.\n");
72    SkDebugf(
73"                     bitmap, Render to a bitmap.\n");
74#if SK_SUPPORT_GPU
75    SkDebugf(
76"                     gpu, Render to the GPU.\n");
77#endif
78}
79
80static void make_output_filepath(SkString* path, const SkString& dir,
81                                 const SkString& name) {
82    sk_tools::make_filepath(path, dir, name);
83    // Remove ".skp"
84    path->remove(path->size() - 4, 4);
85}
86
87static bool render_picture(const SkString& inputPath, const SkString& outputDir,
88                           sk_tools::PictureRenderer& renderer) {
89    SkString inputFilename;
90    sk_tools::get_basename(&inputFilename, inputPath);
91
92    SkFILEStream inputStream;
93    inputStream.setPath(inputPath.c_str());
94    if (!inputStream.isValid()) {
95        SkDebugf("Could not open file %s\n", inputPath.c_str());
96        return false;
97    }
98
99    bool success = false;
100    SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
101    if (!success) {
102        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
103        return false;
104    }
105
106    SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(),
107             inputPath.c_str());
108
109    renderer.init(&picture);
110    renderer.setup();
111
112    SkString outputPath;
113    make_output_filepath(&outputPath, outputDir, inputFilename);
114
115    success = renderer.render(&outputPath);
116    if (!success) {
117        SkDebugf("Could not write to file %s\n", outputPath.c_str());
118    }
119
120    renderer.resetState();
121
122    renderer.end();
123    return success;
124}
125
126static int process_input(const SkString& input, const SkString& outputDir,
127                          sk_tools::PictureRenderer& renderer) {
128    SkOSFile::Iter iter(input.c_str(), "skp");
129    SkString inputFilename;
130    int failures = 0;
131    if (iter.next(&inputFilename)) {
132        do {
133            SkString inputPath;
134            sk_tools::make_filepath(&inputPath, input, inputFilename);
135            if (!render_picture(inputPath, outputDir, renderer)) {
136                ++failures;
137            }
138        } while(iter.next(&inputFilename));
139    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
140        SkString inputPath(input);
141        if (!render_picture(inputPath, outputDir, renderer)) {
142            ++failures;
143        }
144    } else {
145        SkString warning;
146        warning.printf("Warning: skipping %s\n", input.c_str());
147        SkDebugf(warning.c_str());
148    }
149    return failures;
150}
151
152static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
153                              sk_tools::PictureRenderer*& renderer){
154    const char* argv0 = argv[0];
155    char* const* stop = argv + argc;
156
157    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
158        sk_tools::PictureRenderer::kBitmap_DeviceType;
159
160    bool usePipe = false;
161    int numThreads = 1;
162    bool useTiles = false;
163    const char* widthString = NULL;
164    const char* heightString = NULL;
165    bool isPowerOf2Mode = false;
166    const char* mode = NULL;
167
168    for (++argv; argv < stop; ++argv) {
169        if (0 == strcmp(*argv, "--mode")) {
170            SkDELETE(renderer);
171
172            ++argv;
173            if (argv >= stop) {
174                SkDebugf("Missing mode for --mode\n");
175                usage(argv0);
176                exit(-1);
177            }
178
179            if (0 == strcmp(*argv, "simple")) {
180                renderer = SkNEW(sk_tools::SimplePictureRenderer);
181            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
182                useTiles = true;
183                mode = *argv;
184
185                if (0 == strcmp(*argv, "pow2tile")) {
186                    isPowerOf2Mode = true;
187                }
188
189                ++argv;
190                if (argv >= stop) {
191                    SkDebugf("Missing width for --mode %s\n", mode);
192                    usage(argv0);
193                    exit(-1);
194                }
195
196                widthString = *argv;
197                ++argv;
198                if (argv >= stop) {
199                    SkDebugf("Missing height for --mode tile\n");
200                    usage(argv0);
201                    exit(-1);
202                }
203                heightString = *argv;
204            } else {
205                SkDebugf("%s is not a valid mode for --mode\n", *argv);
206                usage(argv0);
207                exit(-1);
208            }
209        } else if (0 == strcmp(*argv, "--pipe")) {
210            usePipe = true;
211        } else if (0 == strcmp(*argv, "--multi")) {
212            ++argv;
213            if (argv >= stop) {
214                SkDebugf("Missing arg for --multi\n");
215                usage(argv0);
216                exit(-1);
217            }
218            numThreads = atoi(*argv);
219            if (numThreads < 2) {
220                SkDebugf("Number of threads must be at least 2.\n");
221                usage(argv0);
222                exit(-1);
223            }
224        } else if (0 == strcmp(*argv, "--device")) {
225            ++argv;
226            if (argv >= stop) {
227                SkDebugf("Missing mode for --device\n");
228                usage(argv0);
229                exit(-1);
230            }
231
232            if (0 == strcmp(*argv, "bitmap")) {
233                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
234            }
235#if SK_SUPPORT_GPU
236            else if (0 == strcmp(*argv, "gpu")) {
237                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
238            }
239#endif
240            else {
241                SkDebugf("%s is not a valid mode for --device\n", *argv);
242                usage(argv0);
243                exit(-1);
244            }
245
246        } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
247            SkDELETE(renderer);
248            usage(argv0);
249            exit(-1);
250        } else {
251            inputs->push_back(SkString(*argv));
252        }
253    }
254
255    if (numThreads > 1 && !useTiles) {
256        SkDebugf("Multithreaded drawing requires tiled rendering.\n");
257        usage(argv0);
258        exit(-1);
259    }
260
261    if (useTiles) {
262        SkASSERT(NULL == renderer);
263        sk_tools::TiledPictureRenderer* tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
264        if (isPowerOf2Mode) {
265            int minWidth = atoi(widthString);
266            if (!SkIsPow2(minWidth) || minWidth < 0) {
267                tiledRenderer->unref();
268                SkString err;
269                err.printf("-mode %s must be given a width"
270                           " value that is a power of two\n", mode);
271                SkDebugf(err.c_str());
272                usage(argv0);
273                exit(-1);
274            }
275            tiledRenderer->setTileMinPowerOf2Width(minWidth);
276        } else if (sk_tools::is_percentage(widthString)) {
277            tiledRenderer->setTileWidthPercentage(atof(widthString));
278            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
279                tiledRenderer->unref();
280                SkDebugf("--mode tile must be given a width percentage > 0\n");
281                usage(argv0);
282                exit(-1);
283            }
284        } else {
285            tiledRenderer->setTileWidth(atoi(widthString));
286            if (!(tiledRenderer->getTileWidth() > 0)) {
287                tiledRenderer->unref();
288                SkDebugf("--mode tile must be given a width > 0\n");
289                usage(argv0);
290                exit(-1);
291            }
292        }
293
294        if (sk_tools::is_percentage(heightString)) {
295            tiledRenderer->setTileHeightPercentage(atof(heightString));
296            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
297                tiledRenderer->unref();
298                SkDebugf("--mode tile must be given a height percentage > 0\n");
299                usage(argv0);
300                exit(-1);
301            }
302        } else {
303            tiledRenderer->setTileHeight(atoi(heightString));
304            if (!(tiledRenderer->getTileHeight() > 0)) {
305                tiledRenderer->unref();
306                SkDebugf("--mode tile must be given a height > 0\n");
307                usage(argv0);
308                exit(-1);
309            }
310        }
311        if (numThreads > 1) {
312#if SK_SUPPORT_GPU
313            if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
314                tiledRenderer->unref();
315                SkDebugf("GPU not compatible with multithreaded tiling.\n");
316                usage(argv0);
317                exit(-1);
318            }
319#endif
320            tiledRenderer->setNumberOfThreads(numThreads);
321        }
322        tiledRenderer->setUsePipe(usePipe);
323        renderer = tiledRenderer;
324    } else if (usePipe) {
325        renderer = SkNEW(sk_tools::PipePictureRenderer);
326    }
327
328    if (inputs->count() < 2) {
329        SkDELETE(renderer);
330        usage(argv0);
331        exit(-1);
332    }
333
334    if (NULL == renderer) {
335        renderer = SkNEW(sk_tools::SimplePictureRenderer);
336    }
337
338    renderer->setDeviceType(deviceType);
339}
340
341int tool_main(int argc, char** argv);
342int tool_main(int argc, char** argv) {
343    SkAutoGraphics ag;
344    SkTArray<SkString> inputs;
345    sk_tools::PictureRenderer* renderer = NULL;
346
347    parse_commandline(argc, argv, &inputs, renderer);
348    SkString outputDir = inputs[inputs.count() - 1];
349    SkASSERT(renderer);
350
351    int failures = 0;
352    for (int i = 0; i < inputs.count() - 1; i ++) {
353        failures += process_input(inputs[i], outputDir, *renderer);
354    }
355    if (failures != 0) {
356        SkDebugf("Failed to render %i pictures.\n", failures);
357        return 1;
358    }
359#if SK_SUPPORT_GPU
360#if GR_CACHE_STATS
361    if (renderer->isUsingGpuDevice()) {
362        GrContext* ctx = renderer->getGrContext();
363
364        ctx->printCacheStats();
365    }
366#endif
367#endif
368
369    SkDELETE(renderer);
370    return 0;
371}
372
373#if !defined SK_BUILD_FOR_IOS
374int main(int argc, char * const argv[]) {
375    return tool_main(argc, (char**) argv);
376}
377#endif
378