render_pictures: add --mismatchPath flag

When set, it will only write out images that don't match expectations.

BUG=skia:1942
R=rmistry@google.com

Author: epoger@google.com

Review URL: https://codereview.chromium.org/283123002

git-svn-id: http://skia.googlecode.com/svn/trunk@14748 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 7456700..12f0afa 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -48,9 +48,10 @@
     kDefaultTileHeight = 256
 };
 
-void PictureRenderer::init(SkPicture* pict, const SkString* outputDir,
+void PictureRenderer::init(SkPicture* pict, const SkString* writePath, const SkString* mismatchPath,
                            const SkString* inputFilename, bool useChecksumBasedFilenames) {
-    this->CopyString(&fOutputDir, outputDir);
+    this->CopyString(&fWritePath, writePath);
+    this->CopyString(&fMismatchPath, mismatchPath);
     this->CopyString(&fInputFilename, inputFilename);
     fUseChecksumBasedFilenames = useChecksumBasedFilenames;
 
@@ -265,8 +266,9 @@
  * Write the canvas to an image file and/or JSON summary.
  *
  * @param canvas Must be non-null. Canvas to be written to a file.
- * @param outputDir If nonempty, write the binary image to a file within this directory;
- *     if empty, don't write out the image at all.
+ * @param writePath If nonempty, write the binary image to a file within this directory.
+ * @param mismatchPath If nonempty, write the binary image to a file within this directory,
+ *     but only if the image does not match expectations.
  * @param inputFilename If we are writing out a binary image, use this to build its filename.
  * @param jsonSummaryPtr If not null, add image results (checksum) to this summary.
  * @param useChecksumBasedFilenames If true, use checksum-based filenames when writing to disk.
@@ -274,9 +276,9 @@
  *
  * @return bool True if the operation completed successfully.
  */
-static bool write(SkCanvas* canvas, const SkString& outputDir, const SkString& inputFilename,
-                  ImageResultsAndExpectations *jsonSummaryPtr, bool useChecksumBasedFilenames,
-                  const int* tileNumberPtr=NULL) {
+static bool write(SkCanvas* canvas, const SkString& writePath, const SkString& mismatchPath,
+                  const SkString& inputFilename, ImageResultsAndExpectations *jsonSummaryPtr,
+                  bool useChecksumBasedFilenames, const int* tileNumberPtr=NULL) {
     SkASSERT(canvas != NULL);
     if (NULL == canvas) {
         return false;
@@ -325,21 +327,20 @@
 
         jsonSummaryPtr->add(inputFilename.c_str(), outputRelativePath.c_str(),
                             *imageDigestPtr, tileNumberPtr);
+        if (!mismatchPath.isEmpty() &&
+            !jsonSummaryPtr->matchesExpectation(inputFilename.c_str(), *imageDigestPtr,
+                                                tileNumberPtr)) {
+            if (!write_bitmap_to_disk(bitmap, mismatchPath, outputSubdirPtr, outputFilename)) {
+                return false;
+            }
+        }
     }
 
-    if (outputDir.isEmpty()) {
+    if (writePath.isEmpty()) {
         return true;
-    }
-
-    SkString dirPath;
-    if (outputSubdirPtr) {
-        dirPath = SkOSPath::SkPathJoin(outputDir.c_str(), outputSubdirPtr);
-        sk_mkdir(dirPath.c_str());
     } else {
-        dirPath.set(outputDir);
+        return write_bitmap_to_disk(bitmap, writePath, outputSubdirPtr, outputFilename);
     }
-    SkString fullPath = SkOSPath::SkPathJoin(dirPath.c_str(), outputFilename.c_str());
-    return SkImageEncoder::EncodeFile(fullPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -363,9 +364,9 @@
     this->scaleToScaleFactor(canvas);
     fPicture->draw(canvas);
     SkAutoTUnref<SkPicture> picture(recorder.endRecording());
-    if (!fOutputDir.isEmpty()) {
+    if (!fWritePath.isEmpty()) {
         // Record the new picture as a new SKP with PNG encoded bitmaps.
-        SkString skpPath = SkOSPath::SkPathJoin(fOutputDir.c_str(), fInputFilename.c_str());
+        SkString skpPath = SkOSPath::SkPathJoin(fWritePath.c_str(), fInputFilename.c_str());
         SkFILEWStream stream(skpPath.c_str());
         picture->serialize(&stream, &encode_bitmap_to_data);
         return true;
@@ -397,7 +398,7 @@
         setup_bitmap(*out, fPicture->width(), fPicture->height());
         fCanvas->readPixels(*out, 0, 0);
     }
-    return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
+    return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
                  fUseChecksumBasedFilenames);
 }
 
@@ -407,9 +408,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-void SimplePictureRenderer::init(SkPicture* picture, const SkString* outputDir,
-                                 const SkString* inputFilename, bool useChecksumBasedFilenames) {
-    INHERITED::init(picture, outputDir, inputFilename, useChecksumBasedFilenames);
+void SimplePictureRenderer::init(SkPicture* picture, const SkString* writePath,
+                                 const SkString* mismatchPath, const SkString* inputFilename,
+                                 bool useChecksumBasedFilenames) {
+    INHERITED::init(picture, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames);
     this->buildBBoxHierarchy();
 }
 
@@ -427,7 +429,7 @@
         setup_bitmap(*out, fPicture->width(), fPicture->height());
         fCanvas->readPixels(*out, 0, 0);
     }
-    return write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
+    return write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
                  fUseChecksumBasedFilenames);
 }
 
@@ -447,8 +449,9 @@
     , fTilesX(0)
     , fTilesY(0) { }
 
-void TiledPictureRenderer::init(SkPicture* pict, const SkString* outputDir,
-                                const SkString* inputFilename, bool useChecksumBasedFilenames) {
+void TiledPictureRenderer::init(SkPicture* pict, const SkString* writePath,
+                                const SkString* mismatchPath, const SkString* inputFilename,
+                                bool useChecksumBasedFilenames) {
     SkASSERT(NULL != pict);
     SkASSERT(0 == fTileRects.count());
     if (NULL == pict || fTileRects.count() != 0) {
@@ -458,7 +461,8 @@
     // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
     // used by bench_pictures.
     fPicture.reset(pict)->ref();
-    this->CopyString(&fOutputDir, outputDir);
+    this->CopyString(&fWritePath, writePath);
+    this->CopyString(&fMismatchPath, mismatchPath);
     this->CopyString(&fInputFilename, inputFilename);
     fUseChecksumBasedFilenames = useChecksumBasedFilenames;
     this->buildBBoxHierarchy();
@@ -636,7 +640,7 @@
     bool success = true;
     for (int i = 0; i < fTileRects.count(); ++i) {
         draw_tile_to_canvas(fCanvas, fTileRects[i], fPicture);
-        success &= write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
+        success &= write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
                          fUseChecksumBasedFilenames, &i);
         if (NULL != out) {
             if (fCanvas->readPixels(&bitmap, 0, 0)) {
@@ -720,7 +724,7 @@
 
         for (int i = fStart; i < fEnd; i++) {
             draw_tile_to_canvas(fCanvas, fRects[i], fClone);
-            if (!write(fCanvas, fOutputDir, fInputFilename, fJsonSummaryPtr,
+            if (!write(fCanvas, fWritePath, fMismatchPath, fInputFilename, fJsonSummaryPtr,
                        fUseChecksumBasedFilenames, &i)
                 && fSuccess != NULL) {
                 *fSuccess = false;
@@ -742,9 +746,10 @@
         fDone->run();
     }
 
-    void setPathsAndSuccess(const SkString& outputDir, const SkString& inputFilename,
-                            bool* success) {
-        fOutputDir.set(outputDir);
+    void setPathsAndSuccess(const SkString& writePath, const SkString& mismatchPath,
+                            const SkString& inputFilename, bool* success) {
+        fWritePath.set(writePath);
+        fMismatchPath.set(mismatchPath);
         fInputFilename.set(inputFilename);
         fSuccess = success;
     }
@@ -758,7 +763,8 @@
     SkPicture*         fClone;      // Picture to draw from. Each CloneData has a unique one which
                                     // is threadsafe.
     SkCanvas*          fCanvas;     // Canvas to draw to. Reused for each tile.
-    SkString           fOutputDir;  // If not empty, write results into this directory.
+    SkString           fWritePath;  // If not empty, write all results into this directory.
+    SkString           fMismatchPath;  // If not empty, write all unexpected results into this dir.
     SkString           fInputFilename; // Filename of input SkPicture file.
     SkTDArray<SkRect>& fRects;      // All tiles of the picture.
     const int          fStart;      // Range of tiles drawn by this thread.
@@ -781,10 +787,11 @@
     fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
 }
 
-void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* outputDir,
-                                    const SkString* inputFilename, bool useChecksumBasedFilenames) {
+void MultiCorePictureRenderer::init(SkPicture *pict, const SkString* writePath,
+                                    const SkString* mismatchPath, const SkString* inputFilename,
+                                    bool useChecksumBasedFilenames) {
     // Set fPicture and the tiles.
-    this->INHERITED::init(pict, outputDir, inputFilename, useChecksumBasedFilenames);
+    this->INHERITED::init(pict, writePath, mismatchPath, inputFilename, useChecksumBasedFilenames);
     for (int i = 0; i < fNumThreads; ++i) {
         *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
     }
@@ -812,9 +819,9 @@
 
 bool MultiCorePictureRenderer::render(SkBitmap** out) {
     bool success = true;
-    if (!fOutputDir.isEmpty()) {
+    if (!fWritePath.isEmpty() || !fMismatchPath.isEmpty()) {
         for (int i = 0; i < fNumThreads-1; i++) {
-            fCloneData[i]->setPathsAndSuccess(fOutputDir, fInputFilename, &success);
+            fCloneData[i]->setPathsAndSuccess(fWritePath, fMismatchPath, fInputFilename, &success);
         }
     }
 
@@ -912,7 +919,7 @@
         SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
         SkSafeUnref(data);
 
-        return (fOutputDir.isEmpty());    // we don't have anything to write
+        return (fWritePath.isEmpty());    // we don't have anything to write
     }
 
 private:
@@ -935,7 +942,7 @@
             SkSafeUnref(clone);
         }
 
-        return (fOutputDir.isEmpty());    // we don't have anything to write
+        return (fWritePath.isEmpty());    // we don't have anything to write
     }
 
 private: