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 "LazyDecodeBitmap.h"
9 #include "CopyTilesRenderer.h"
10 #include "SkBitmap.h"
11 #include "SkDevice.h"
12 #include "SkCommandLineFlags.h"
13 #include "SkGraphics.h"
14 #include "SkImageDecoder.h"
15 #include "SkImageEncoder.h"
16 #include "SkMath.h"
17 #include "SkOSFile.h"
18 #include "SkPicture.h"
19 #include "SkStream.h"
20 #include "SkString.h"
21 #include "PictureRenderer.h"
22 #include "PictureRenderingFlags.h"
23 #include "picture_utils.h"
24
25 // Flags used by this file, alphabetically:
26 DEFINE_int32(clone, 0, "Clone the picture n times before rendering.");
27 DECLARE_bool(deferImageDecoding);
28 DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
29 "by more than this amount are considered errors, though all diffs are reported. "
30 "Requires --validate.");
31 DECLARE_string(readPath);
32 DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a "
33 "file rather than decoding it. Requires writePath to be set. Skips drawing the full "
34 "skp to a file. Not compatible with deferImageDecoding.");
35 DEFINE_string(writeJsonSummaryPath, "", "File to write a JSON summary of image results to. "
36 "TODO(epoger): Currently, this only works if --writePath is also specified.");
37 DEFINE_string2(writePath, w, "", "Directory to write the rendered images.");
38 DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a "
39 "file, instead of an image for each tile.");
40 DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as "
41 "the picture rendered in simple mode. When used in conjunction with --bbh, results "
42 "are validated against the picture rendered in the same mode, but without the bbh.");
43
44 DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture.");
45
make_output_filepath(SkString * path,const SkString & dir,const SkString & name)46 static void make_output_filepath(SkString* path, const SkString& dir,
47 const SkString& name) {
48 sk_tools::make_filepath(path, dir, name);
49 // Remove ".skp"
50 path->remove(path->size() - 4, 4);
51 }
52
53 ////////////////////////////////////////////////////////////////////////////////////////////////////
54
55 /**
56 * Table for translating from format of data to a suffix.
57 */
58 struct Format {
59 SkImageDecoder::Format fFormat;
60 const char* fSuffix;
61 };
62 static const Format gFormats[] = {
63 { SkImageDecoder::kBMP_Format, ".bmp" },
64 { SkImageDecoder::kGIF_Format, ".gif" },
65 { SkImageDecoder::kICO_Format, ".ico" },
66 { SkImageDecoder::kJPEG_Format, ".jpg" },
67 { SkImageDecoder::kPNG_Format, ".png" },
68 { SkImageDecoder::kWBMP_Format, ".wbmp" },
69 { SkImageDecoder::kWEBP_Format, ".webp" },
70 { SkImageDecoder::kUnknown_Format, "" },
71 };
72
73 /**
74 * Get an appropriate suffix for an image format.
75 */
get_suffix_from_format(SkImageDecoder::Format format)76 static const char* get_suffix_from_format(SkImageDecoder::Format format) {
77 for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) {
78 if (gFormats[i].fFormat == format) {
79 return gFormats[i].fSuffix;
80 }
81 }
82 return "";
83 }
84
85 /**
86 * Base name for an image file created from the encoded data in an skp.
87 */
88 static SkString gInputFileName;
89
90 /**
91 * Number to be appended to the image file name so that it is unique.
92 */
93 static uint32_t gImageNo;
94
95 /**
96 * Set up the name for writing encoded data to a file.
97 * Sets gInputFileName to name, minus any extension ".*"
98 * Sets gImageNo to 0, so images from file "X.skp" will
99 * look like "X_<gImageNo>.<suffix>", beginning with 0
100 * for each new skp.
101 */
reset_image_file_base_name(const SkString & name)102 static void reset_image_file_base_name(const SkString& name) {
103 gImageNo = 0;
104 // Remove ".skp"
105 const char* cName = name.c_str();
106 const char* dot = strrchr(cName, '.');
107 if (dot != NULL) {
108 gInputFileName.set(cName, dot - cName);
109 } else {
110 gInputFileName.set(name);
111 }
112 }
113
114 /**
115 * Write the raw encoded bitmap data to a file.
116 */
write_image_to_file(const void * buffer,size_t size,SkBitmap * bitmap)117 static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) {
118 SkASSERT(!FLAGS_writePath.isEmpty());
119 SkMemoryStream memStream(buffer, size);
120 SkString outPath;
121 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream);
122 SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++,
123 get_suffix_from_format(format));
124 SkString dir(FLAGS_writePath[0]);
125 sk_tools::make_filepath(&outPath, dir, name);
126 SkFILEWStream fileStream(outPath.c_str());
127 if (!(fileStream.isValid() && fileStream.write(buffer, size))) {
128 SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str());
129 }
130 // Put in a dummy bitmap.
131 return SkImageDecoder::DecodeStream(&memStream, bitmap, SkBitmap::kNo_Config,
132 SkImageDecoder::kDecodeBounds_Mode);
133 }
134
135 ////////////////////////////////////////////////////////////////////////////////////////////////////
136
137 /**
138 * Called only by render_picture().
139 */
render_picture_internal(const SkString & inputPath,const SkString * outputDir,sk_tools::PictureRenderer & renderer,SkBitmap ** out)140 static bool render_picture_internal(const SkString& inputPath, const SkString* outputDir,
141 sk_tools::PictureRenderer& renderer,
142 SkBitmap** out) {
143 SkString inputFilename;
144 sk_tools::get_basename(&inputFilename, inputPath);
145
146 SkFILEStream inputStream;
147 inputStream.setPath(inputPath.c_str());
148 if (!inputStream.isValid()) {
149 SkDebugf("Could not open file %s\n", inputPath.c_str());
150 return false;
151 }
152
153 SkPicture::InstallPixelRefProc proc;
154 if (FLAGS_deferImageDecoding) {
155 proc = &sk_tools::LazyDecodeBitmap;
156 } else if (FLAGS_writeEncodedImages) {
157 SkASSERT(!FLAGS_writePath.isEmpty());
158 reset_image_file_base_name(inputFilename);
159 proc = &write_image_to_file;
160 } else {
161 proc = &SkImageDecoder::DecodeMemory;
162 }
163
164 SkDebugf("deserializing... %s\n", inputPath.c_str());
165
166 SkPicture* picture = SkPicture::CreateFromStream(&inputStream, proc);
167
168 if (NULL == picture) {
169 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
170 return false;
171 }
172
173 while (FLAGS_bench_record) {
174 const int kRecordFlags = 0;
175 SkPicture other;
176 picture->draw(other.beginRecording(picture->width(), picture->height(), kRecordFlags));
177 other.endRecording();
178 }
179
180 for (int i = 0; i < FLAGS_clone; ++i) {
181 SkPicture* clone = picture->clone();
182 SkDELETE(picture);
183 picture = clone;
184 }
185
186 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
187 inputPath.c_str());
188
189 renderer.init(picture);
190 renderer.setup();
191
192 SkString* outputPath = NULL;
193 if (NULL != outputDir && outputDir->size() > 0 && !FLAGS_writeEncodedImages) {
194 outputPath = SkNEW(SkString);
195 make_output_filepath(outputPath, *outputDir, inputFilename);
196 }
197
198 bool success = renderer.render(outputPath, out);
199 if (outputPath) {
200 if (!success) {
201 SkDebugf("Could not write to file %s\n", outputPath->c_str());
202 }
203 SkDELETE(outputPath);
204 }
205
206 renderer.end();
207
208 SkDELETE(picture);
209 return success;
210 }
211
getByte(uint32_t value,int index)212 static inline int getByte(uint32_t value, int index) {
213 SkASSERT(0 <= index && index < 4);
214 return (value >> (index * 8)) & 0xFF;
215 }
216
MaxByteDiff(uint32_t v1,uint32_t v2)217 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
218 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
219 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
220 }
221
222 namespace {
223 class AutoRestoreBbhType {
224 public:
AutoRestoreBbhType()225 AutoRestoreBbhType() {
226 fRenderer = NULL;
227 fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
228 }
229
set(sk_tools::PictureRenderer * renderer,sk_tools::PictureRenderer::BBoxHierarchyType bbhType)230 void set(sk_tools::PictureRenderer* renderer,
231 sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
232 fRenderer = renderer;
233 fSavedBbhType = renderer->getBBoxHierarchyType();
234 renderer->setBBoxHierarchyType(bbhType);
235 }
236
~AutoRestoreBbhType()237 ~AutoRestoreBbhType() {
238 if (NULL != fRenderer) {
239 fRenderer->setBBoxHierarchyType(fSavedBbhType);
240 }
241 }
242
243 private:
244 sk_tools::PictureRenderer* fRenderer;
245 sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
246 };
247 }
248
249 /**
250 * Render the SKP file(s) within inputPath, writing their bitmap images into outputDir.
251 *
252 * @param inputPath path to an individual SKP file, or a directory of SKP files
253 * @param outputDir if not NULL, write the image(s) generated into this directory
254 * @param renderer PictureRenderer to use to render the SKPs
255 * @param jsonSummaryPtr if not NULL, add the image(s) generated to this summary
256 */
render_picture(const SkString & inputPath,const SkString * outputDir,sk_tools::PictureRenderer & renderer,sk_tools::ImageResultsSummary * jsonSummaryPtr)257 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
258 sk_tools::PictureRenderer& renderer,
259 sk_tools::ImageResultsSummary *jsonSummaryPtr) {
260 int diffs[256] = {0};
261 SkBitmap* bitmap = NULL;
262 renderer.setJsonSummaryPtr(jsonSummaryPtr);
263 bool success = render_picture_internal(inputPath,
264 FLAGS_writeWholeImage ? NULL : outputDir,
265 renderer,
266 FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL);
267
268 if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) {
269 SkDebugf("Failed to draw the picture.\n");
270 SkDELETE(bitmap);
271 return false;
272 }
273
274 if (FLAGS_validate) {
275 SkBitmap* referenceBitmap = NULL;
276 sk_tools::PictureRenderer* referenceRenderer;
277 // If the renderer uses a BBoxHierarchy, then the reference renderer
278 // will be the same renderer, without the bbh.
279 AutoRestoreBbhType arbbh;
280 if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
281 renderer.getBBoxHierarchyType()) {
282 referenceRenderer = &renderer;
283 referenceRenderer->ref(); // to match auto unref below
284 arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
285 } else {
286 referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer);
287 }
288 SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
289
290 success = render_picture_internal(inputPath, NULL, *referenceRenderer,
291 &referenceBitmap);
292
293 if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) {
294 SkDebugf("Failed to draw the reference picture.\n");
295 SkDELETE(bitmap);
296 SkDELETE(referenceBitmap);
297 return false;
298 }
299
300 if (success && (bitmap->width() != referenceBitmap->width())) {
301 SkDebugf("Expected image width: %i, actual image width %i.\n",
302 referenceBitmap->width(), bitmap->width());
303 SkDELETE(bitmap);
304 SkDELETE(referenceBitmap);
305 return false;
306 }
307 if (success && (bitmap->height() != referenceBitmap->height())) {
308 SkDebugf("Expected image height: %i, actual image height %i",
309 referenceBitmap->height(), bitmap->height());
310 SkDELETE(bitmap);
311 SkDELETE(referenceBitmap);
312 return false;
313 }
314
315 for (int y = 0; success && y < bitmap->height(); y++) {
316 for (int x = 0; success && x < bitmap->width(); x++) {
317 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
318 *bitmap->getAddr32(x, y));
319 SkASSERT(diff >= 0 && diff <= 255);
320 diffs[diff]++;
321
322 if (diff > FLAGS_maxComponentDiff) {
323 SkDebugf("Expected pixel at (%i %i) exceedds maximum "
324 "component diff of %i: 0x%x, actual 0x%x\n",
325 x, y, FLAGS_maxComponentDiff,
326 *referenceBitmap->getAddr32(x, y),
327 *bitmap->getAddr32(x, y));
328 SkDELETE(bitmap);
329 SkDELETE(referenceBitmap);
330 return false;
331 }
332 }
333 }
334 SkDELETE(referenceBitmap);
335
336 for (int i = 1; i <= 255; ++i) {
337 if(diffs[i] > 0) {
338 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
339 }
340 }
341 }
342
343 if (FLAGS_writeWholeImage) {
344 sk_tools::force_all_opaque(*bitmap);
345
346 if (NULL != jsonSummaryPtr) {
347 // EPOGER: This is a hacky way of constructing the filename associated with the
348 // image checksum; we basically are repeating the logic of make_output_filepath()
349 // and code below here, within here.
350 // It would be better for the filename (without outputDir) to be passed in here,
351 // and used both for the checksum file and writing into outputDir.
352 //
353 // EPOGER: what about including the config type within hashFilename? That way,
354 // we could combine results of different config types without conflicting filenames.
355 SkString hashFilename;
356 sk_tools::get_basename(&hashFilename, inputPath);
357 hashFilename.remove(hashFilename.size() - 4, 4); // Remove ".skp"
358 hashFilename.append(".png");
359 jsonSummaryPtr->add(hashFilename.c_str(), *bitmap);
360 }
361
362 if (NULL != outputDir) {
363 SkString inputFilename;
364 sk_tools::get_basename(&inputFilename, inputPath);
365 SkString outputPath;
366 make_output_filepath(&outputPath, *outputDir, inputFilename);
367 outputPath.append(".png");
368 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap,
369 SkImageEncoder::kPNG_Type, 100)) {
370 SkDebugf("Failed to draw the picture.\n");
371 success = false;
372 }
373 }
374 }
375 SkDELETE(bitmap);
376
377 return success;
378 }
379
380
process_input(const char * input,const SkString * outputDir,sk_tools::PictureRenderer & renderer,sk_tools::ImageResultsSummary * jsonSummaryPtr)381 static int process_input(const char* input, const SkString* outputDir,
382 sk_tools::PictureRenderer& renderer,
383 sk_tools::ImageResultsSummary *jsonSummaryPtr) {
384 SkOSFile::Iter iter(input, "skp");
385 SkString inputFilename;
386 int failures = 0;
387 SkDebugf("process_input, %s\n", input);
388 if (iter.next(&inputFilename)) {
389 do {
390 SkString inputPath;
391 SkString inputAsSkString(input);
392 sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename);
393 if (!render_picture(inputPath, outputDir, renderer, jsonSummaryPtr)) {
394 ++failures;
395 }
396 } while(iter.next(&inputFilename));
397 } else if (SkStrEndsWith(input, ".skp")) {
398 SkString inputPath(input);
399 if (!render_picture(inputPath, outputDir, renderer, jsonSummaryPtr)) {
400 ++failures;
401 }
402 } else {
403 SkString warning;
404 warning.printf("Warning: skipping %s\n", input);
405 SkDebugf(warning.c_str());
406 }
407 return failures;
408 }
409
410 int tool_main(int argc, char** argv);
tool_main(int argc,char ** argv)411 int tool_main(int argc, char** argv) {
412 SkCommandLineFlags::SetUsage("Render .skp files.");
413 SkCommandLineFlags::Parse(argc, argv);
414
415 if (FLAGS_readPath.isEmpty()) {
416 SkDebugf(".skp files or directories are required.\n");
417 exit(-1);
418 }
419
420 if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
421 SkDebugf("--maxComponentDiff must be between 0 and 256\n");
422 exit(-1);
423 }
424
425 if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
426 SkDebugf("--maxComponentDiff requires --validate\n");
427 exit(-1);
428 }
429
430 if (FLAGS_clone < 0) {
431 SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone);
432 exit(-1);
433 }
434
435 if (FLAGS_writeEncodedImages) {
436 if (FLAGS_writePath.isEmpty()) {
437 SkDebugf("--writeEncodedImages requires --writePath\n");
438 exit(-1);
439 }
440 if (FLAGS_deferImageDecoding) {
441 SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n");
442 exit(-1);
443 }
444 }
445
446 SkString errorString;
447 SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
448 kRender_PictureTool));
449 if (errorString.size() > 0) {
450 SkDebugf("%s\n", errorString.c_str());
451 }
452
453 if (renderer.get() == NULL) {
454 exit(-1);
455 }
456
457 SkAutoGraphics ag;
458
459 SkString outputDir;
460 if (FLAGS_writePath.count() == 1) {
461 outputDir.set(FLAGS_writePath[0]);
462 }
463 sk_tools::ImageResultsSummary jsonSummary;
464 sk_tools::ImageResultsSummary* jsonSummaryPtr = NULL;
465 if (FLAGS_writeJsonSummaryPath.count() == 1) {
466 jsonSummaryPtr = &jsonSummary;
467 }
468
469 int failures = 0;
470 for (int i = 0; i < FLAGS_readPath.count(); i ++) {
471 failures += process_input(FLAGS_readPath[i], &outputDir, *renderer.get(), jsonSummaryPtr);
472 }
473 if (failures != 0) {
474 SkDebugf("Failed to render %i pictures.\n", failures);
475 return 1;
476 }
477 #if SK_SUPPORT_GPU
478 #if GR_CACHE_STATS
479 if (renderer->isUsingGpuDevice()) {
480 GrContext* ctx = renderer->getGrContext();
481 ctx->printCacheStats();
482 #ifdef SK_DEVELOPER
483 ctx->dumpFontCache();
484 #endif
485 }
486 #endif
487 #endif
488 if (FLAGS_writeJsonSummaryPath.count() == 1) {
489 jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]);
490 }
491 return 0;
492 }
493
494 #if !defined SK_BUILD_FOR_IOS
main(int argc,char * const argv[])495 int main(int argc, char * const argv[]) {
496 return tool_main(argc, (char**) argv);
497 }
498 #endif
499