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 "SkImageEncoder.h"
15 #include "SkMath.h"
16 #include "SkOSFile.h"
17 #include "SkPicture.h"
18 #include "SkStream.h"
19 #include "SkString.h"
20 #include "SkTArray.h"
21 #include "PictureRenderer.h"
22 #include "picture_utils.h"
23
usage(const char * argv0)24 static void usage(const char* argv0) {
25 SkDebugf("SkPicture rendering tool\n");
26 SkDebugf("\n"
27 "Usage: \n"
28 " %s <input>... \n"
29 " [-w <outputDir>]\n"
30 " [--mode pow2tile minWidth height | copyTile width height | simple\n"
31 " | tile width height]\n"
32 " [--pipe]\n"
33 " [--bbh bbhType]\n"
34 " [--multi count]\n"
35 " [--validate [--maxComponentDiff n]]\n"
36 " [--writeWholeImage]\n"
37 " [--clone n]\n"
38 " [--viewport width height][--scale sf]\n"
39 " [--device bitmap"
40 #if SK_SUPPORT_GPU
41 " | gpu"
42 #endif
43 "]"
44 , argv0);
45 SkDebugf("\n\n");
46 SkDebugf(
47 " input: A list of directories and files to use as input. Files are\n"
48 " expected to have the .skp extension.\n\n");
49 SkDebugf(
50 " outputDir: directory to write the rendered images.\n\n");
51 SkDebugf(
52 " --mode pow2tile minWidth height | copyTile width height | simple\n"
53 " | tile width height | rerecord: Run in the corresponding mode.\n"
54 " Default is simple.\n");
55 SkDebugf(
56 " pow2tile minWidth height, Creates tiles with widths\n"
57 " that are all a power of two\n"
58 " such that they minimize the\n"
59 " amount of wasted tile space.\n"
60 " minWidth is the minimum width\n"
61 " of these tiles and must be a\n"
62 " power of two. A simple render\n"
63 " is done with these tiles.\n");
64 SkDebugf(
65 " simple, Render using the default rendering method.\n"
66 " rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n"
67 );
68 SkDebugf(
69 " tile width height, Do a simple render using tiles\n"
70 " with the given dimensions.\n"
71 " copyTile width height, Draw the picture, then copy it into tiles.\n"
72 " Does not support percentages.\n"
73 " If the picture is large enough, breaks it into\n"
74 " larger tiles (and draws the picture once per\n"
75 " larger tile) to avoid creating a large canvas.\n"
76 " Add --tiles x y to specify the number of tiles\n"
77 " per larger tile in the x and y direction.\n"
78 );
79 SkDebugf("\n");
80 SkDebugf(
81 " --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
82 " than 1. Only works with tiled rendering.\n"
83 " --viewport width height : Set the viewport.\n"
84 " --scale sf : Scale drawing by sf.\n"
85 " --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
86 SkDebugf(
87 " --validate: Verify that the rendered image contains the same pixels as "
88 "the picture rendered in simple mode.\n"
89 " --maxComponentDiff: maximum diff on a component. Default is 256, "
90 "which means we report but we do not generate an error.\n"
91 " --writeWholeImage: In tile mode, write the entire rendered image to a "
92 "file, instead of an image for each tile.\n");
93 SkDebugf(
94 " --clone n: Clone the picture n times before rendering.\n");
95 SkDebugf(
96 " --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
97 " be used. Accepted values are: none, rtree, grid. Default\n"
98 " value is none. Not compatible with --pipe. With value\n"
99 " 'grid', width and height must be specified. 'grid' can\n"
100 " only be used with modes tile, record, and\n"
101 " playbackCreation.");
102 SkDebugf(
103 " --device bitmap"
104 #if SK_SUPPORT_GPU
105 " | gpu"
106 #endif
107 ": Use the corresponding device. Default is bitmap.\n");
108 SkDebugf(
109 " bitmap, Render to a bitmap.\n");
110 #if SK_SUPPORT_GPU
111 SkDebugf(
112 " gpu, Render to the GPU.\n");
113 #endif
114 }
115
make_output_filepath(SkString * path,const SkString & dir,const SkString & name)116 static void make_output_filepath(SkString* path, const SkString& dir,
117 const SkString& name) {
118 sk_tools::make_filepath(path, dir, name);
119 // Remove ".skp"
120 path->remove(path->size() - 4, 4);
121 }
122
render_picture(const SkString & inputPath,const SkString * outputDir,sk_tools::PictureRenderer & renderer,SkBitmap ** out,int clones)123 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
124 sk_tools::PictureRenderer& renderer,
125 SkBitmap** out,
126 int clones) {
127 SkString inputFilename;
128 sk_tools::get_basename(&inputFilename, inputPath);
129
130 SkFILEStream inputStream;
131 inputStream.setPath(inputPath.c_str());
132 if (!inputStream.isValid()) {
133 SkDebugf("Could not open file %s\n", inputPath.c_str());
134 return false;
135 }
136
137 bool success = false;
138 SkPicture* picture = SkNEW_ARGS(SkPicture,
139 (&inputStream, &success, &SkImageDecoder::DecodeStream));
140 if (!success) {
141 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
142 return false;
143 }
144
145 for (int i = 0; i < clones; ++i) {
146 SkPicture* clone = picture->clone();
147 SkDELETE(picture);
148 picture = clone;
149 }
150
151 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
152 inputPath.c_str());
153
154 renderer.init(picture);
155 renderer.setup();
156
157 SkString* outputPath = NULL;
158 if (NULL != outputDir) {
159 outputPath = SkNEW(SkString);
160 make_output_filepath(outputPath, *outputDir, inputFilename);
161 }
162
163 success = renderer.render(outputPath, out);
164 if (outputPath) {
165 if (!success) {
166 SkDebugf("Could not write to file %s\n", outputPath->c_str());
167 }
168 SkDELETE(outputPath);
169 }
170
171 renderer.end();
172
173 SkDELETE(picture);
174 return success;
175 }
176
getByte(uint32_t value,int index)177 static inline int getByte(uint32_t value, int index) {
178 SkASSERT(0 <= index && index < 4);
179 return (value >> (index * 8)) & 0xFF;
180 }
181
MaxByteDiff(uint32_t v1,uint32_t v2)182 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
183 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
184 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
185 }
186
render_picture(const SkString & inputPath,const SkString * outputDir,sk_tools::PictureRenderer & renderer,bool validate,int maxComponentDiff,bool writeWholeImage,int clones)187 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
188 sk_tools::PictureRenderer& renderer,
189 bool validate, int maxComponentDiff,
190 bool writeWholeImage,
191 int clones) {
192 int diffs[256] = {0};
193 SkBitmap* bitmap = NULL;
194 bool success = render_picture(inputPath,
195 writeWholeImage ? NULL : outputDir,
196 renderer,
197 validate || writeWholeImage ? &bitmap : NULL, clones);
198
199 if (!success || ((validate || writeWholeImage) && bitmap == NULL)) {
200 SkDebugf("Failed to draw the picture.\n");
201 SkDELETE(bitmap);
202 return false;
203 }
204
205 if (validate) {
206 SkBitmap* referenceBitmap = NULL;
207 sk_tools::SimplePictureRenderer referenceRenderer;
208 success = render_picture(inputPath, NULL, referenceRenderer,
209 &referenceBitmap, 0);
210
211 if (!success || !referenceBitmap) {
212 SkDebugf("Failed to draw the reference picture.\n");
213 SkDELETE(bitmap);
214 SkDELETE(referenceBitmap);
215 return false;
216 }
217
218 if (success && (bitmap->width() != referenceBitmap->width())) {
219 SkDebugf("Expected image width: %i, actual image width %i.\n",
220 referenceBitmap->width(), bitmap->width());
221 SkDELETE(bitmap);
222 SkDELETE(referenceBitmap);
223 return false;
224 }
225 if (success && (bitmap->height() != referenceBitmap->height())) {
226 SkDebugf("Expected image height: %i, actual image height %i",
227 referenceBitmap->height(), bitmap->height());
228 SkDELETE(bitmap);
229 SkDELETE(referenceBitmap);
230 return false;
231 }
232
233 for (int y = 0; success && y < bitmap->height(); y++) {
234 for (int x = 0; success && x < bitmap->width(); x++) {
235 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
236 *bitmap->getAddr32(x, y));
237 SkASSERT(diff >= 0 && diff <= 255);
238 diffs[diff]++;
239
240 if (diff > maxComponentDiff) {
241 SkDebugf("Expected pixel at (%i %i) exceedds maximum "
242 "component diff of %i: 0x%x, actual 0x%x\n",
243 x, y, maxComponentDiff,
244 *referenceBitmap->getAddr32(x, y),
245 *bitmap->getAddr32(x, y));
246 SkDELETE(bitmap);
247 SkDELETE(referenceBitmap);
248 return false;
249 }
250 }
251 }
252 SkDELETE(referenceBitmap);
253
254 for (int i = 1; i <= 255; ++i) {
255 if(diffs[i] > 0) {
256 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
257 }
258 }
259 }
260
261 if (writeWholeImage) {
262 sk_tools::force_all_opaque(*bitmap);
263 if (NULL != outputDir && writeWholeImage) {
264 SkString inputFilename;
265 sk_tools::get_basename(&inputFilename, inputPath);
266 SkString outputPath;
267 make_output_filepath(&outputPath, *outputDir, inputFilename);
268 outputPath.append(".png");
269 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap,
270 SkImageEncoder::kPNG_Type, 100)) {
271 SkDebugf("Failed to draw the picture.\n");
272 success = false;
273 }
274 }
275 }
276 SkDELETE(bitmap);
277
278 return success;
279 }
280
281
process_input(const SkString & input,const SkString * outputDir,sk_tools::PictureRenderer & renderer,bool validate,int maxComponentDiff,bool writeWholeImage,int clones)282 static int process_input(const SkString& input, const SkString* outputDir,
283 sk_tools::PictureRenderer& renderer,
284 bool validate, int maxComponentDiff,
285 bool writeWholeImage, int clones) {
286 SkOSFile::Iter iter(input.c_str(), "skp");
287 SkString inputFilename;
288 int failures = 0;
289 SkDebugf("process_input, %s\n", input.c_str());
290 if (iter.next(&inputFilename)) {
291 do {
292 SkString inputPath;
293 sk_tools::make_filepath(&inputPath, input, inputFilename);
294 if (!render_picture(inputPath, outputDir, renderer,
295 validate, maxComponentDiff,
296 writeWholeImage, clones)) {
297 ++failures;
298 }
299 } while(iter.next(&inputFilename));
300 } else if (SkStrEndsWith(input.c_str(), ".skp")) {
301 SkString inputPath(input);
302 if (!render_picture(inputPath, outputDir, renderer,
303 validate, maxComponentDiff,
304 writeWholeImage, clones)) {
305 ++failures;
306 }
307 } else {
308 SkString warning;
309 warning.printf("Warning: skipping %s\n", input.c_str());
310 SkDebugf(warning.c_str());
311 }
312 return failures;
313 }
314
parse_commandline(int argc,char * const argv[],SkTArray<SkString> * inputs,sk_tools::PictureRenderer * & renderer,SkString * & outputDir,bool * validate,int * maxComponentDiff,bool * writeWholeImage,int * clones)315 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
316 sk_tools::PictureRenderer*& renderer, SkString*& outputDir,
317 bool* validate, int* maxComponentDiff,
318 bool* writeWholeImage,
319 int* clones){
320 const char* argv0 = argv[0];
321 char* const* stop = argv + argc;
322
323 sk_tools::PictureRenderer::SkDeviceTypes deviceType =
324 sk_tools::PictureRenderer::kBitmap_DeviceType;
325
326 bool usePipe = false;
327 int numThreads = 1;
328 bool useTiles = false;
329 const char* widthString = NULL;
330 const char* heightString = NULL;
331 int gridWidth = 0;
332 int gridHeight = 0;
333 bool isPowerOf2Mode = false;
334 bool isCopyMode = false;
335 const char* xTilesString = NULL;
336 const char* yTilesString = NULL;
337 const char* mode = NULL;
338 bool gridSupported = false;
339 sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
340 sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
341 *validate = false;
342 *maxComponentDiff = 256;
343 *writeWholeImage = false;
344 *clones = 0;
345 SkISize viewport;
346 viewport.setEmpty();
347 SkScalar scaleFactor = SK_Scalar1;
348
349 for (++argv; argv < stop; ++argv) {
350 if (0 == strcmp(*argv, "--mode")) {
351 if (renderer != NULL) {
352 renderer->unref();
353 SkDebugf("Cannot combine modes.\n");
354 usage(argv0);
355 exit(-1);
356 }
357
358 ++argv;
359 if (argv >= stop) {
360 SkDebugf("Missing mode for --mode\n");
361 usage(argv0);
362 exit(-1);
363 }
364
365 if (0 == strcmp(*argv, "simple")) {
366 renderer = SkNEW(sk_tools::SimplePictureRenderer);
367 } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
368 || 0 == strcmp(*argv, "copyTile")) {
369 useTiles = true;
370 mode = *argv;
371
372 if (0 == strcmp(*argv, "pow2tile")) {
373 isPowerOf2Mode = true;
374 } else if (0 == strcmp(*argv, "copyTile")) {
375 isCopyMode = true;
376 } else {
377 gridSupported = true;
378 }
379
380 ++argv;
381 if (argv >= stop) {
382 SkDebugf("Missing width for --mode %s\n", mode);
383 usage(argv0);
384 exit(-1);
385 }
386
387 widthString = *argv;
388 ++argv;
389 if (argv >= stop) {
390 SkDebugf("Missing height for --mode %s\n", mode);
391 usage(argv0);
392 exit(-1);
393 }
394 heightString = *argv;
395 } else if (0 == strcmp(*argv, "rerecord")) {
396 renderer = SkNEW(sk_tools::RecordPictureRenderer);
397 } else {
398 SkDebugf("%s is not a valid mode for --mode\n", *argv);
399 usage(argv0);
400 exit(-1);
401 }
402 } else if (0 == strcmp(*argv, "--bbh")) {
403 ++argv;
404 if (argv >= stop) {
405 SkDebugf("Missing value for --bbh\n");
406 usage(argv0);
407 exit(-1);
408 }
409 if (0 == strcmp(*argv, "none")) {
410 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
411 } else if (0 == strcmp(*argv, "rtree")) {
412 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
413 } else if (0 == strcmp(*argv, "grid")) {
414 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
415 ++argv;
416 if (argv >= stop) {
417 SkDebugf("Missing width for --bbh grid\n");
418 usage(argv0);
419 exit(-1);
420 }
421 gridWidth = atoi(*argv);
422 ++argv;
423 if (argv >= stop) {
424 SkDebugf("Missing height for --bbh grid\n");
425 usage(argv0);
426 exit(-1);
427 }
428 gridHeight = atoi(*argv);
429 } else {
430 SkDebugf("%s is not a valid value for --bbhType\n", *argv);
431 usage(argv0);
432 exit(-1);;
433 }
434 } else if (0 == strcmp(*argv, "--viewport")) {
435 ++argv;
436 if (argv >= stop) {
437 SkDebugf("Missing width for --viewport\n");
438 usage(argv0);
439 exit(-1);
440 }
441 viewport.fWidth = atoi(*argv);
442 ++argv;
443 if (argv >= stop) {
444 SkDebugf("Missing height for --viewport\n");
445 usage(argv0);
446 exit(-1);
447 }
448 viewport.fHeight = atoi(*argv);
449 } else if (0 == strcmp(*argv, "--scale")) {
450 ++argv;
451 if (argv >= stop) {
452 SkDebugf("Missing scaleFactor for --scale\n");
453 usage(argv0);
454 exit(-1);
455 }
456 scaleFactor = SkDoubleToScalar(atof(*argv));
457 } else if (0 == strcmp(*argv, "--tiles")) {
458 ++argv;
459 if (argv >= stop) {
460 SkDebugf("Missing x for --tiles\n");
461 usage(argv0);
462 exit(-1);
463 }
464 xTilesString = *argv;
465 ++argv;
466 if (argv >= stop) {
467 SkDebugf("Missing y for --tiles\n");
468 usage(argv0);
469 exit(-1);
470 }
471 yTilesString = *argv;
472 } else if (0 == strcmp(*argv, "--pipe")) {
473 usePipe = true;
474 } else if (0 == strcmp(*argv, "--multi")) {
475 ++argv;
476 if (argv >= stop) {
477 SkSafeUnref(renderer);
478 SkDebugf("Missing arg for --multi\n");
479 usage(argv0);
480 exit(-1);
481 }
482 numThreads = atoi(*argv);
483 if (numThreads < 2) {
484 SkSafeUnref(renderer);
485 SkDebugf("Number of threads must be at least 2.\n");
486 usage(argv0);
487 exit(-1);
488 }
489 } else if (0 == strcmp(*argv, "--clone")) {
490 ++argv;
491 if (argv >= stop) {
492 SkSafeUnref(renderer);
493 SkDebugf("Missing arg for --clone\n");
494 usage(argv0);
495 exit(-1);
496 }
497 *clones = atoi(*argv);
498 if (*clones < 0) {
499 SkSafeUnref(renderer);
500 SkDebugf("Number of clones must be at least 0.\n");
501 usage(argv0);
502 exit(-1);
503 }
504 } else if (0 == strcmp(*argv, "--device")) {
505 ++argv;
506 if (argv >= stop) {
507 SkSafeUnref(renderer);
508 SkDebugf("Missing mode for --device\n");
509 usage(argv0);
510 exit(-1);
511 }
512
513 if (0 == strcmp(*argv, "bitmap")) {
514 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
515 }
516 #if SK_SUPPORT_GPU
517 else if (0 == strcmp(*argv, "gpu")) {
518 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
519 }
520 #endif
521 else {
522 SkSafeUnref(renderer);
523 SkDebugf("%s is not a valid mode for --device\n", *argv);
524 usage(argv0);
525 exit(-1);
526 }
527
528 } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
529 SkSafeUnref(renderer);
530 usage(argv0);
531 exit(-1);
532 } else if (0 == strcmp(*argv, "-w")) {
533 ++argv;
534 if (argv >= stop) {
535 SkDebugf("Missing output directory for -w\n");
536 usage(argv0);
537 exit(-1);
538 }
539 outputDir = SkNEW_ARGS(SkString, (*argv));
540 } else if (0 == strcmp(*argv, "--validate")) {
541 *validate = true;
542 } else if (0 == strcmp(*argv, "--maxComponentDiff")) {
543 if (!*validate) {
544 SkDebugf("--maxComponentDiff must be used only with --validate\n");
545 usage(argv0);
546 exit(-1);
547 }
548 ++argv;
549 if (argv >= stop) {
550 SkDebugf("Missing arg for --maxComponentDiff\n");
551 usage(argv0);
552 exit(-1);
553 }
554 *maxComponentDiff = atoi(*argv);
555 if (*maxComponentDiff < 0 || *maxComponentDiff > 256) {
556 SkSafeUnref(renderer);
557 SkDebugf("maxComponentDiff: 0 - 256.\n");
558 usage(argv0);
559 exit(-1);
560 }
561 } else if (0 == strcmp(*argv, "--writeWholeImage")) {
562 *writeWholeImage = true;
563 } else {
564 inputs->push_back(SkString(*argv));
565 }
566 }
567
568 if (numThreads > 1 && !useTiles) {
569 SkSafeUnref(renderer);
570 SkDebugf("Multithreaded drawing requires tiled rendering.\n");
571 usage(argv0);
572 exit(-1);
573 }
574
575 if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
576 SkDebugf("--pipe and --bbh cannot be used together\n");
577 usage(argv0);
578 exit(-1);
579 }
580
581 if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
582 !gridSupported) {
583 SkDebugf("'--bbh grid' is not compatible with specified --mode.\n");
584 usage(argv0);
585 exit(-1);
586 }
587
588 if (useTiles) {
589 SkASSERT(NULL == renderer);
590 sk_tools::TiledPictureRenderer* tiledRenderer;
591 if (isCopyMode) {
592 int x, y;
593 if (xTilesString != NULL) {
594 SkASSERT(yTilesString != NULL);
595 x = atoi(xTilesString);
596 y = atoi(yTilesString);
597 if (x <= 0 || y <= 0) {
598 SkDebugf("--tiles must be given values > 0\n");
599 usage(argv0);
600 exit(-1);
601 }
602 } else {
603 x = y = 4;
604 }
605 tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
606 } else if (numThreads > 1) {
607 tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
608 } else {
609 tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
610 }
611 if (isPowerOf2Mode) {
612 int minWidth = atoi(widthString);
613 if (!SkIsPow2(minWidth) || minWidth < 0) {
614 tiledRenderer->unref();
615 SkString err;
616 err.printf("-mode %s must be given a width"
617 " value that is a power of two\n", mode);
618 SkDebugf(err.c_str());
619 usage(argv0);
620 exit(-1);
621 }
622 tiledRenderer->setTileMinPowerOf2Width(minWidth);
623 } else if (sk_tools::is_percentage(widthString)) {
624 if (isCopyMode) {
625 tiledRenderer->unref();
626 SkString err;
627 err.printf("--mode %s does not support percentages.\n", mode);
628 SkDebugf(err.c_str());
629 usage(argv0);
630 exit(-1);
631 }
632 tiledRenderer->setTileWidthPercentage(atof(widthString));
633 if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
634 tiledRenderer->unref();
635 SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
636 usage(argv0);
637 exit(-1);
638 }
639 } else {
640 tiledRenderer->setTileWidth(atoi(widthString));
641 if (!(tiledRenderer->getTileWidth() > 0)) {
642 tiledRenderer->unref();
643 SkDebugf("--mode %s must be given a width > 0\n", mode);
644 usage(argv0);
645 exit(-1);
646 }
647 }
648
649 if (sk_tools::is_percentage(heightString)) {
650 if (isCopyMode) {
651 tiledRenderer->unref();
652 SkString err;
653 err.printf("--mode %s does not support percentages.\n", mode);
654 SkDebugf(err.c_str());
655 usage(argv0);
656 exit(-1);
657 }
658 tiledRenderer->setTileHeightPercentage(atof(heightString));
659 if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
660 tiledRenderer->unref();
661 SkDebugf("--mode %s must be given a height percentage > 0\n", mode);
662 usage(argv0);
663 exit(-1);
664 }
665 } else {
666 tiledRenderer->setTileHeight(atoi(heightString));
667 if (!(tiledRenderer->getTileHeight() > 0)) {
668 tiledRenderer->unref();
669 SkDebugf("--mode %s must be given a height > 0\n", mode);
670 usage(argv0);
671 exit(-1);
672 }
673 }
674 if (numThreads > 1) {
675 #if SK_SUPPORT_GPU
676 if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
677 tiledRenderer->unref();
678 SkDebugf("GPU not compatible with multithreaded tiling.\n");
679 usage(argv0);
680 exit(-1);
681 }
682 #endif
683 }
684 renderer = tiledRenderer;
685 if (usePipe) {
686 SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
687 "Turning off pipe.\n");
688 }
689 } else if (usePipe) {
690 if (renderer != NULL) {
691 renderer->unref();
692 SkDebugf("Pipe is incompatible with other modes.\n");
693 usage(argv0);
694 exit(-1);
695 }
696 renderer = SkNEW(sk_tools::PipePictureRenderer);
697 }
698
699 if (inputs->empty()) {
700 SkSafeUnref(renderer);
701 if (NULL != outputDir) {
702 SkDELETE(outputDir);
703 }
704 usage(argv0);
705 exit(-1);
706 }
707
708 if (NULL == renderer) {
709 renderer = SkNEW(sk_tools::SimplePictureRenderer);
710 }
711
712 renderer->setBBoxHierarchyType(bbhType);
713 renderer->setGridSize(gridWidth, gridHeight);
714 renderer->setViewport(viewport);
715 renderer->setScaleFactor(scaleFactor);
716 renderer->setDeviceType(deviceType);
717 }
718
719 int tool_main(int argc, char** argv);
tool_main(int argc,char ** argv)720 int tool_main(int argc, char** argv) {
721 SkAutoGraphics ag;
722 SkTArray<SkString> inputs;
723 sk_tools::PictureRenderer* renderer = NULL;
724 SkString* outputDir = NULL;
725 bool validate = false;
726 int maxComponentDiff = 256;
727 bool writeWholeImage = false;
728 int clones = 0;
729 parse_commandline(argc, argv, &inputs, renderer, outputDir,
730 &validate, &maxComponentDiff, &writeWholeImage, &clones);
731 SkASSERT(renderer);
732
733 int failures = 0;
734 for (int i = 0; i < inputs.count(); i ++) {
735 failures += process_input(inputs[i], outputDir, *renderer,
736 validate, maxComponentDiff,
737 writeWholeImage, clones);
738 }
739 if (failures != 0) {
740 SkDebugf("Failed to render %i pictures.\n", failures);
741 return 1;
742 }
743 #if SK_SUPPORT_GPU
744 #if GR_CACHE_STATS
745 if (renderer->isUsingGpuDevice()) {
746 GrContext* ctx = renderer->getGrContext();
747
748 ctx->printCacheStats();
749 }
750 #endif
751 #endif
752 if (NULL != outputDir) {
753 SkDELETE(outputDir);
754 }
755 SkDELETE(renderer);
756 return 0;
757 }
758
759 #if !defined SK_BUILD_FOR_IOS
main(int argc,char * const argv[])760 int main(int argc, char * const argv[]) {
761 return tool_main(argc, (char**) argv);
762 }
763 #endif
764