render_pictures_main.cpp revision c0d5e549ab8d594a5da8db417db39622e9491fff
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 "CopyTilesRenderer.h"
9#include "SkBitmap.h"
10#include "SkCanvas.h"
11#include "SkDevice.h"
12#include "SkGraphics.h"
13#include "SkImageDecoder.h"
14#include "SkMath.h"
15#include "SkOSFile.h"
16#include "SkPicture.h"
17#include "SkStream.h"
18#include "SkString.h"
19#include "SkTArray.h"
20#include "PictureRenderer.h"
21#include "picture_utils.h"
22
23static void usage(const char* argv0) {
24    SkDebugf("SkPicture rendering tool\n");
25    SkDebugf("\n"
26"Usage: \n"
27"     %s <input>... \n"
28"     [-w <outputDir>]\n"
29"     [--mode pow2tile minWidth height | copyTile width height | simple\n"
30"         | tile width height]\n"
31"     [--pipe]\n"
32"     [--multi count]\n"
33"     [--viewport width height]\n"
34"     [--device bitmap"
35#if SK_SUPPORT_GPU
36" | gpu"
37#endif
38"]"
39, argv0);
40    SkDebugf("\n\n");
41    SkDebugf(
42"     input:     A list of directories and files to use as input. Files are\n"
43"                expected to have the .skp extension.\n\n");
44    SkDebugf(
45"     outputDir: directory to write the rendered images.\n\n");
46    SkDebugf(
47"     --mode pow2tile minWidth height | copyTile width height | simple\n"
48"          | tile width height | rerecord: Run in the corresponding mode.\n"
49"                                     Default is simple.\n");
50    SkDebugf(
51"                     pow2tile minWidth height, Creates tiles with widths\n"
52"                                                  that are all a power of two\n"
53"                                                  such that they minimize the\n"
54"                                                  amount of wasted tile space.\n"
55"                                                  minWidth is the minimum width\n"
56"                                                  of these tiles and must be a\n"
57"                                                  power of two. A simple render\n"
58"                                                  is done with these tiles.\n");
59    SkDebugf(
60"                     simple, Render using the default rendering method.\n"
61"                     rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n"
62             );
63    SkDebugf(
64"                     tile width height, Do a simple render using tiles\n"
65"                                              with the given dimensions.\n"
66"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
67"                                                Does not support percentages.\n"
68"                                                If the picture is large enough, breaks it into\n"
69"                                                larger tiles (and draws the picture once per\n"
70"                                                larger tile) to avoid creating a large canvas.\n"
71"                                                Add --tiles x y to specify the number of tiles\n"
72"                                                per larger tile in the x and y direction.\n"
73             );
74    SkDebugf("\n");
75    SkDebugf(
76"     --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
77"                     than 1. Only works with tiled rendering.\n"
78"     --viewport width height : Set the viewport.\n"
79"     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
80    SkDebugf(
81"     --device bitmap"
82#if SK_SUPPORT_GPU
83" | gpu"
84#endif
85": Use the corresponding device. Default is bitmap.\n");
86    SkDebugf(
87"                     bitmap, Render to a bitmap.\n");
88#if SK_SUPPORT_GPU
89    SkDebugf(
90"                     gpu, Render to the GPU.\n");
91#endif
92}
93
94static void make_output_filepath(SkString* path, const SkString& dir,
95                                 const SkString& name) {
96    sk_tools::make_filepath(path, dir, name);
97    // Remove ".skp"
98    path->remove(path->size() - 4, 4);
99}
100
101static bool render_picture(const SkString& inputPath, const SkString* outputDir,
102                           sk_tools::PictureRenderer& renderer) {
103    SkString inputFilename;
104    sk_tools::get_basename(&inputFilename, inputPath);
105
106    SkFILEStream inputStream;
107    inputStream.setPath(inputPath.c_str());
108    if (!inputStream.isValid()) {
109        SkDebugf("Could not open file %s\n", inputPath.c_str());
110        return false;
111    }
112
113    bool success = false;
114    SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
115    if (!success) {
116        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
117        return false;
118    }
119
120    SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(),
121             inputPath.c_str());
122
123    renderer.init(&picture);
124    renderer.setup();
125
126    SkString* outputPath = NULL;
127    if (NULL != outputDir) {
128        outputPath = SkNEW(SkString);
129        make_output_filepath(outputPath, *outputDir, inputFilename);
130    }
131    success = renderer.render(outputPath);
132    if (outputPath) {
133        if (!success) {
134            SkDebugf("Could not write to file %s\n", outputPath->c_str());
135        }
136        SkDELETE(outputPath);
137    }
138
139    renderer.resetState();
140
141    renderer.end();
142    return success;
143}
144
145static int process_input(const SkString& input, const SkString* outputDir,
146                          sk_tools::PictureRenderer& renderer) {
147    SkOSFile::Iter iter(input.c_str(), "skp");
148    SkString inputFilename;
149    int failures = 0;
150    SkDebugf("process_input, %s\n", input.c_str());
151    if (iter.next(&inputFilename)) {
152        do {
153            SkString inputPath;
154            sk_tools::make_filepath(&inputPath, input, inputFilename);
155            if (!render_picture(inputPath, outputDir, renderer)) {
156                ++failures;
157            }
158        } while(iter.next(&inputFilename));
159    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
160        SkString inputPath(input);
161        if (!render_picture(inputPath, outputDir, renderer)) {
162            ++failures;
163        }
164    } else {
165        SkString warning;
166        warning.printf("Warning: skipping %s\n", input.c_str());
167        SkDebugf(warning.c_str());
168    }
169    return failures;
170}
171
172static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
173                              sk_tools::PictureRenderer*& renderer, SkString*& outputDir){
174    const char* argv0 = argv[0];
175    char* const* stop = argv + argc;
176
177    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
178        sk_tools::PictureRenderer::kBitmap_DeviceType;
179
180    bool usePipe = false;
181    int numThreads = 1;
182    bool useTiles = false;
183    const char* widthString = NULL;
184    const char* heightString = NULL;
185    bool isPowerOf2Mode = false;
186    bool isCopyMode = false;
187    const char* xTilesString = NULL;
188    const char* yTilesString = NULL;
189    const char* mode = NULL;
190    SkISize viewport;
191    viewport.setEmpty();
192    for (++argv; argv < stop; ++argv) {
193        if (0 == strcmp(*argv, "--mode")) {
194            if (renderer != NULL) {
195                renderer->unref();
196                SkDebugf("Cannot combine modes.\n");
197                usage(argv0);
198                exit(-1);
199            }
200
201            ++argv;
202            if (argv >= stop) {
203                SkDebugf("Missing mode for --mode\n");
204                usage(argv0);
205                exit(-1);
206            }
207
208            if (0 == strcmp(*argv, "simple")) {
209                renderer = SkNEW(sk_tools::SimplePictureRenderer);
210            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
211                       || 0 == strcmp(*argv, "copyTile")) {
212                useTiles = true;
213                mode = *argv;
214
215                if (0 == strcmp(*argv, "pow2tile")) {
216                    isPowerOf2Mode = true;
217                } else if (0 == strcmp(*argv, "copyTile")) {
218                    isCopyMode = true;
219                }
220
221                ++argv;
222                if (argv >= stop) {
223                    SkDebugf("Missing width for --mode %s\n", mode);
224                    usage(argv0);
225                    exit(-1);
226                }
227
228                widthString = *argv;
229                ++argv;
230                if (argv >= stop) {
231                    SkDebugf("Missing height for --mode %s\n", mode);
232                    usage(argv0);
233                    exit(-1);
234                }
235                heightString = *argv;
236            } else if (0 == strcmp(*argv, "rerecord")) {
237                renderer = SkNEW(sk_tools::RecordPictureRenderer);
238            } else {
239                SkDebugf("%s is not a valid mode for --mode\n", *argv);
240                usage(argv0);
241                exit(-1);
242            }
243        } else if (0 == strcmp(*argv, "--viewport")) {
244            ++argv;
245            if (argv >= stop) {
246                SkDebugf("Missing width for --viewport\n");
247                usage(argv0);
248                exit(-1);
249            }
250            viewport.fWidth = atoi(*argv);
251            ++argv;
252            if (argv >= stop) {
253                SkDebugf("Missing height for --viewport\n");
254                usage(argv0);
255                exit(-1);
256            }
257            viewport.fHeight = atoi(*argv);
258        } else if (0 == strcmp(*argv, "--tiles")) {
259            ++argv;
260            if (argv >= stop) {
261                SkDebugf("Missing x for --tiles\n");
262                usage(argv0);
263                exit(-1);
264            }
265            xTilesString = *argv;
266            ++argv;
267            if (argv >= stop) {
268                SkDebugf("Missing y for --tiles\n");
269                usage(argv0);
270                exit(-1);
271            }
272            yTilesString = *argv;
273        } else if (0 == strcmp(*argv, "--pipe")) {
274            usePipe = true;
275        } else if (0 == strcmp(*argv, "--multi")) {
276            ++argv;
277            if (argv >= stop) {
278                SkSafeUnref(renderer);
279                SkDebugf("Missing arg for --multi\n");
280                usage(argv0);
281                exit(-1);
282            }
283            numThreads = atoi(*argv);
284            if (numThreads < 2) {
285                SkSafeUnref(renderer);
286                SkDebugf("Number of threads must be at least 2.\n");
287                usage(argv0);
288                exit(-1);
289            }
290        } else if (0 == strcmp(*argv, "--device")) {
291            ++argv;
292            if (argv >= stop) {
293                SkSafeUnref(renderer);
294                SkDebugf("Missing mode for --device\n");
295                usage(argv0);
296                exit(-1);
297            }
298
299            if (0 == strcmp(*argv, "bitmap")) {
300                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
301            }
302#if SK_SUPPORT_GPU
303            else if (0 == strcmp(*argv, "gpu")) {
304                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
305            }
306#endif
307            else {
308                SkSafeUnref(renderer);
309                SkDebugf("%s is not a valid mode for --device\n", *argv);
310                usage(argv0);
311                exit(-1);
312            }
313
314        } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
315            SkSafeUnref(renderer);
316            usage(argv0);
317            exit(-1);
318        } else if (0 == strcmp(*argv, "-w")) {
319            ++argv;
320            if (argv >= stop) {
321                SkDebugf("Missing output directory for -w\n");
322                usage(argv0);
323                exit(-1);
324            }
325            outputDir = SkNEW_ARGS(SkString, (*argv));
326        } else {
327            inputs->push_back(SkString(*argv));
328        }
329    }
330
331    if (numThreads > 1 && !useTiles) {
332        SkSafeUnref(renderer);
333        SkDebugf("Multithreaded drawing requires tiled rendering.\n");
334        usage(argv0);
335        exit(-1);
336    }
337
338    if (useTiles) {
339        SkASSERT(NULL == renderer);
340        sk_tools::TiledPictureRenderer* tiledRenderer;
341        if (isCopyMode) {
342            int x, y;
343            if (xTilesString != NULL) {
344                SkASSERT(yTilesString != NULL);
345                x = atoi(xTilesString);
346                y = atoi(yTilesString);
347                if (x <= 0 || y <= 0) {
348                    SkDebugf("--tiles must be given values > 0\n");
349                    usage(argv0);
350                    exit(-1);
351                }
352            } else {
353                x = y = 4;
354            }
355            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
356        } else if (numThreads > 1) {
357            tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
358        } else {
359            tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
360        }
361        if (isPowerOf2Mode) {
362            int minWidth = atoi(widthString);
363            if (!SkIsPow2(minWidth) || minWidth < 0) {
364                tiledRenderer->unref();
365                SkString err;
366                err.printf("-mode %s must be given a width"
367                           " value that is a power of two\n", mode);
368                SkDebugf(err.c_str());
369                usage(argv0);
370                exit(-1);
371            }
372            tiledRenderer->setTileMinPowerOf2Width(minWidth);
373        } else if (sk_tools::is_percentage(widthString)) {
374            if (isCopyMode) {
375                tiledRenderer->unref();
376                SkString err;
377                err.printf("--mode %s does not support percentages.\n", mode);
378                SkDebugf(err.c_str());
379                usage(argv0);
380                exit(-1);
381            }
382            tiledRenderer->setTileWidthPercentage(atof(widthString));
383            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
384                tiledRenderer->unref();
385                SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
386                usage(argv0);
387                exit(-1);
388            }
389        } else {
390            tiledRenderer->setTileWidth(atoi(widthString));
391            if (!(tiledRenderer->getTileWidth() > 0)) {
392                tiledRenderer->unref();
393                SkDebugf("--mode %s must be given a width > 0\n", mode);
394                usage(argv0);
395                exit(-1);
396            }
397        }
398
399        if (sk_tools::is_percentage(heightString)) {
400            if (isCopyMode) {
401                tiledRenderer->unref();
402                SkString err;
403                err.printf("--mode %s does not support percentages.\n", mode);
404                SkDebugf(err.c_str());
405                usage(argv0);
406                exit(-1);
407            }
408            tiledRenderer->setTileHeightPercentage(atof(heightString));
409            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
410                tiledRenderer->unref();
411                SkDebugf("--mode %s must be given a height percentage > 0\n", mode);
412                usage(argv0);
413                exit(-1);
414            }
415        } else {
416            tiledRenderer->setTileHeight(atoi(heightString));
417            if (!(tiledRenderer->getTileHeight() > 0)) {
418                tiledRenderer->unref();
419                SkDebugf("--mode %s must be given a height > 0\n", mode);
420                usage(argv0);
421                exit(-1);
422            }
423        }
424        if (numThreads > 1) {
425#if SK_SUPPORT_GPU
426            if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
427                tiledRenderer->unref();
428                SkDebugf("GPU not compatible with multithreaded tiling.\n");
429                usage(argv0);
430                exit(-1);
431            }
432#endif
433        }
434        renderer = tiledRenderer;
435        if (usePipe) {
436            SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
437                     "Turning off pipe.\n");
438        }
439    } else if (usePipe) {
440        if (renderer != NULL) {
441            renderer->unref();
442            SkDebugf("Pipe is incompatible with other modes.\n");
443            usage(argv0);
444            exit(-1);
445        }
446        renderer = SkNEW(sk_tools::PipePictureRenderer);
447    }
448
449    if (inputs->empty()) {
450        SkSafeUnref(renderer);
451        if (NULL != outputDir) {
452            SkDELETE(outputDir);
453        }
454        usage(argv0);
455        exit(-1);
456    }
457
458    if (NULL == renderer) {
459        renderer = SkNEW(sk_tools::SimplePictureRenderer);
460    }
461
462    renderer->setViewport(viewport);
463    renderer->setDeviceType(deviceType);
464}
465
466int tool_main(int argc, char** argv);
467int tool_main(int argc, char** argv) {
468    SkAutoGraphics ag;
469    SkTArray<SkString> inputs;
470    sk_tools::PictureRenderer* renderer = NULL;
471    SkString* outputDir = NULL;
472    parse_commandline(argc, argv, &inputs, renderer, outputDir);
473    SkASSERT(renderer);
474
475    int failures = 0;
476    for (int i = 0; i < inputs.count(); i ++) {
477        failures += process_input(inputs[i], outputDir, *renderer);
478    }
479    if (failures != 0) {
480        SkDebugf("Failed to render %i pictures.\n", failures);
481        return 1;
482    }
483#if SK_SUPPORT_GPU
484#if GR_CACHE_STATS
485    if (renderer->isUsingGpuDevice()) {
486        GrContext* ctx = renderer->getGrContext();
487
488        ctx->printCacheStats();
489    }
490#endif
491#endif
492    if (NULL != outputDir) {
493        SkDELETE(outputDir);
494    }
495    SkDELETE(renderer);
496    return 0;
497}
498
499#if !defined SK_BUILD_FOR_IOS
500int main(int argc, char * const argv[]) {
501    return tool_main(argc, (char**) argv);
502}
503#endif
504