Counterproposal for skirting the BBH when the query fully contains the picture.

BUG=skia:
R=reed@google.com, robertphillips@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/485703002
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index 7af955a..8825123 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -197,12 +197,18 @@
     SkASSERT(NULL != canvas);
     SkASSERT(NULL != fData.get() || NULL != fRecord.get());
 
+    // If the query contains the whole picture, don't bother with the BBH.
+    SkRect clipBounds = { 0, 0, 0, 0 };
+    (void)canvas->getClipBounds(&clipBounds);
+    const bool useBBH = !clipBounds.contains(SkRect::MakeWH(this->width(), this->height()));
+
     if (NULL != fData.get()) {
         SkPicturePlayback playback(this);
+        playback.setUseBBH(useBBH);
         playback.draw(canvas, callback);
     }
     if (NULL != fRecord.get()) {
-        SkRecordDraw(*fRecord, canvas, fBBH.get(), callback);
+        SkRecordDraw(*fRecord, canvas, useBBH ? fBBH.get() : NULL, callback);
     }
 }
 
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index bc5e7eb..fda8488 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -17,13 +17,15 @@
     if (NULL != bbh) {
         // Draw only ops that affect pixels in the canvas's current clip.
         SkIRect query;
-#if 1   // TODO: Why is this the right way to make the query?  I'd think it'd be the else branch.
-        SkRect clipBounds;
-        canvas->getClipBounds(&clipBounds);
+
+        // The SkRecord and BBH were recorded in identity space.  This canvas
+        // is not necessarily in that same space.  getClipBounds() returns us
+        // this canvas' clip bounds transformed back into identity space, which
+        // lets us query the BBH.
+        SkRect clipBounds = { 0, 0, 0, 0 };
+        (void)canvas->getClipBounds(&clipBounds);
         clipBounds.roundOut(&query);
-#else
-        canvas->getClipDeviceBounds(&query);
-#endif
+
         SkTDArray<void*> ops;
         bbh->search(query, &ops);
 
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index b7e4bb6..61cc502 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkBBoxHierarchy.h"
 #include "SkBlurImageFilter.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
@@ -662,7 +663,7 @@
 }
 
 #ifdef SK_DEBUG
-// Ensure that deleting an empty SkPicture does not assert. Asserts only fire 
+// Ensure that deleting an empty SkPicture does not assert. Asserts only fire
 // in debug mode, so only run in debug mode.
 static void test_deleting_empty_picture() {
     SkPictureRecorder recorder;
@@ -1716,3 +1717,49 @@
     REPORTER_ASSERT(reporter, replayBM.getColor(30, 30) == 0xff000080);
     REPORTER_ASSERT(reporter, replayBM.getColor(55, 55) == 0xff800000);
 }
+
+struct CountingBBH : public SkBBoxHierarchy {
+    mutable int searchCalls;
+
+    CountingBBH() : searchCalls(0) {}
+
+    virtual void search(const SkIRect& query, SkTDArray<void*>* results) const {
+        this->searchCalls++;
+    }
+
+    // All other methods unimplemented.
+    virtual void insert(void* data, const SkIRect& bounds, bool defer) {}
+    virtual void flushDeferredInserts() {}
+    virtual void clear() {}
+    virtual int getCount() const { return 0; }
+    virtual int getDepth() const { return 0; }
+    virtual void rewindInserts() {}
+};
+
+class SpoonFedBBHFactory : public SkBBHFactory {
+public:
+    explicit SpoonFedBBHFactory(SkBBoxHierarchy* bbh) : fBBH(bbh) {}
+    virtual SkBBoxHierarchy* operator()(int width, int height) const {
+        return SkRef(fBBH);
+    }
+private:
+    SkBBoxHierarchy* fBBH;
+};
+
+// When the canvas clip covers the full picture, we don't need to call the BBH.
+DEF_TEST(Picture_SkipBBH, r) {
+    CountingBBH bbh;
+    SpoonFedBBHFactory factory(&bbh);
+
+    SkPictureRecorder recorder;
+    recorder.beginRecording(320, 240, &factory);
+    SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
+
+    SkCanvas big(640, 480), small(300, 200);
+
+    picture->draw(&big);
+    REPORTER_ASSERT(r, bbh.searchCalls == 0);
+
+    picture->draw(&small);
+    REPORTER_ASSERT(r, bbh.searchCalls == 1);
+}