render_pictures_main.cpp revision e04e92b19f050892c2770da955e4c931e0ef698b
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 "SkMath.h"
13#include "SkOSFile.h"
14#include "SkPicture.h"
15#include "SkStream.h"
16#include "SkString.h"
17#include "SkTArray.h"
18#include "PictureRenderer.h"
19#include "picture_utils.h"
20
21static void usage(const char* argv0) {
22    SkDebugf("SkPicture rendering tool\n");
23    SkDebugf("\n"
24"Usage: \n"
25"     %s <input>... <outputDir> \n"
26"     [--mode pipe | pow2tile minWidth height[%] | simple\n"
27"         | tile width[%] height[%]]\n"
28"     [--device bitmap"
29#if SK_SUPPORT_GPU
30" | gpu"
31#endif
32"]"
33, argv0);
34    SkDebugf("\n\n");
35    SkDebugf(
36"     input:     A list of directories and files to use as input. Files are\n"
37"                expected to have the .skp extension.\n\n");
38    SkDebugf(
39"     outputDir: directory to write the rendered images.\n\n");
40    SkDebugf(
41"     --mode pipe | pow2tile minWidth height[%] | simple\n"
42"          | tile width[%] height[%]: Run in the corresponding mode.\n"
43"                                     Default is simple.\n");
44    SkDebugf(
45"                     pipe, Render using a SkGPipe.\n");
46    SkDebugf(
47"                     pow2tile minWidth height[%], Creates tiles with widths\n"
48"                                                  that are all a power of two\n"
49"                                                  such that they minimize the\n"
50"                                                  amount of wasted tile space.\n"
51"                                                  minWidth is the minimum width\n"
52"                                                  of these tiles and must be a\n"
53"                                                  power of two. A simple render\n"
54"                                                  is done with these tiles.\n");
55    SkDebugf(
56"                     simple, Render using the default rendering method.\n");
57    SkDebugf(
58"                     tile width[%] height[%], Do a simple render using tiles\n"
59"                                              with the given dimensions.\n");
60    SkDebugf("\n");
61    SkDebugf(
62"     --device bitmap"
63#if SK_SUPPORT_GPU
64" | gpu"
65#endif
66": Use the corresponding device. Default is bitmap.\n");
67    SkDebugf(
68"                     bitmap, Render to a bitmap.\n");
69#if SK_SUPPORT_GPU
70    SkDebugf(
71"                     gpu, Render to the GPU.\n");
72#endif
73}
74
75static void make_output_filepath(SkString* path, const SkString& dir,
76                                 const SkString& name) {
77    sk_tools::make_filepath(path, dir, name);
78    path->remove(path->size() - 3, 3);
79    path->append("png");
80}
81
82static void write_output(const SkString& outputDir, const SkString& inputFilename,
83                         const sk_tools::PictureRenderer& renderer) {
84    SkString outputPath;
85    make_output_filepath(&outputPath, outputDir, inputFilename);
86    bool isWritten = renderer.write(outputPath);
87    if (!isWritten) {
88        SkDebugf("Could not write to file %s\n", outputPath.c_str());
89    }
90}
91
92static bool area_too_big(int w, int h, SkISize* newSize) {
93    // just a guess, based on what seems to fail on smaller android devices
94    static const int64_t kMaxAreaForMemory = 16 * 1024 * 1024;
95
96    if ((int64_t)w * h > kMaxAreaForMemory) {
97        do {
98            w >>= 1;
99            h >>= 1;
100        } while ((int64_t)w * h > kMaxAreaForMemory);
101        if (0 == w) {
102            w = 1;
103        }
104        if (0 == h) {
105            h = 1;
106        }
107        newSize->set(w, h);
108        return true;
109    }
110    return false;
111}
112
113static void render_picture(const SkString& inputPath, const SkString& outputDir,
114                           sk_tools::PictureRenderer& renderer) {
115    SkString inputFilename;
116    sk_tools::get_basename(&inputFilename, inputPath);
117
118    SkFILEStream inputStream;
119    inputStream.setPath(inputPath.c_str());
120    if (!inputStream.isValid()) {
121        SkDebugf("Could not open file %s\n", inputPath.c_str());
122        return;
123    }
124
125    SkPicture picture(&inputStream);
126
127    SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(),
128             inputPath.c_str());
129
130
131    // rescale to avoid memory issues allcoating a very large offscreen
132    SkPicture* pic = &picture;
133    SkISize newSize;
134    SkAutoUnref aur(NULL);
135
136    if (area_too_big(picture.width(), picture.height(), &newSize)) {
137        pic = new SkPicture;
138        aur.reset(pic);
139
140        SkCanvas* canvas = pic->beginRecording(newSize.width(), newSize.height());
141        SkScalar scale = SkIntToScalar(newSize.width()) / picture.width();
142        canvas->scale(scale, scale);
143        canvas->drawPicture(picture);
144        pic->endRecording();
145
146        SkDebugf("... rescaling to [%d %d] to avoid overly large allocations\n",
147                 newSize.width(), newSize.height());
148    }
149
150    renderer.init(pic);
151
152    renderer.render(true);
153
154    renderer.resetState();
155
156    write_output(outputDir, inputFilename, renderer);
157
158    renderer.end();
159}
160
161static void process_input(const SkString& input, const SkString& outputDir,
162                          sk_tools::PictureRenderer& renderer) {
163    SkOSFile::Iter iter(input.c_str(), "skp");
164    SkString inputFilename;
165
166    if (iter.next(&inputFilename)) {
167        do {
168            SkString inputPath;
169            sk_tools::make_filepath(&inputPath, input, inputFilename);
170            render_picture(inputPath, outputDir, renderer);
171        } while(iter.next(&inputFilename));
172    } else {
173        SkString inputPath(input);
174        render_picture(inputPath, outputDir, renderer);
175    }
176}
177
178static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
179                              sk_tools::PictureRenderer*& renderer){
180    const char* argv0 = argv[0];
181    char* const* stop = argv + argc;
182
183    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
184        sk_tools::PictureRenderer::kBitmap_DeviceType;
185
186    for (++argv; argv < stop; ++argv) {
187        if (0 == strcmp(*argv, "--mode")) {
188            SkDELETE(renderer);
189
190            ++argv;
191            if (argv >= stop) {
192                SkDebugf("Missing mode for --mode\n");
193                usage(argv0);
194                exit(-1);
195            }
196
197            if (0 == strcmp(*argv, "pipe")) {
198                renderer = SkNEW(sk_tools::PipePictureRenderer);
199            } else if (0 == strcmp(*argv, "simple")) {
200                renderer = SkNEW(sk_tools::SimplePictureRenderer);
201            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
202                char* mode = *argv;
203                bool isPowerOf2Mode = false;
204
205                if (0 == strcmp(*argv, "pow2tile")) {
206                    isPowerOf2Mode = true;
207                }
208
209                sk_tools::TiledPictureRenderer* tileRenderer =
210                    SkNEW(sk_tools::TiledPictureRenderer);
211                ++argv;
212                if (argv >= stop) {
213                    SkDELETE(tileRenderer);
214                    SkDebugf("Missing width for --mode %s\n", mode);
215                    usage(argv0);
216                    exit(-1);
217                }
218
219                if (isPowerOf2Mode) {
220                    int minWidth = atoi(*argv);
221
222                    if (!SkIsPow2(minWidth) || minWidth <= 0) {
223                        SkDELETE(tileRenderer);
224                        SkDebugf("--mode %s must be given a width"
225                                 " value that is a power of two\n", mode);
226                        exit(-1);
227                    }
228
229                    tileRenderer->setTileMinPowerOf2Width(minWidth);
230                } else if (sk_tools::is_percentage(*argv)) {
231                    tileRenderer->setTileWidthPercentage(atof(*argv));
232                    if (!(tileRenderer->getTileWidthPercentage() > 0)) {
233                        SkDELETE(tileRenderer);
234                        SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
235                        exit(-1);
236                    }
237                } else {
238                    tileRenderer->setTileWidth(atoi(*argv));
239                    if (!(tileRenderer->getTileWidth() > 0)) {
240                        SkDELETE(tileRenderer);
241                        SkDebugf("--mode %s must be given a width > 0\n", mode);
242                        exit(-1);
243                    }
244                }
245
246                ++argv;
247                if (argv >= stop) {
248                    SkDELETE(tileRenderer);
249                    SkDebugf("Missing height for --mode %s\n", mode);
250                    usage(argv0);
251                    exit(-1);
252                }
253
254                if (sk_tools::is_percentage(*argv)) {
255                    tileRenderer->setTileHeightPercentage(atof(*argv));
256                    if (!(tileRenderer->getTileHeightPercentage() > 0)) {
257                        SkDELETE(tileRenderer);
258                        SkDebugf(
259                            "--mode %s must be given a height percentage > 0\n", mode);
260                        exit(-1);
261                    }
262                } else {
263                    tileRenderer->setTileHeight(atoi(*argv));
264                    if (!(tileRenderer->getTileHeight() > 0)) {
265                        SkDELETE(tileRenderer);
266                        SkDebugf("--mode %s must be given a height > 0\n", mode);
267                        exit(-1);
268                    }
269                }
270
271                renderer = tileRenderer;
272            } else {
273                SkDebugf("%s is not a valid mode for --mode\n", *argv);
274                usage(argv0);
275                exit(-1);
276            }
277        } else if (0 == strcmp(*argv, "--device")) {
278            ++argv;
279            if (argv >= stop) {
280                SkDebugf("Missing mode for --deivce\n");
281                usage(argv0);
282                exit(-1);
283            }
284
285            if (0 == strcmp(*argv, "bitmap")) {
286                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
287            }
288#if SK_SUPPORT_GPU
289            else if (0 == strcmp(*argv, "gpu")) {
290                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
291            }
292#endif
293            else {
294                SkDebugf("%s is not a valid mode for --device\n", *argv);
295                usage(argv0);
296                exit(-1);
297            }
298
299        } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
300            SkDELETE(renderer);
301            usage(argv0);
302            exit(-1);
303        } else {
304            inputs->push_back(SkString(*argv));
305        }
306    }
307
308    if (inputs->count() < 2) {
309        SkDELETE(renderer);
310        usage(argv0);
311        exit(-1);
312    }
313
314    if (NULL == renderer) {
315        renderer = SkNEW(sk_tools::SimplePictureRenderer);
316    }
317
318    renderer->setDeviceType(deviceType);
319}
320
321int main(int argc, char* const argv[]) {
322    SkGraphics::Init();
323    SkTArray<SkString> inputs;
324    sk_tools::PictureRenderer* renderer = NULL;
325
326    parse_commandline(argc, argv, &inputs, renderer);
327    SkString outputDir = inputs[inputs.count() - 1];
328    SkASSERT(renderer);
329
330    for (int i = 0; i < inputs.count() - 1; i ++) {
331        process_input(inputs[i], outputDir, *renderer);
332    }
333
334#if SK_SUPPORT_GPU
335#if GR_CACHE_STATS
336    if (renderer->isUsingGpuDevice()) {
337        GrContext* ctx = renderer->getGrContext();
338
339        ctx->printCacheStats();
340    }
341#endif
342#endif
343
344    SkDELETE(renderer);
345    SkGraphics::Term();
346}
347