2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "CopyTilesRenderer.h"
10 #include "SkBitmapFactory.h"
14 #include "SkGraphics.h"
15 #include "SkImageDecoder.h"
16 #include "SkImageEncoder.h"
19 #include "SkPicture.h"
23 #include "PictureRenderer.h"
24 #include "PictureRenderingFlags.h"
25 #include "picture_utils.h"
27 // Flags used by this file, alphabetically:
28 DEFINE_int32(clone, 0, "Clone the picture n times before rendering.");
29 DECLARE_bool(deferImageDecoding);
30 DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ "
31 "by more than this amount are considered errors, though all diffs are reported. "
32 "Requires --validate.");
34 DEFINE_string(w, "", "Directory to write the rendered images.");
35 DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a "
36 "file, instead of an image for each tile.");
37 DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as "
38 "the picture rendered in simple mode. When used in conjunction with --bbh, results "
39 "are validated against the picture rendered in the same mode, but without the bbh.");
41 static void make_output_filepath(SkString* path, const SkString& dir,
42 const SkString& name) {
43 sk_tools::make_filepath(path, dir, name);
45 path->remove(path->size() - 4, 4);
49 #include "SkLruImageCache.h"
51 static SkLruImageCache gLruImageCache(1024*1024);
53 #ifdef SK_BUILD_FOR_ANDROID
54 #include "SkAshmemImageCache.h"
57 static SkImageCache* cache_selector(const SkImage::Info& info) {
58 if (info.fWidth * info.fHeight > 32 * 1024) {
59 return SkAshmemImageCache::GetAshmemImageCache();
61 return &gLruImageCache;
66 static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) {
67 void* copiedBuffer = sk_malloc_throw(size);
68 memcpy(copiedBuffer, buffer, size);
69 SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size));
70 SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget);
71 #ifdef SK_BUILD_FOR_ANDROID
72 factory.setCacheSelector(&cache_selector);
74 factory.setImageCache(&gLruImageCache);
76 return factory.installPixelRef(data, bitmap);
79 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
80 sk_tools::PictureRenderer& renderer,
82 SkString inputFilename;
83 sk_tools::get_basename(&inputFilename, inputPath);
85 SkFILEStream inputStream;
86 inputStream.setPath(inputPath.c_str());
87 if (!inputStream.isValid()) {
88 SkDebugf("Could not open file %s\n", inputPath.c_str());
94 if (FLAGS_deferImageDecoding) {
95 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap));
97 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory));
100 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
104 for (int i = 0; i < FLAGS_clone; ++i) {
105 SkPicture* clone = picture->clone();
110 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
113 renderer.init(picture);
116 SkString* outputPath = NULL;
117 if (NULL != outputDir && outputDir->size() > 0) {
118 outputPath = SkNEW(SkString);
119 make_output_filepath(outputPath, *outputDir, inputFilename);
122 success = renderer.render(outputPath, out);
125 SkDebugf("Could not write to file %s\n", outputPath->c_str());
127 SkDELETE(outputPath);
136 static inline int getByte(uint32_t value, int index) {
137 SkASSERT(0 <= index && index < 4);
138 return (value >> (index * 8)) & 0xFF;
141 static int MaxByteDiff(uint32_t v1, uint32_t v2) {
142 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
143 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
147 class AutoRestoreBbhType {
149 AutoRestoreBbhType() {
151 fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
154 void set(sk_tools::PictureRenderer* renderer,
155 sk_tools::PictureRenderer::BBoxHierarchyType bbhType) {
156 fRenderer = renderer;
157 fSavedBbhType = renderer->getBBoxHierarchyType();
158 renderer->setBBoxHierarchyType(bbhType);
161 ~AutoRestoreBbhType() {
162 if (NULL != fRenderer) {
163 fRenderer->setBBoxHierarchyType(fSavedBbhType);
168 sk_tools::PictureRenderer* fRenderer;
169 sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType;
173 static bool render_picture(const SkString& inputPath, const SkString* outputDir,
174 sk_tools::PictureRenderer& renderer) {
175 int diffs[256] = {0};
176 SkBitmap* bitmap = NULL;
177 bool success = render_picture(inputPath,
178 FLAGS_writeWholeImage ? NULL : outputDir,
180 FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL);
182 if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) {
183 SkDebugf("Failed to draw the picture.\n");
188 if (FLAGS_validate) {
189 SkBitmap* referenceBitmap = NULL;
190 sk_tools::PictureRenderer* referenceRenderer;
191 // If the renderer uses a BBoxHierarchy, then the reference renderer
192 // will be the same renderer, without the bbh.
193 AutoRestoreBbhType arbbh;
194 if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType !=
195 renderer.getBBoxHierarchyType()) {
196 referenceRenderer = &renderer;
197 referenceRenderer->ref(); // to match auto unref below
198 arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
200 referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer);
202 SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer);
204 success = render_picture(inputPath, NULL, *referenceRenderer,
207 if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) {
208 SkDebugf("Failed to draw the reference picture.\n");
210 SkDELETE(referenceBitmap);
214 if (success && (bitmap->width() != referenceBitmap->width())) {
215 SkDebugf("Expected image width: %i, actual image width %i.\n",
216 referenceBitmap->width(), bitmap->width());
218 SkDELETE(referenceBitmap);
221 if (success && (bitmap->height() != referenceBitmap->height())) {
222 SkDebugf("Expected image height: %i, actual image height %i",
223 referenceBitmap->height(), bitmap->height());
225 SkDELETE(referenceBitmap);
229 for (int y = 0; success && y < bitmap->height(); y++) {
230 for (int x = 0; success && x < bitmap->width(); x++) {
231 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
232 *bitmap->getAddr32(x, y));
233 SkASSERT(diff >= 0 && diff <= 255);
236 if (diff > FLAGS_maxComponentDiff) {
237 SkDebugf("Expected pixel at (%i %i) exceedds maximum "
238 "component diff of %i: 0x%x, actual 0x%x\n",
239 x, y, FLAGS_maxComponentDiff,
240 *referenceBitmap->getAddr32(x, y),
241 *bitmap->getAddr32(x, y));
243 SkDELETE(referenceBitmap);
248 SkDELETE(referenceBitmap);
250 for (int i = 1; i <= 255; ++i) {
252 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
257 if (FLAGS_writeWholeImage) {
258 sk_tools::force_all_opaque(*bitmap);
259 if (NULL != outputDir && FLAGS_writeWholeImage) {
260 SkString inputFilename;
261 sk_tools::get_basename(&inputFilename, inputPath);
263 make_output_filepath(&outputPath, *outputDir, inputFilename);
264 outputPath.append(".png");
265 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap,
266 SkImageEncoder::kPNG_Type, 100)) {
267 SkDebugf("Failed to draw the picture.\n");
278 static int process_input(const char* input, const SkString* outputDir,
279 sk_tools::PictureRenderer& renderer) {
280 SkOSFile::Iter iter(input, "skp");
281 SkString inputFilename;
283 SkDebugf("process_input, %s\n", input);
284 if (iter.next(&inputFilename)) {
287 SkString inputAsSkString(input);
288 sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename);
289 if (!render_picture(inputPath, outputDir, renderer)) {
292 } while(iter.next(&inputFilename));
293 } else if (SkStrEndsWith(input, ".skp")) {
294 SkString inputPath(input);
295 if (!render_picture(inputPath, outputDir, renderer)) {
300 warning.printf("Warning: skipping %s\n", input);
301 SkDebugf(warning.c_str());
306 int tool_main(int argc, char** argv);
307 int tool_main(int argc, char** argv) {
308 SkFlags::SetUsage("Render .skp files.");
309 SkFlags::ParseCommandLine(argc, argv);
311 if (FLAGS_r.isEmpty()) {
312 SkDebugf(".skp files or directories are required.\n");
316 if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) {
317 SkDebugf("--maxComponentDiff must be between 0 and 256\n");
321 if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) {
322 SkDebugf("--maxComponentDiff requires --validate\n");
326 if (FLAGS_clone < 0) {
327 SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone);
331 SkString errorString;
332 SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
333 kRender_PictureTool));
334 if (errorString.size() > 0) {
335 SkDebugf("%s\n", errorString.c_str());
338 if (renderer.get() == NULL) {
345 if (FLAGS_w.count() == 1) {
346 outputDir.set(FLAGS_w[0]);
350 for (int i = 0; i < FLAGS_r.count(); i ++) {
351 failures += process_input(FLAGS_r[i], &outputDir, *renderer.get());
354 SkDebugf("Failed to render %i pictures.\n", failures);
359 if (renderer->isUsingGpuDevice()) {
360 GrContext* ctx = renderer->getGrContext();
362 ctx->printCacheStats();
369 #if !defined SK_BUILD_FOR_IOS
370 int main(int argc, char * const argv[]) {
371 return tool_main(argc, (char**) argv);