SkRecord: make culling work if SkRecordAnnotateCullingPairs is called.

  - Allow stateful functors; allow visit()/mutate() at a given index; add count().
  - Annotate cull push/pop pairs on the PushCull records.  (tested)
  - Use those annotations to skip ahead in SkRecordDraw.   (not yet tested beyond dm --skr)
  - Make SkRecordDraw a function, move its implementation to a .cpp.

BUG=skia:2378
R=fmalita@chromium.org, mtklein@google.com

Author: mtklein@chromium.org

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14101 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/record/SkRecord.h b/src/record/SkRecord.h
index 4013874..0fe7918 100644
--- a/src/record/SkRecord.h
+++ b/src/record/SkRecord.h
@@ -21,33 +21,49 @@
 public:
     SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof(void*))
         : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstReserveCount) {}
-    ~SkRecord() { this->mutate(Destroyer()); }
+
+    ~SkRecord() {
+        Destroyer destroyer;
+        this->mutate(destroyer);
+    }
+
+    unsigned count() const { return fCount; }
 
     // Accepts a visitor functor with this interface:
     //   template <typename T>
-    //   void operator()()(const T& record) { ... }
+    //   void operator()(const T& record) { ... }
     // This operator() must be defined for at least all SkRecords::*; your compiler will help you
     // get this right.
-    //
-    // f will be called on each recorded canvas call in the order they were append()ed.
     template <typename F>
-    void visit(F f) const {
+    void visit(unsigned i, F& f) const {
+        SkASSERT(i < this->count());
+        fRecords[i].visit(fTypes[i], f);
+    }
+
+    // As above.  f will be called on each recorded canvas call in the order they were append()ed.
+    template <typename F>
+    void visit(F& f) const {
         for (unsigned i = 0; i < fCount; i++) {
-            fRecords[i].visit(fTypes[i], f);
+            this->visit(i, f);
         }
     }
 
     // Accepts a visitor functor with this interface:
     //   template <typename T>
-    //   void operator()()(T* record) { ... }
+    //   void operator()(T* record) { ... }
     // This operator() must be defined for at least all SkRecords::*; again, your compiler will help
     // you get this right.
-    //
-    // f will be called on each recorded canvas call in the order they were append()ed.
     template <typename F>
-    void mutate(F f) {
+    void mutate(unsigned i, F& f) {
+        SkASSERT(i < this->count());
+        fRecords[i].mutate(fTypes[i], f);
+    }
+
+    // As above.  f will be called on each recorded canvas call in the order they were append()ed.
+    template <typename F>
+    void mutate(F& f) {
         for (unsigned i = 0; i < fCount; i++) {
-            fRecords[i].mutate(fTypes[i], f);
+            this->mutate(i, f);
         }
     }
 
@@ -154,7 +170,7 @@
         // Visit this record with functor F (see public API above) assuming the record we're
         // pointing to has this type.
         template <typename F>
-        void visit(Type8 type, F f) const {
+        void visit(Type8 type, F& f) const {
         #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>());
             switch(type) { SK_RECORD_TYPES(CASE) }
         #undef CASE
@@ -163,7 +179,7 @@
         // Mutate this record with functor F (see public API above) assuming the record we're
         // pointing to has this type.
         template <typename F>
-        void mutate(Type8 type, F f) {
+        void mutate(Type8 type, F& f) {
         #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>());
             switch(type) { SK_RECORD_TYPES(CASE) }
         #undef CASE
diff --git a/src/record/SkRecordCulling.cpp b/src/record/SkRecordCulling.cpp
new file mode 100644
index 0000000..e866bd8
--- /dev/null
+++ b/src/record/SkRecordCulling.cpp
@@ -0,0 +1,38 @@
+#include "SkRecordCulling.h"
+
+#include "SkRecords.h"
+#include "SkTDArray.h"
+
+namespace {
+
+struct Annotator {
+    unsigned index;
+    SkTDArray<SkRecords::PushCull*> pushStack;
+
+    // Do nothing to most record types.
+    template <typename T> void operator()(T*) {}
+};
+
+template <> void Annotator::operator()(SkRecords::PushCull* push) {
+    // Store the push's index for now.  We'll calculate the offset using this in the paired pop.
+    push->popOffset = index;
+    pushStack.push(push);
+}
+
+template <> void Annotator::operator()(SkRecords::PopCull* pop) {
+    SkRecords::PushCull* push = pushStack.top();
+    pushStack.pop();
+
+    SkASSERT(index > push->popOffset);          // push->popOffset holds the index of the push.
+    push->popOffset = index - push->popOffset;  // Now it's the offset between push and pop.
+}
+
+}  // namespace
+
+void SkRecordAnnotateCullingPairs(SkRecord* record) {
+    Annotator annotator;
+
+    for (annotator.index = 0; annotator.index < record->count(); annotator.index++) {
+        record->mutate(annotator.index, annotator);
+    }
+}
diff --git a/src/record/SkRecordCulling.h b/src/record/SkRecordCulling.h
new file mode 100644
index 0000000..b8ed88c
--- /dev/null
+++ b/src/record/SkRecordCulling.h
@@ -0,0 +1,9 @@
+#ifndef SkRecordCulling_DEFINED
+#define SkRecordCulling_DEFINED
+
+#include "SkRecord.h"
+
+// Annotates PushCull records in record with the relative offset of their paired PopCull.
+void SkRecordAnnotateCullingPairs(SkRecord* record);
+
+#endif//SkRecordCulling_DEFINED
diff --git a/src/record/SkRecordDraw.cpp b/src/record/SkRecordDraw.cpp
new file mode 100644
index 0000000..66b48a3
--- /dev/null
+++ b/src/record/SkRecordDraw.cpp
@@ -0,0 +1,78 @@
+#include "SkRecordDraw.h"
+
+namespace {
+
+// This is an SkRecord visitor that will draw that SkRecord to an SkCanvas.
+struct Draw {
+    unsigned index;
+    SkCanvas* canvas;
+
+    // No base case, so we'll be compile-time checked that we implemented all possibilities below.
+    template <typename T> void operator()(const T&);
+};
+
+template <> void Draw::operator()(const SkRecords::PushCull& pushCull) {
+    if (pushCull.popOffset != SkRecords::kUnsetPopOffset &&
+        canvas->quickReject(pushCull.rect)) {
+        // We skip to the popCull, then the loop moves us just beyond it.
+        index += pushCull.popOffset;
+    } else {
+        canvas->pushCull(pushCull.rect);
+    }
+}
+
+// Nothing fancy below here.
+
+#define CASE(T) template <> void Draw::operator()(const SkRecords::T& r)
+
+CASE(Restore) { canvas->restore(); }
+CASE(Save) { canvas->save(r.flags); }
+CASE(SaveLayer) { canvas->saveLayer(r.bounds, r.paint, r.flags); }
+
+CASE(PopCull) { canvas->popCull(); }
+
+CASE(Concat) { canvas->concat(r.matrix); }
+CASE(SetMatrix) { canvas->setMatrix(r.matrix); }
+
+CASE(ClipPath) { canvas->clipPath(r.path, r.op, r.doAA); }
+CASE(ClipRRect) { canvas->clipRRect(r.rrect, r.op, r.doAA); }
+CASE(ClipRect) { canvas->clipRect(r.rect, r.op, r.doAA); }
+CASE(ClipRegion) { canvas->clipRegion(r.region, r.op); }
+
+CASE(Clear) { canvas->clear(r.color); }
+CASE(DrawBitmap) { canvas->drawBitmap(r.bitmap, r.left, r.top, r.paint); }
+CASE(DrawBitmapMatrix) { canvas->drawBitmapMatrix(r.bitmap, r.matrix, r.paint); }
+CASE(DrawBitmapNine) { canvas->drawBitmapNine(r.bitmap, r.center, r.dst, r.paint); }
+CASE(DrawBitmapRectToRect) {
+    canvas->drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags);
+}
+CASE(DrawDRRect) { canvas->drawDRRect(r.outer, r.inner, r.paint); }
+CASE(DrawOval) { canvas->drawOval(r.oval, r.paint); }
+CASE(DrawPaint) { canvas->drawPaint(r.paint); }
+CASE(DrawPath) { canvas->drawPath(r.path, r.paint); }
+CASE(DrawPoints) { canvas->drawPoints(r.mode, r.count, r.pts, r.paint); }
+CASE(DrawPosText) { canvas->drawPosText(r.text, r.byteLength, r.pos, r.paint); }
+CASE(DrawPosTextH) { canvas->drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint); }
+CASE(DrawRRect) { canvas->drawRRect(r.rrect, r.paint); }
+CASE(DrawRect) { canvas->drawRect(r.rect, r.paint); }
+CASE(DrawSprite) { canvas->drawSprite(r.bitmap, r.left, r.top, r.paint); }
+CASE(DrawText) { canvas->drawText(r.text, r.byteLength, r.x, r.y, r.paint); }
+CASE(DrawTextOnPath) { canvas->drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint); }
+CASE(DrawVertices) {
+    canvas->drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
+                         r.xmode.get(), r.indices, r.indexCount, r.paint);
+}
+#undef CASE
+
+}  // namespace
+
+void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) {
+    Draw draw;
+    draw.canvas = canvas;
+
+    for (draw.index = 0; draw.index < record.count(); draw.index++) {
+        record.visit(draw.index, draw);
+    }
+}
+
+
diff --git a/src/record/SkRecordDraw.h b/src/record/SkRecordDraw.h
index 042147d..684cc1b 100644
--- a/src/record/SkRecordDraw.h
+++ b/src/record/SkRecordDraw.h
@@ -2,64 +2,9 @@
 #define SkRecordDraw_DEFINED
 
 #include "SkRecord.h"
-#include "SkRecords.h"
 #include "SkCanvas.h"
 
-// This is an SkRecord visitor that will draw that SkRecord to an SkCanvas.
-
-struct SkRecordDraw {
-    explicit SkRecordDraw(SkCanvas* canvas) : canvas(canvas) {}
-
-    // No base case, so we'll be compile-time checked that we implemented all possibilities below.
-    template <typename T> void operator()(const T& record);
-
-    SkCanvas* canvas;
-};
-
-// Nothing fancy here.
-// The structs in SkRecord are completely isomorphic to their corresponding SkCanvas calls.
-
-#define CASE(T) template <> void SkRecordDraw::operator()(const SkRecords::T& r)
-
-CASE(Restore) { canvas->restore(); }
-CASE(Save) { canvas->save(r.flags); }
-CASE(SaveLayer) { canvas->saveLayer(r.bounds, r.paint, r.flags); }
-
-CASE(PushCull) { canvas->pushCull(r.rect); }
-CASE(PopCull) { canvas->popCull(); }
-
-CASE(Concat) { canvas->concat(r.matrix); }
-CASE(SetMatrix) { canvas->setMatrix(r.matrix); }
-
-CASE(ClipPath) { canvas->clipPath(r.path, r.op, r.doAA); }
-CASE(ClipRRect) { canvas->clipRRect(r.rrect, r.op, r.doAA); }
-CASE(ClipRect) { canvas->clipRect(r.rect, r.op, r.doAA); }
-CASE(ClipRegion) { canvas->clipRegion(r.region, r.op); }
-
-CASE(Clear) { canvas->clear(r.color); }
-CASE(DrawBitmap) { canvas->drawBitmap(r.bitmap, r.left, r.top, r.paint); }
-CASE(DrawBitmapMatrix) { canvas->drawBitmapMatrix(r.bitmap, r.matrix, r.paint); }
-CASE(DrawBitmapNine) { canvas->drawBitmapNine(r.bitmap, r.center, r.dst, r.paint); }
-CASE(DrawBitmapRectToRect) {
-    canvas->drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags);
-}
-CASE(DrawDRRect) { canvas->drawDRRect(r.outer, r.inner, r.paint); }
-CASE(DrawOval) { canvas->drawOval(r.oval, r.paint); }
-CASE(DrawPaint) { canvas->drawPaint(r.paint); }
-CASE(DrawPath) { canvas->drawPath(r.path, r.paint); }
-CASE(DrawPoints) { canvas->drawPoints(r.mode, r.count, r.pts, r.paint); }
-CASE(DrawPosText) { canvas->drawPosText(r.text, r.byteLength, r.pos, r.paint); }
-CASE(DrawPosTextH) { canvas->drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint); }
-CASE(DrawRRect) { canvas->drawRRect(r.rrect, r.paint); }
-CASE(DrawRect) { canvas->drawRect(r.rect, r.paint); }
-CASE(DrawSprite) { canvas->drawSprite(r.bitmap, r.left, r.top, r.paint); }
-CASE(DrawText) { canvas->drawText(r.text, r.byteLength, r.x, r.y, r.paint); }
-CASE(DrawTextOnPath) { canvas->drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint); }
-CASE(DrawVertices) {
-    canvas->drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
-                         r.xmode.get(), r.indices, r.indexCount, r.paint);
-}
-
-#undef CASE
+// Draw an SkRecord into an SkCanvas.
+void SkRecordDraw(const SkRecord&, SkCanvas*);
 
 #endif//SkRecordDraw_DEFINED
diff --git a/src/record/SkRecorder.cpp b/src/record/SkRecorder.cpp
index fabb4be..cba5f74 100644
--- a/src/record/SkRecorder.cpp
+++ b/src/record/SkRecorder.cpp
@@ -196,7 +196,7 @@
 }
 
 void SkRecorder::onPushCull(const SkRect& rect) {
-    APPEND(PushCull, rect);
+    APPEND(PushCull, rect, SkRecords::kUnsetPopOffset);
 }
 
 void SkRecorder::onPopCull() {
diff --git a/src/record/SkRecords.h b/src/record/SkRecords.h
index 17a8e84..961728a 100644
--- a/src/record/SkRecords.h
+++ b/src/record/SkRecords.h
@@ -127,7 +127,8 @@
 RECORD1(Save, SkCanvas::SaveFlags, flags);
 RECORD3(SaveLayer, SkRect*, bounds, SkPaint*, paint, SkCanvas::SaveFlags, flags);
 
-RECORD1(PushCull, SkRect, rect);
+static const unsigned kUnsetPopOffset = 0;
+RECORD2(PushCull, SkRect, rect, unsigned, popOffset);
 RECORD0(PopCull);
 
 RECORD1(Concat, SkMatrix, matrix);