SkRecordPartialDraw with less code duplication

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

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/527423002
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index 0117ade..f7f0299 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -35,15 +35,29 @@
         }
     } else {
         // Draw all ops.
-        for (SkRecords::Draw draw(canvas); draw.index() < record.count(); draw.next()) {
+        SkRecords::Draw draw(canvas);
+        for (unsigned i = 0; i < record.count(); i++) {
             if (NULL != callback && callback->abortDrawing()) {
                 return;
             }
-            record.visit<void>(draw.index(), draw);
+            record.visit<void>(i, draw);
         }
     }
 }
 
+void SkRecordPartialDraw(const SkRecord& record,
+                         SkCanvas* canvas,
+                         const SkRect& clearRect,
+                         unsigned start, unsigned stop) {
+    SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
+
+    stop = SkTMin(stop, record.count());
+    SkRecords::PartialDraw draw(canvas, clearRect);
+    for (unsigned i = start; i < stop; i++) {
+        record.visit<void>(i, draw);
+    }
+}
+
 namespace SkRecords {
 
 // FIXME: SkBitmaps are stateful, so we need to copy them to play back in multiple threads.
diff --git a/src/core/SkRecordDraw.h b/src/core/SkRecordDraw.h
index 8da7fb5..033b76d 100644
--- a/src/core/SkRecordDraw.h
+++ b/src/core/SkRecordDraw.h
@@ -19,16 +19,16 @@
 // Draw an SkRecord into an SkCanvas.  A convenience wrapper around SkRecords::Draw.
 void SkRecordDraw(const SkRecord&, SkCanvas*, const SkBBoxHierarchy*, SkDrawPictureCallback*);
 
+// Draw a portion of an SkRecord into an SkCanvas while replacing clears with drawRects.
+void SkRecordPartialDraw(const SkRecord&, SkCanvas*, const SkRect&, unsigned start, unsigned stop);
+
 namespace SkRecords {
 
 // This is an SkRecord visitor that will draw that SkRecord to an SkCanvas.
 class Draw : SkNoncopyable {
 public:
     explicit Draw(SkCanvas* canvas)
-        : fInitialCTM(canvas->getTotalMatrix()), fCanvas(canvas), fIndex(0) {}
-
-    unsigned index() const { return fIndex; }
-    void next() { ++fIndex; }
+        : fInitialCTM(canvas->getTotalMatrix()), fCanvas(canvas) {}
 
     template <typename T> void operator()(const T& r) {
         this->draw(r);
@@ -40,7 +40,28 @@
 
     const SkMatrix fInitialCTM;
     SkCanvas* fCanvas;
-    unsigned fIndex;
+};
+
+// Used by SkRecordPartialDraw.
+class PartialDraw : public Draw {
+public:
+    PartialDraw(SkCanvas* canvas, const SkRect& clearRect)
+        : INHERITED(canvas), fClearRect(clearRect) {}
+
+    // Same as Draw for all ops except Clear.
+    template <typename T> void operator()(const T& r) {
+        this->INHERITED::operator()(r);
+    }
+    void operator()(const Clear& c) {
+        SkPaint p;
+        p.setColor(c.color);
+        DrawRect drawRect(p, fClearRect);
+        this->INHERITED::operator()(drawRect);
+    }
+
+private:
+    const SkRect fClearRect;
+    typedef Draw INHERITED;
 };
 
 }  // namespace SkRecords
diff --git a/tests/RecordDrawTest.cpp b/tests/RecordDrawTest.cpp
index 80f645d..11b835b 100644
--- a/tests/RecordDrawTest.cpp
+++ b/tests/RecordDrawTest.cpp
@@ -143,3 +143,55 @@
         REPORTER_ASSERT(r, !SkRect::MakeLTRB(+1, +1, 399, 479).contains(bbh.entries[i].bounds));
     }
 }
+
+// Base test to ensure start/stop range is respected
+DEF_TEST(RecordDraw_PartialStartStop, r) {
+    static const int kWidth = 10, kHeight = 10;
+
+    SkRect r1 = { 0, 0, kWidth,   kHeight };
+    SkRect r2 = { 0, 0, kWidth,   kHeight/2 };
+    SkRect r3 = { 0, 0, kWidth/2, kHeight };
+    SkPaint p;
+
+    SkRecord record;
+    SkRecorder recorder(&record, kWidth, kHeight);
+    recorder.drawRect(r1, p);
+    recorder.drawRect(r2, p);
+    recorder.drawRect(r3, p);
+
+    SkRecord rerecord;
+    SkRecorder canvas(&rerecord, kWidth, kHeight);
+    SkRecordPartialDraw(record, &canvas, r1, 1, 2); // replay just drawRect of r2
+
+    REPORTER_ASSERT(r, 3 == rerecord.count());
+    assert_type<SkRecords::Save>     (r, rerecord, 0);
+    assert_type<SkRecords::DrawRect> (r, rerecord, 1);
+    assert_type<SkRecords::Restore>  (r, rerecord, 2);
+
+    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
+    REPORTER_ASSERT(r, drawRect->rect == r2);
+}
+
+// Check that clears are converted to drawRects
+DEF_TEST(RecordDraw_PartialClear, r) {
+    static const int kWidth = 10, kHeight = 10;
+
+    SkRect rect = { 0, 0, kWidth, kHeight };
+
+    SkRecord record;
+    SkRecorder recorder(&record, kWidth, kHeight);
+    recorder.clear(SK_ColorRED);
+
+    SkRecord rerecord;
+    SkRecorder canvas(&rerecord, kWidth, kHeight);
+    SkRecordPartialDraw(record, &canvas, rect, 0, 1); // replay just the clear
+
+    REPORTER_ASSERT(r, 3 == rerecord.count());
+    assert_type<SkRecords::Save>    (r, rerecord, 0);
+    assert_type<SkRecords::DrawRect>(r, rerecord, 1);
+    assert_type<SkRecords::Restore> (r, rerecord, 2);
+
+    const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, rerecord, 1);
+    REPORTER_ASSERT(r, drawRect->rect == rect);
+    REPORTER_ASSERT(r, drawRect->paint.getColor() == SK_ColorRED);
+}
diff --git a/tools/DumpRecord.cpp b/tools/DumpRecord.cpp
index 6e679a5..c505123 100644
--- a/tools/DumpRecord.cpp
+++ b/tools/DumpRecord.cpp
@@ -20,6 +20,7 @@
     explicit Dumper(SkCanvas* canvas, int count, bool timeWithCommand)
         : fDigits(0)
         , fIndent(0)
+        , fIndex(0)
         , fDraw(canvas)
         , fTimeWithCommand(timeWithCommand) {
         while (count > 0) {
@@ -28,9 +29,6 @@
         }
     }
 
-    unsigned index() const { return fDraw.index(); }
-    void next() { fDraw.next(); }
-
     template <typename T>
     void operator()(const T& command) {
         Timer timer;
@@ -71,7 +69,7 @@
         if (!fTimeWithCommand) {
             printf("%6.1f ", time * 1000);
         }
-        printf("%*d ", fDigits, fDraw.index());
+        printf("%*d ", fDigits, fIndex++);
         for (int i = 0; i < fIndent; i++) {
             putchar('\t');
         }
@@ -96,6 +94,7 @@
 
     int fDigits;
     int fIndent;
+    int fIndex;
     SkRecords::Draw fDraw;
     const bool fTimeWithCommand;
 };
@@ -105,9 +104,8 @@
 void DumpRecord(const SkRecord& record,
                   SkCanvas* canvas,
                   bool timeWithCommand) {
-    for (Dumper dumper(canvas, record.count(), timeWithCommand);
-         dumper.index() < record.count();
-         dumper.next()) {
-        record.visit<void>(dumper.index(), dumper);
+    Dumper dumper(canvas, record.count(), timeWithCommand);
+    for (unsigned i = 0; i < record.count(); i++) {
+        record.visit<void>(i, dumper);
     }
 }