Add a new PictureRenderer that draws the picture then breaks up into tiles.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@6333 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/tools/CopyTilesRenderer.cpp b/tools/CopyTilesRenderer.cpp
new file mode 100644
index 0000000..553209c
--- /dev/null
+++ b/tools/CopyTilesRenderer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "picture_utils.h"
+#include "CopyTilesRenderer.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkImageEncoder.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+namespace sk_tools {
+    CopyTilesRenderer::CopyTilesRenderer(int x, int y)
+    : fXTilesPerLargeTile(x)
+    , fYTilesPerLargeTile(y) {
+    }
+    void CopyTilesRenderer::init(SkPicture* pict) {
+        SkASSERT(pict != NULL);
+        // Only work with absolute widths (as opposed to percentages).
+        SkASSERT(this->getTileWidth() != 0 && this->getTileHeight() != 0);
+        fPicture = pict;
+        fPicture->ref();
+        this->buildBBoxHierarchy();
+        // In order to avoid allocating a large canvas (particularly important for GPU), create one
+        // canvas that is a multiple of the tile size, and draw portions of the picture.
+        fLargeTileWidth = fXTilesPerLargeTile * this->getTileWidth();
+        fLargeTileHeight = fYTilesPerLargeTile * this->getTileHeight();
+        fCanvas.reset(this->INHERITED::setupCanvas(fLargeTileWidth, fLargeTileHeight));
+    }
+
+    bool CopyTilesRenderer::render(const SkString* path) {
+        int i = 0;
+        bool success = true;
+        SkBitmap dst;
+        for (int x = 0; x < fPicture->width(); x += fLargeTileWidth) {
+            for (int y = 0; y < fPicture->height(); y += fLargeTileHeight) {
+                SkAutoCanvasRestore autoRestore(fCanvas, true);
+                fCanvas->translate(SkIntToScalar(-x), SkIntToScalar(-y));
+                // Draw the picture
+                fCanvas->drawPicture(*fPicture);
+                // Now extract the picture into tiles
+                const SkBitmap& baseBitmap = fCanvas->getDevice()->accessBitmap(false);
+                SkIRect subset;
+                for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) {
+                    for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) {
+                        subset.set(tileX, tileY, tileX + this->getTileWidth(),
+                                   tileY + this->getTileHeight());
+                        SkDEBUGCODE(bool extracted =)
+                        baseBitmap.extractSubset(&dst, subset);
+                        SkASSERT(extracted);
+                        if (path != NULL) {
+                            // Similar to writeAppendNumber in PictureRenderer.cpp, but just encodes
+                            // a bitmap directly.
+                            SkString pathWithNumber(*path);
+                            pathWithNumber.appendf("%i.png", i++);
+                            SkBitmap copy;
+#if SK_SUPPORT_GPU
+                            if (isUsingGpuDevice()) {
+                                dst.pixelRef()->readPixels(&copy, &subset);
+                            } else {
+#endif
+                                dst.copyTo(&copy, dst.config());
+#if SK_SUPPORT_GPU
+                            }
+#endif
+                            success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy,
+                                                                  SkImageEncoder::kPNG_Type, 100);
+                        }
+                    }
+                }
+            }
+        }
+        return success;
+    }
+
+    SkString CopyTilesRenderer::getConfigNameInternal() {
+        return SkString("copy_tiles");
+    }
+}
diff --git a/tools/CopyTilesRenderer.h b/tools/CopyTilesRenderer.h
new file mode 100644
index 0000000..55abc9c
--- /dev/null
+++ b/tools/CopyTilesRenderer.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CopyTilesRenderer_DEFINED
+#define CopyTilesRenderer_DEFINED
+
+#include "PictureRenderer.h"
+#include "SkTypes.h"
+
+class SkPicture;
+class SkString;
+
+namespace sk_tools {
+    /**
+     *  PictureRenderer that draws the picture and then extracts it into tiles. For large pictures,
+     *  it will divide the picture into large tiles and draw the picture once for each large tile.
+     */
+    class CopyTilesRenderer : public TiledPictureRenderer {
+
+    public:
+        CopyTilesRenderer(int x, int y);
+        virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+        /**
+         *  Similar to TiledPictureRenderer, this will draw a PNG for each tile. However, the
+         *  numbering (and actual tiles) will be different.
+         */
+        virtual bool render(const SkString* path) SK_OVERRIDE;
+
+    private:
+        int fXTilesPerLargeTile;
+        int fYTilesPerLargeTile;
+
+        int fLargeTileWidth;
+        int fLargeTileHeight;
+
+        virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+        typedef TiledPictureRenderer INHERITED;
+    };
+} // sk_tools
+#endif // CopyTilesRenderer_DEFINED
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index acbe747..409439e 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "BenchTimer.h"
+#include "CopyTilesRenderer.h"
 #include "PictureBenchmark.h"
 #include "SkBenchLogger.h"
 #include "SkCanvas.h"
@@ -120,8 +121,8 @@
 "     %s <inputDir>...\n"
 "     [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
 "     [--repeat] \n"
-"     [--mode pow2tile minWidth height[] | record | simple\n"
-"             | tile width[] height[] | playbackCreation]\n"
+"     [--mode pow2tile minWidth height | record | simple\n"
+"             | tile width height | playbackCreation]\n"
 "     [--pipe]\n"
 "     [--bbh bbhType]\n"
 "     [--multi numThreads]\n"
@@ -143,12 +144,12 @@
     SkDebugf("     --timers [wcgWC]* : "
              "Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
     SkDebugf(
-"     --mode pow2tile minWidth height[] | record | simple\n"
-"            | tile width[] height[] | playbackCreation:\n"
+"     --mode pow2tile minWidth height | copyTile width height | record | simple\n"
+"            | tile width height | playbackCreation:\n"
 "            Run in the corresponding mode.\n"
 "            Default is simple.\n");
     SkDebugf(
-"                     pow2tile minWidth height[], Creates tiles with widths\n"
+"                     pow2tile minWidth height, Creates tiles with widths\n"
 "                                                 that are all a power of two\n"
 "                                                 such that they minimize the\n"
 "                                                 amount of wasted tile space.\n"
@@ -162,8 +163,16 @@
     SkDebugf(
 "                     simple, Benchmark a simple rendering.\n");
     SkDebugf(
-"                     tile width[] height[], Benchmark simple rendering using\n"
-"                                            tiles with the given dimensions.\n");
+"                     tile width height, Benchmark simple rendering using\n"
+"                                            tiles with the given dimensions.\n"
+"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
+"                                                Does not support percentages.\n"
+"                                                If the picture is large enough, breaks it into\n"
+"                                                larger tiles (and draws the picture once per\n"
+"                                                larger tile) to avoid creating a large canvas.\n"
+"                                                Add --tiles x y to specify the number of tiles\n"
+"                                                per larger tile in the x and y direction.\n"
+             );
     SkDebugf(
 "                     playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
     SkDebugf("\n");
@@ -271,6 +280,9 @@
     int gridWidth = 0;
     int gridHeight = 0;
     bool isPowerOf2Mode = false;
+    bool isCopyMode = false;
+    const char* xTilesString = NULL;
+    const char* yTilesString = NULL;
     const char* mode = NULL;
     bool gridSupported = false;
     sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
@@ -368,12 +380,15 @@
                 gridSupported = true;
             } else if (0 == strcmp(*argv, "simple")) {
                 renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
-            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
+            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
+                       || 0 == strcmp(*argv, "copyTile")) {
                 useTiles = true;
                 mode = *argv;
 
                 if (0 == strcmp(*argv, "pow2tile")) {
                     isPowerOf2Mode = true;
+                } else if (0 == strcmp(*argv, "copyTile")) {
+                    isCopyMode = true;
                 } else {
                     gridSupported = true;
                 }
@@ -389,7 +404,9 @@
                 widthString = *argv;
                 ++argv;
                 if (argv >= stop) {
-                    gLogger.logError("Missing height for --mode tile\n");
+                    SkString err;
+                    err.appendf("Missing height for --mode %s\n", mode);
+                    gLogger.logError(err);
                     PRINT_USAGE_AND_EXIT;
                 }
                 heightString = *argv;
@@ -402,6 +419,19 @@
                 gLogger.logError(err);
                 PRINT_USAGE_AND_EXIT;
             }
+        } else if (0 == strcmp(*argv, "--tiles")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing x for --tiles\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            xTilesString = *argv;
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing y for --tiles\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            yTilesString = *argv;
         }  else if (0 == strcmp(*argv, "--device")) {
             ++argv;
             if (argv >= stop) {
@@ -548,7 +578,21 @@
     if (useTiles) {
         SkASSERT(NULL == renderer);
         sk_tools::TiledPictureRenderer* tiledRenderer;
-        if (numThreads > 1) {
+        if (isCopyMode) {
+            int x, y;
+            if (xTilesString != NULL) {
+                SkASSERT(yTilesString != NULL);
+                x = atoi(xTilesString);
+                y = atoi(yTilesString);
+                if (x <= 0 || y <= 0) {
+                    gLogger.logError("--tiles must be given values > 0\n");
+                    PRINT_USAGE_AND_EXIT;
+                }
+            } else {
+                x = y = 4;
+            }
+            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
+        } else if (numThreads > 1) {
             tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
         } else {
             tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
@@ -565,33 +609,55 @@
             }
             tiledRenderer->setTileMinPowerOf2Width(minWidth);
         } else if (sk_tools::is_percentage(widthString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                gLogger.logError(err.c_str());
+                PRINT_USAGE_AND_EXIT;
+            }
             tiledRenderer->setTileWidthPercentage(atof(widthString));
             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
                 tiledRenderer->unref();
-                gLogger.logError("--mode tile must be given a width percentage > 0\n");
+                SkString err;
+                err.appendf("--mode %s must be given a width percentage > 0\n", mode);
+                gLogger.logError(err);
                 PRINT_USAGE_AND_EXIT;
             }
         } else {
             tiledRenderer->setTileWidth(atoi(widthString));
             if (!(tiledRenderer->getTileWidth() > 0)) {
                 tiledRenderer->unref();
-                gLogger.logError("--mode tile must be given a width > 0\n");
+                SkString err;
+                err.appendf("--mode %s must be given a width > 0\n", mode);
+                gLogger.logError(err);
                 PRINT_USAGE_AND_EXIT;
             }
         }
 
         if (sk_tools::is_percentage(heightString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                gLogger.logError(err.c_str());
+                PRINT_USAGE_AND_EXIT;
+            }
             tiledRenderer->setTileHeightPercentage(atof(heightString));
             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
                 tiledRenderer->unref();
-                gLogger.logError("--mode tile must be given a height percentage > 0\n");
+                SkString err;
+                err.appendf("--mode %s must be given a height percentage > 0\n", mode);
+                gLogger.logError(err);
                 PRINT_USAGE_AND_EXIT;
             }
         } else {
             tiledRenderer->setTileHeight(atoi(heightString));
             if (!(tiledRenderer->getTileHeight() > 0)) {
                 tiledRenderer->unref();
-                gLogger.logError("--mode tile must be given a height > 0\n");
+                SkString err;
+                err.appendf("--mode %s must be given a height > 0\n", mode);
+                gLogger.logError(err);
                 PRINT_USAGE_AND_EXIT;
             }
         }
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
index 7d6275d..250058e 100644
--- a/tools/render_pictures_main.cpp
+++ b/tools/render_pictures_main.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CopyTilesRenderer.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
@@ -24,9 +25,9 @@
     SkDebugf("\n"
 "Usage: \n"
 "     %s <input>... \n"
-"     [-w <outputDir>]"
-"     [--mode pow2tile minWidth height[%] | simple\n"
-"         | tile width[%] height[%]]\n"
+"     [-w <outputDir>]\n"
+"     [--mode pow2tile minWidth height | copyTile width height | simple\n"
+"         | tile width height]\n"
 "     [--pipe]\n"
 "     [--multi count]\n"
 "     [--device bitmap"
@@ -42,11 +43,11 @@
     SkDebugf(
 "     outputDir: directory to write the rendered images.\n\n");
     SkDebugf(
-"     --mode pow2tile minWidth height[%] | simple | rerecord\n"
-"          | tile width[%] height[%]: Run in the corresponding mode.\n"
+"     --mode pow2tile minWidth height | copyTile width height | simple\n"
+"          | tile width height | rerecord: Run in the corresponding mode.\n"
 "                                     Default is simple.\n");
     SkDebugf(
-"                     pow2tile minWidth height[%], Creates tiles with widths\n"
+"                     pow2tile minWidth height, Creates tiles with widths\n"
 "                                                  that are all a power of two\n"
 "                                                  such that they minimize the\n"
 "                                                  amount of wasted tile space.\n"
@@ -59,8 +60,16 @@
 "                     rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n"
              );
     SkDebugf(
-"                     tile width[%] height[%], Do a simple render using tiles\n"
-"                                              with the given dimensions.\n");
+"                     tile width height, Do a simple render using tiles\n"
+"                                              with the given dimensions.\n"
+"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
+"                                                Does not support percentages.\n"
+"                                                If the picture is large enough, breaks it into\n"
+"                                                larger tiles (and draws the picture once per\n"
+"                                                larger tile) to avoid creating a large canvas.\n"
+"                                                Add --tiles x y to specify the number of tiles\n"
+"                                                per larger tile in the x and y direction.\n"
+             );
     SkDebugf("\n");
     SkDebugf(
 "     --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
@@ -172,6 +181,9 @@
     const char* widthString = NULL;
     const char* heightString = NULL;
     bool isPowerOf2Mode = false;
+    bool isCopyMode = false;
+    const char* xTilesString = NULL;
+    const char* yTilesString = NULL;
     const char* mode = NULL;
 
     for (++argv; argv < stop; ++argv) {
@@ -192,12 +204,15 @@
 
             if (0 == strcmp(*argv, "simple")) {
                 renderer = SkNEW(sk_tools::SimplePictureRenderer);
-            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
+            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
+                       || 0 == strcmp(*argv, "copyTile")) {
                 useTiles = true;
                 mode = *argv;
 
                 if (0 == strcmp(*argv, "pow2tile")) {
                     isPowerOf2Mode = true;
+                } else if (0 == strcmp(*argv, "copyTile")) {
+                    isCopyMode = true;
                 }
 
                 ++argv;
@@ -210,7 +225,7 @@
                 widthString = *argv;
                 ++argv;
                 if (argv >= stop) {
-                    SkDebugf("Missing height for --mode tile\n");
+                    SkDebugf("Missing height for --mode %s\n", mode);
                     usage(argv0);
                     exit(-1);
                 }
@@ -222,6 +237,21 @@
                 usage(argv0);
                 exit(-1);
             }
+        } else if (0 == strcmp(*argv, "--tiles")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing x for --tiles\n");
+                usage(argv0);
+                exit(-1);
+            }
+            xTilesString = *argv;
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing y for --tiles\n");
+                usage(argv0);
+                exit(-1);
+            }
+            yTilesString = *argv;
         } else if (0 == strcmp(*argv, "--pipe")) {
             usePipe = true;
         } else if (0 == strcmp(*argv, "--multi")) {
@@ -290,7 +320,22 @@
     if (useTiles) {
         SkASSERT(NULL == renderer);
         sk_tools::TiledPictureRenderer* tiledRenderer;
-        if (numThreads > 1) {
+        if (isCopyMode) {
+            int x, y;
+            if (xTilesString != NULL) {
+                SkASSERT(yTilesString != NULL);
+                x = atoi(xTilesString);
+                y = atoi(yTilesString);
+                if (x <= 0 || y <= 0) {
+                    SkDebugf("--tiles must be given values > 0\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+            } else {
+                x = y = 4;
+            }
+            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
+        } else if (numThreads > 1) {
             tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
         } else {
             tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
@@ -308,10 +353,18 @@
             }
             tiledRenderer->setTileMinPowerOf2Width(minWidth);
         } else if (sk_tools::is_percentage(widthString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                SkDebugf(err.c_str());
+                usage(argv0);
+                exit(-1);
+            }
             tiledRenderer->setTileWidthPercentage(atof(widthString));
             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
                 tiledRenderer->unref();
-                SkDebugf("--mode tile must be given a width percentage > 0\n");
+                SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
                 usage(argv0);
                 exit(-1);
             }
@@ -319,17 +372,25 @@
             tiledRenderer->setTileWidth(atoi(widthString));
             if (!(tiledRenderer->getTileWidth() > 0)) {
                 tiledRenderer->unref();
-                SkDebugf("--mode tile must be given a width > 0\n");
+                SkDebugf("--mode %s must be given a width > 0\n", mode);
                 usage(argv0);
                 exit(-1);
             }
         }
 
         if (sk_tools::is_percentage(heightString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                SkDebugf(err.c_str());
+                usage(argv0);
+                exit(-1);
+            }
             tiledRenderer->setTileHeightPercentage(atof(heightString));
             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
                 tiledRenderer->unref();
-                SkDebugf("--mode tile must be given a height percentage > 0\n");
+                SkDebugf("--mode %s must be given a height percentage > 0\n", mode);
                 usage(argv0);
                 exit(-1);
             }
@@ -337,7 +398,7 @@
             tiledRenderer->setTileHeight(atoi(heightString));
             if (!(tiledRenderer->getTileHeight() > 0)) {
                 tiledRenderer->unref();
-                SkDebugf("--mode tile must be given a height > 0\n");
+                SkDebugf("--mode %s must be given a height > 0\n", mode);
                 usage(argv0);
                 exit(-1);
             }