Add nanobench tests for BitmapRegionDecoder

SkBitmapRegionDecoderInterface provides an interface
for multiple implementations of Android's
BitmapRegionDecoder.

We already have correctness tests in DM that will enable us
to compare the quality of our various BRD implementations.

We also need these performance tests to compare the speed
of our various implementations.

BUG=skia:4357

Review URL: https://codereview.chromium.org/1344993003
diff --git a/bench/BitmapRegionDecoderBench.cpp b/bench/BitmapRegionDecoderBench.cpp
new file mode 100644
index 0000000..77d3423
--- /dev/null
+++ b/bench/BitmapRegionDecoderBench.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "BitmapRegionDecoderBench.h"
+#include "CodecBenchPriv.h"
+#include "SkBitmap.h"
+#include "SkCodecTools.h"
+#include "SkOSFile.h"
+
+BitmapRegionDecoderBench::BitmapRegionDecoderBench(const char* baseName, SkData* encoded,
+        SkBitmapRegionDecoderInterface::Strategy strategy, SkColorType colorType,
+        uint32_t sampleSize, const SkIRect& subset)
+    : fBRD(nullptr)
+    , fData(SkRef(encoded))
+    , fStrategy(strategy)
+    , fColorType(colorType)
+    , fSampleSize(sampleSize)
+    , fSubset(subset)
+{
+    // Choose a useful name for the region decoding strategy
+    const char* strategyName;
+    switch (strategy) {
+        case SkBitmapRegionDecoderInterface::kOriginal_Strategy:
+            strategyName = "Original";
+            break;
+        case SkBitmapRegionDecoderInterface::kCanvas_Strategy:
+            strategyName = "Canvas";
+            break;
+        default:
+            SkASSERT(false);
+            strategyName = "";
+            break;
+    }
+
+    // Choose a useful name for the color type
+    const char* colorName = color_type_to_str(colorType);
+
+    fName.printf("BRD_%s_%s_%s", baseName, strategyName, colorName);
+    if (1 != sampleSize) {
+        fName.appendf("_%.3f", get_scale_from_sample_size(sampleSize));
+    }
+}
+
+const char* BitmapRegionDecoderBench::onGetName() {
+    return fName.c_str();
+}
+
+bool BitmapRegionDecoderBench::isSuitableFor(Backend backend) {
+    return kNonRendering_Backend == backend;
+}
+
+void BitmapRegionDecoderBench::onPreDraw() {
+    SkStreamRewindable* stream = new SkMemoryStream(fData);
+    fBRD.reset(SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, fStrategy));
+}
+
+void BitmapRegionDecoderBench::onDraw(const int n, SkCanvas* canvas) {
+    SkAutoTDelete<SkBitmap> bitmap;
+    for (int i = 0; i < n; i++) {
+        bitmap.reset(fBRD->decodeRegion(fSubset.left(), fSubset.top(), fSubset.width(),
+                fSubset.height(), fSampleSize, fColorType));
+        SkASSERT(nullptr != bitmap.get());
+    }
+}
diff --git a/bench/BitmapRegionDecoderBench.h b/bench/BitmapRegionDecoderBench.h
new file mode 100644
index 0000000..7be770f
--- /dev/null
+++ b/bench/BitmapRegionDecoderBench.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef BitmapRegionDecoderBench_DEFINED
+#define BitmapRegionDecoderBench_DEFINED
+
+#include "Benchmark.h"
+#include "SkBitmapRegionDecoderInterface.h"
+#include "SkData.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+/**
+ *  Benchmark Android's BitmapRegionDecoder for a particular colorType, sampleSize, and subset.
+ *
+ *  fStrategy determines which of various implementations is to be used.
+ *
+ *  nanobench.cpp handles creating benchmarks for interesting scaled subsets.  We strive to test
+ *  on real use cases.
+ */
+class BitmapRegionDecoderBench : public Benchmark {
+public:
+    // Calls encoded->ref()
+    BitmapRegionDecoderBench(const char* basename, SkData* encoded,
+            SkBitmapRegionDecoderInterface::Strategy strategy, SkColorType colorType,
+            uint32_t sampleSize, const SkIRect& subset);
+
+protected:
+    const char* onGetName() override;
+    bool isSuitableFor(Backend backend) override;
+    void onDraw(const int n, SkCanvas* canvas) override;
+    void onPreDraw() override;
+
+private:
+    SkString                                       fName;
+    SkAutoTDelete<SkBitmapRegionDecoderInterface>  fBRD;
+    SkAutoTUnref<SkData>                           fData;
+    const SkBitmapRegionDecoderInterface::Strategy fStrategy;
+    const SkColorType                              fColorType;
+    const uint32_t                                 fSampleSize;
+    const SkIRect                                  fSubset;
+    typedef Benchmark INHERITED;
+};
+#endif // BitmapRegionDecoderBench_DEFINED
diff --git a/bench/CodecBench.cpp b/bench/CodecBench.cpp
index 1e6b520..a21aae1 100644
--- a/bench/CodecBench.cpp
+++ b/bench/CodecBench.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "CodecBench.h"
+#include "CodecBenchPriv.h"
 #include "SkBitmap.h"
 #include "SkCodec.h"
 #include "SkOSFile.h"
@@ -15,21 +16,7 @@
     , fData(SkRef(encoded))
 {
     // Parse filename and the color type to give the benchmark a useful name
-    const char* colorName;
-    switch(colorType) {
-        case kN32_SkColorType:
-            colorName = "N32";
-            break;
-        case kRGB_565_SkColorType:
-            colorName = "565";
-            break;
-        case kAlpha_8_SkColorType:
-            colorName = "Alpha8";
-            break;
-        default:
-            colorName = "Unknown";
-    }
-    fName.printf("Codec_%s_%s", baseName.c_str(), colorName);
+    fName.printf("Codec_%s_%s", baseName.c_str(), color_type_to_str(colorType));
 #ifdef SK_DEBUG
     // Ensure that we can create an SkCodec from this data.
     SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(fData));
diff --git a/bench/CodecBenchPriv.h b/bench/CodecBenchPriv.h
new file mode 100644
index 0000000..d8585b6
--- /dev/null
+++ b/bench/CodecBenchPriv.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CodecBenchPriv_DEFINED
+#define CodecBenchPriv_DEFINED
+
+#include "SkImageInfo.h"
+
+inline const char* color_type_to_str(SkColorType colorType) {
+    switch (colorType) {
+        case kN32_SkColorType:
+            return "N32";
+        case kRGB_565_SkColorType:
+            return "565";
+        case kGray_8_SkColorType:
+            return "Gray8";
+        case kIndex_8_SkColorType:
+            return "Index8";
+        case kAlpha_8_SkColorType:
+            return "Alpha8";
+        default:
+            return "Unknown";
+    }
+}
+
+#endif // CodecBenchPriv_DEFINED
diff --git a/bench/DecodingBench.cpp b/bench/DecodingBench.cpp
index 421cabe..7b6ad2b 100644
--- a/bench/DecodingBench.cpp
+++ b/bench/DecodingBench.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CodecBenchPriv.h"
 #include "DecodingBench.h"
 #include "SkBitmap.h"
 #include "SkData.h"
@@ -25,21 +26,7 @@
 {
     // Parse filename and the color type to give the benchmark a useful name
     SkString baseName = SkOSPath::Basename(path.c_str());
-    const char* colorName;
-    switch(colorType) {
-        case kN32_SkColorType:
-            colorName = "N32";
-            break;
-        case kRGB_565_SkColorType:
-            colorName = "565";
-            break;
-        case kAlpha_8_SkColorType:
-            colorName = "Alpha8";
-            break;
-        default:
-            colorName = "Unknown";
-    }
-    fName.printf("Decode_%s_%s", baseName.c_str(), colorName);
+    fName.printf("Decode_%s_%s", baseName.c_str(), color_type_to_str(colorType));
     
 #ifdef SK_DEBUG
     // Ensure that we can create a decoder.
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index e7019dc..a1db0ac 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -10,7 +10,9 @@
 #include "nanobench.h"
 
 #include "Benchmark.h"
+#include "BitmapRegionDecoderBench.h"
 #include "CodecBench.h"
+#include "CodecBenchPriv.h"
 #include "CrashHandler.h"
 #include "DecodingBench.h"
 #include "GMBench.h"
@@ -19,13 +21,13 @@
 #include "RecordingBench.h"
 #include "SKPAnimationBench.h"
 #include "SKPBench.h"
-#include "SubsetBenchPriv.h"
 #include "SubsetSingleBench.h"
 #include "SubsetTranslateBench.h"
 #include "SubsetZoomBench.h"
 #include "Stats.h"
 #include "Timer.h"
 
+#include "SkBitmapRegionDecoderInterface.h"
 #include "SkBBoxHierarchy.h"
 #include "SkCanvas.h"
 #include "SkCodec.h"
@@ -525,7 +527,7 @@
                 colors, &colorCount) != SkCodec::kSuccess)
         {
             SkDebugf("Could not create scanline decoder for %s with color type %s.  "
-                    "Skipping bench.\n", path.c_str(), get_color_name(colorType));
+                    "Skipping bench.\n", path.c_str(), color_type_to_str(colorType));
             return false;
         }
         *width = info.width();
@@ -539,7 +541,7 @@
         //FIXME: See skbug.com/3921
         if (kIndex_8_SkColorType == colorType || kGray_8_SkColorType == colorType) {
             SkDebugf("Cannot use image subset decoder for %s with color type %s.  "
-                    "Skipping bench.\n", path.c_str(), get_color_name(colorType));
+                    "Skipping bench.\n", path.c_str(), color_type_to_str(colorType));
             return false;
         }
         if (!decoder->buildTileIndex(stream.detach(), width, height)) {
@@ -557,6 +559,38 @@
     return true;
 }
 
+static bool valid_brd_bench(SkData* encoded, SkBitmapRegionDecoderInterface::Strategy strategy,
+        SkColorType colorType, uint32_t sampleSize, uint32_t minOutputSize, int* width,
+        int* height) {
+    SkStreamRewindable* stream = new SkMemoryStream(encoded);
+    SkAutoTDelete<SkBitmapRegionDecoderInterface> brd(
+            SkBitmapRegionDecoderInterface::CreateBitmapRegionDecoder(stream, strategy));
+    if (nullptr == brd.get()) {
+        // This is indicates that subset decoding is not supported for a particular image format.
+        return false;
+    }
+
+    SkAutoTDelete<SkBitmap> bitmap(brd->decodeRegion(0, 0, brd->width(), brd->height(), 1,
+            colorType));
+    if (nullptr == bitmap.get() || colorType != bitmap->colorType()) {
+        // This indicates that conversion to the requested color type is not supported for the
+        // particular image.
+        return false;
+    }
+
+    if (sampleSize * minOutputSize > (uint32_t) brd->width() || sampleSize * minOutputSize >
+            (uint32_t) brd->height()) {
+        // This indicates that the image is not large enough to decode a
+        // minOutputSize x minOutputSize subset at the given sampleSize.
+        return false;
+    }
+
+    // Set the image width and height.  The calling code will use this to choose subsets to decode.
+    *width = brd->width();
+    *height = brd->height();
+    return true;
+}
+
 static void cleanup_run(Target* target) {
     delete target;
 #if SK_SUPPORT_GPU
@@ -580,9 +614,12 @@
                       , fCurrentCodec(0)
                       , fCurrentImage(0)
                       , fCurrentSubsetImage(0)
+                      , fCurrentBRDImage(0)
                       , fCurrentColorType(0)
                       , fCurrentSubsetType(0)
                       , fUseCodec(0)
+                      , fCurrentBRDStrategy(0)
+                      , fCurrentBRDSampleSize(0)
                       , fCurrentAnimSKP(0) {
         for (int i = 0; i < FLAGS_skps.count(); i++) {
             if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
@@ -876,6 +913,97 @@
             fUseCodec++;
         }
 
+        // Run the BRDBenches
+        // We will benchmark multiple BRD strategies.
+        const SkBitmapRegionDecoderInterface::Strategy strategies[] = {
+                SkBitmapRegionDecoderInterface::kOriginal_Strategy,
+                SkBitmapRegionDecoderInterface::kCanvas_Strategy,
+        };
+
+        // We intend to create benchmarks that model the use cases in
+        // android/libraries/social/tiledimage.  In this library, an image is decoded in 512x512
+        // tiles.  The image can be translated freely, so the location of a tile may be anywhere in
+        // the image.  For that reason, we will benchmark decodes in five representative locations
+        // in the image.  Additionally, this use case utilizes power of two scaling, so we will
+        // test on power of two sample sizes.  The output tile is always 512x512, so, when a
+        // sampleSize is used, the size of the subset that is decoded is always
+        // (sampleSize*512)x(sampleSize*512).
+        // There are a few good reasons to only test on power of two sample sizes at this time:
+        //     JPEG decodes using kOriginal_Strategy are broken for non-powers of two.
+        //         skbug.com/4319
+        //     All use cases we are aware of only scale by powers of two.
+        //     PNG decodes use the indicated sampling strategy regardless of the sample size, so
+        //         these tests are sufficient to provide good coverage of our scaling options.
+        const uint32_t sampleSizes[] = { 1, 2, 4, 8, 16 };
+        const uint32_t minOutputSize = 512;
+        while (fCurrentBRDImage < fImages.count()) {
+            while (fCurrentBRDStrategy < (int) SK_ARRAY_COUNT(strategies)) {
+                while (fCurrentColorType < fColorTypes.count()) {
+                    while (fCurrentBRDSampleSize < (int) SK_ARRAY_COUNT(sampleSizes)) {
+                        while (fCurrentSubsetType <= kLastSingle_SubsetType) {
+                            const SkString& path = fImages[fCurrentBRDImage];
+                            const SkBitmapRegionDecoderInterface::Strategy strategy =
+                                    strategies[fCurrentBRDStrategy];
+                            SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
+                            const SkColorType colorType = fColorTypes[fCurrentColorType];
+                            uint32_t sampleSize = sampleSizes[fCurrentBRDSampleSize];
+                            int currentSubsetType = fCurrentSubsetType++;
+
+                            int width = 0;
+                            int height = 0;
+                            if (!valid_brd_bench(encoded.get(), strategy, colorType, sampleSize,
+                                    minOutputSize, &width, &height)) {
+                                break;
+                            }
+
+                            SkString basename = SkOSPath::Basename(path.c_str());
+                            SkIRect subset;
+                            const uint32_t subsetSize = sampleSize * minOutputSize;
+                            switch (currentSubsetType) {
+                                case kTopLeft_SubsetType:
+                                    basename.append("_TopLeft");
+                                    subset = SkIRect::MakeXYWH(0, 0, subsetSize, subsetSize);
+                                    break;
+                                case kTopRight_SubsetType:
+                                    basename.append("_TopRight");
+                                    subset = SkIRect::MakeXYWH(width - subsetSize, 0, subsetSize,
+                                            subsetSize);
+                                    break;
+                                case kMiddle_SubsetType:
+                                    basename.append("_Middle");
+                                    subset = SkIRect::MakeXYWH((width - subsetSize) / 2,
+                                            (height - subsetSize) / 2, subsetSize, subsetSize);
+                                    break;
+                                case kBottomLeft_SubsetType:
+                                    basename.append("_BottomLeft");
+                                    subset = SkIRect::MakeXYWH(0, height - subsetSize, subsetSize,
+                                            subsetSize);
+                                    break;
+                                case kBottomRight_SubsetType:
+                                    basename.append("_BottomRight");
+                                    subset = SkIRect::MakeXYWH(width - subsetSize,
+                                            height - subsetSize, subsetSize, subsetSize);
+                                    break;
+                                default:
+                                    SkASSERT(false);
+                            }
+
+                            return new BitmapRegionDecoderBench(basename.c_str(), encoded.get(),
+                                    strategy, colorType, sampleSize, subset);
+                        }
+                        fCurrentSubsetType = 0;
+                        fCurrentBRDSampleSize++;
+                    }
+                    fCurrentBRDSampleSize = 0;
+                    fCurrentColorType++;
+                }
+                fCurrentColorType = 0;
+                fCurrentBRDStrategy++;
+            }
+            fCurrentBRDStrategy = 0;
+            fCurrentBRDImage++;
+        }
+
         return nullptr;
     }
 
@@ -907,7 +1035,8 @@
         kBottomRight_SubsetType = 4,
         kTranslate_SubsetType   = 5,
         kZoom_SubsetType        = 6,
-        kLast_SubsetType        = kZoom_SubsetType
+        kLast_SubsetType        = kZoom_SubsetType,
+        kLastSingle_SubsetType  = kBottomRight_SubsetType,
     };
 
     const BenchRegistry* fBenches;
@@ -932,9 +1061,12 @@
     int fCurrentCodec;
     int fCurrentImage;
     int fCurrentSubsetImage;
+    int fCurrentBRDImage;
     int fCurrentColorType;
     int fCurrentSubsetType;
     int fUseCodec;
+    int fCurrentBRDStrategy;
+    int fCurrentBRDSampleSize;
     int fCurrentAnimSKP;
 };
 
diff --git a/bench/subset/SubsetBenchPriv.h b/bench/subset/SubsetBenchPriv.h
index 02f7040..7e1dfb9 100644
--- a/bench/subset/SubsetBenchPriv.h
+++ b/bench/subset/SubsetBenchPriv.h
@@ -13,26 +13,6 @@
 #include "SkImageGenerator.h"
 
 /*
- * Convert the color type to a string
- */
-static const char* get_color_name(SkColorType colorType) {
-    switch(colorType) {
-        case kN32_SkColorType:
-            return "N32";
-        case kRGB_565_SkColorType:
-            return "565";
-        case kGray_8_SkColorType:
-            return "Gray8";
-        case kIndex_8_SkColorType:
-            return "Index8";
-        case kAlpha_8_SkColorType:
-            return "Alpha8";
-        default:
-            return "Unknown";
-    }
-}
-
-/*
  * If we plan to decode to kIndex8, we must create a color table and pass it to the
  * bitmap when we allocate pixels.  Otherwise, we simply allocate pixels using the
  * decode info.
diff --git a/bench/subset/SubsetSingleBench.cpp b/bench/subset/SubsetSingleBench.cpp
index ce00f20..2e5703f 100644
--- a/bench/subset/SubsetSingleBench.cpp
+++ b/bench/subset/SubsetSingleBench.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CodecBenchPriv.h"
 #include "SubsetSingleBench.h"
 #include "SubsetBenchPriv.h"
 #include "SkData.h"
@@ -39,7 +40,7 @@
     SkString baseName = SkOSPath::Basename(path.c_str());
 
     // Choose an informative color name
-    const char* colorName = get_color_name(fColorType);
+    const char* colorName = color_type_to_str(fColorType);
 
     fName.printf("%sSubsetSingle_%dx%d +%d_+%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
             fSubsetHeight, fOffsetLeft, fOffsetTop, baseName.c_str(), colorName);
diff --git a/bench/subset/SubsetTranslateBench.cpp b/bench/subset/SubsetTranslateBench.cpp
index 8f6f2ea..bed3580 100644
--- a/bench/subset/SubsetTranslateBench.cpp
+++ b/bench/subset/SubsetTranslateBench.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CodecBenchPriv.h"
 #include "SubsetTranslateBench.h"
 #include "SubsetBenchPriv.h"
 #include "SkData.h"
@@ -35,7 +36,7 @@
     SkString baseName = SkOSPath::Basename(path.c_str());
 
     // Choose an informative color name
-    const char* colorName = get_color_name(fColorType);
+    const char* colorName = color_type_to_str(fColorType);
 
     fName.printf("%sSubsetTranslate_%dx%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
             fSubsetHeight, baseName.c_str(), colorName);
diff --git a/bench/subset/SubsetZoomBench.cpp b/bench/subset/SubsetZoomBench.cpp
index bbdca75..655285f 100644
--- a/bench/subset/SubsetZoomBench.cpp
+++ b/bench/subset/SubsetZoomBench.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "CodecBenchPriv.h"
 #include "SubsetZoomBench.h"
 #include "SubsetBenchPriv.h"
 #include "SkData.h"
@@ -35,7 +36,7 @@
     SkString baseName = SkOSPath::Basename(path.c_str());
 
     // Choose an informative color name
-    const char* colorName = get_color_name(fColorType);
+    const char* colorName = color_type_to_str(fColorType);
 
     fName.printf("%sSubsetZoom_%dx%d_%s_%s", fUseCodec ? "Codec" : "Image", fSubsetWidth,
             fSubsetHeight, baseName.c_str(), colorName);