Allow render_pictures to render using multiple threads.

Make write() a static function so it can be used by the
thread entry functions.

Add a helper function to append a number to a string and
call write to share code.

Review URL: https://codereview.appspot.com/6589062

git-svn-id: http://skia.googlecode.com/svn/trunk@5789 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index 2eb89b2..2478752 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -99,10 +99,16 @@
 #endif
 }
 
-bool PictureRenderer::write(SkCanvas* canvas, SkString path) const {
+/**
+ * Write the canvas to the specified path.
+ * @param canvas Must be non-null. Canvas to be written to a file.
+ * @param path Path for the file to be written. Should have no extension; write() will append
+ *             an appropriate one. Passed in by value so it can be modified.
+ * @return bool True if the Canvas is written to a file.
+ */
+static bool write(SkCanvas* canvas, SkString path) {
     SkASSERT(canvas != NULL);
-    SkASSERT(fPicture != NULL);
-    if (NULL == canvas || NULL == fPicture) {
+    if (NULL == canvas) {
         return false;
     }
 
@@ -118,6 +124,19 @@
     return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
 }
 
+/**
+ * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
+ * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
+ */
+static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
+    if (NULL == path) {
+        return true;
+    }
+    SkString pathWithNumber(*path);
+    pathWithNumber.appendf("%i", number);
+    return write(canvas, pathWithNumber);
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 bool RecordPictureRenderer::render(const SkString*) {
@@ -144,7 +163,7 @@
     pipeCanvas->drawPicture(*fPicture);
     writer.endRecording();
     fCanvas->flush();
-    return path != NULL && this->write(fCanvas, *path);
+    return path != NULL && write(fCanvas, *path);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -158,7 +177,7 @@
 
     fCanvas->drawPicture(*fPicture);
     fCanvas->flush();
-    return path != NULL && this->write(fCanvas, *path);
+    return path != NULL && write(fCanvas, *path);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -307,23 +326,32 @@
 // Base class for data used both by pipe and clone picture multi threaded drawing.
 
 struct ThreadData {
-    ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects)
+    ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
+               const SkString* path, bool* success)
     : fCanvas(target)
     , fTileCounter(tileCounter)
-    , fTileRects(tileRects) {
+    , fTileRects(tileRects)
+    , fPath(path)
+    , fSuccess(success) {
         SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
+        // Success must start off true, and it will be set to false upon failure.
+        SkASSERT(success != NULL && *success);
     }
 
-    const SkRect* nextTile() {
+    int32_t nextTile(SkRect* rect) {
         int32_t i = sk_atomic_inc(fTileCounter);
         if (i < fTileRects->count()) {
-            return &fTileRects->operator[](i);
+            SkASSERT(rect != NULL);
+            *rect = fTileRects->operator[](i);
+            return i;
         }
-        return NULL;
+        return -1;
     }
 
     // All of these are pointers to objects owned elsewhere
     SkCanvas*                fCanvas;
+    const SkString*          fPath;
+    bool*                    fSuccess;
 private:
     // Shared by all threads, this states which is the next tile to be drawn.
     int32_t*                 fTileCounter;
@@ -337,8 +365,8 @@
 
 struct TileData : public ThreadData {
     TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
-             SkTDArray<SkRect>* tileRects)
-    : INHERITED(canvas, tileCounter, tileRects)
+             SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
+    : INHERITED(canvas, tileCounter, tileRects, path, success)
     , fController(controller) {}
 
     ThreadSafePipeController* fController;
@@ -350,9 +378,14 @@
     SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
     TileData* tileData = static_cast<TileData*>(data);
 
-    const SkRect* tileRect;
-    while ((tileRect = tileData->nextTile()) != NULL) {
-        DrawTileToCanvas(tileData->fCanvas, *tileRect, tileData->fController);
+    SkRect tileRect;
+    int32_t i;
+    while ((i = tileData->nextTile(&tileRect)) != -1) {
+        DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
+        if (!writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
+            *tileData->fSuccess = false;
+            break;
+        }
     }
     SkDELETE(tileData);
 }
@@ -361,8 +394,9 @@
 // Draw using Picture
 
 struct CloneData : public ThreadData {
-    CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects)
-    : INHERITED(target, tileCounter, tileRects)
+    CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
+              const SkString* path, bool* success)
+    : INHERITED(target, tileCounter, tileRects, path, success)
     , fClone(clone) {}
 
     SkPicture* fClone;
@@ -374,9 +408,14 @@
     SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
     CloneData* cloneData = static_cast<CloneData*>(data);
 
-    const SkRect* tileRect;
-    while ((tileRect = cloneData->nextTile()) != NULL) {
-        DrawTileToCanvas(cloneData->fCanvas, *tileRect, cloneData->fClone);
+    SkRect tileRect;
+    int32_t i;
+    while ((i = cloneData->nextTile(&tileRect)) != -1) {
+        DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
+        if (!writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
+            *cloneData->fSuccess = false;
+            break;
+        }
     }
     SkDELETE(cloneData);
 }
@@ -416,15 +455,17 @@
         SkASSERT(fCanvasPool.count() == fNumThreads);
         SkTDArray<SkThread*> threads;
         SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
+        bool success = true;
         for (int i = 0; i < fNumThreads; ++i) {
             // data will be deleted by the entryPointProc.
             ThreadData* data;
             if (fUsePipe) {
-                data = SkNEW_ARGS(TileData,
-                                  (fPipeController, fCanvasPool[i], &fTileCounter, &fTileRects));
+                data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
+                                             &fTileRects, path, &success));
             } else {
                 SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
-                data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects));
+                data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
+                                              &success));
             }
             SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
             if (!thread->start()) {
@@ -439,8 +480,7 @@
             SkDELETE(thread);
         }
         threads.reset();
-        // Currently multithreaded is not an option for render_pictures
-        return false;
+        return success;
     } else {
         // For single thread, we really only need one canvas total.
         SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
@@ -448,12 +488,8 @@
 
         for (int i = 0; i < fTileRects.count(); ++i) {
             DrawTileToCanvas(canvas, fTileRects[i], fPicture);
-            if (path != NULL) {
-                SkString tilePath(*path);
-                tilePath.appendf("%i", i);
-                if (!this->write(canvas, tilePath)) {
-                    return false;
-                }
+            if (!writeAppendNumber(canvas, path, i)) {
+                return false;
             }
         }
         return path != NULL;