Refactor SkPicturePlayback for SkPictureReplacementPlayback

This CL is intended to maximize code reuse when SkPictureReplacementPlayback is split off.

R=mtklein@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/378343002
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 734a54a..eee63d2 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -106,17 +106,7 @@
     return bitmap;
 }
 
-void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
-    AutoResetOpID aroi(this);
-    SkASSERT(0 == fCurOffset);
-
-    // kDrawComplete will be the signal that we have reached the end of
-    // the command stream
-    static const uint32_t kDrawComplete = SK_MaxU32;
-
-    SkReader32 reader(fPictureData->fOpData->bytes(), fPictureData->fOpData->size());
-    SkAutoTDelete<const SkPicture::OperationList> activeOpsList;
-    const SkTDArray<void*>* activeOps = NULL;
+const SkPicture::OperationList* SkPicturePlayback::getActiveOps(const SkCanvas* canvas) {
 
     if (fUseBBH && NULL != fPictureData->fStateTree && NULL != fPictureData->fBoundingHierarchy) {
         SkRect clipBounds;
@@ -124,28 +114,151 @@
             SkIRect query;
             clipBounds.roundOut(&query);
 
-            activeOpsList.reset(fPictureData->getActiveOps(query));
-            if (NULL != activeOpsList.get()) {
-                if (0 == activeOpsList->numOps()) {
-                    return;     // nothing to draw
+            return fPictureData->getActiveOps(query);
+        }
+    } 
+
+    return NULL;
+}
+
+// Initialize the state tree iterator. Return false if there is nothing left to draw.
+bool SkPicturePlayback::initIterator(SkPictureStateTree::Iterator* iter, 
+                                     SkCanvas* canvas,
+                                     const SkPicture::OperationList *activeOpsList) {
+
+    if (NULL != activeOpsList) {
+        if (0 == activeOpsList->numOps()) {
+            return false;  // nothing to draw
+        }
+
+        fPictureData->fStateTree->initIterator(iter, activeOpsList->fOps, canvas);
+    }
+
+    return true;
+}
+
+bool SkPicturePlayback::replaceOps(SkPictureStateTree::Iterator* iter, 
+                                   SkReader32* reader, 
+                                   SkCanvas* canvas,
+                                   const SkMatrix& initialMatrix) {
+    if (NULL != fReplacements) {
+        // Potentially replace a block of operations with a single drawBitmap call
+        SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp =
+                                            fReplacements->lookupByStart(reader->offset());
+        if (NULL != temp) {
+            SkASSERT(NULL != temp->fBM);
+            SkASSERT(NULL != temp->fPaint);
+            canvas->save();
+            canvas->setMatrix(initialMatrix);
+            SkRect src = SkRect::Make(temp->fSrcRect);
+            SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY,
+                                          temp->fSrcRect.width(),
+                                          temp->fSrcRect.height());
+            canvas->drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint);
+            canvas->restore();
+
+            if (iter->isValid()) {
+                // This save is needed since the BBH will automatically issue
+                // a restore to balanced the saveLayer we're skipping
+                canvas->save();
+
+                // At this point we know that the PictureStateTree was aiming
+                // for some draw op within temp's saveLayer (although potentially
+                // in a separate saveLayer nested inside it).
+                // We need to skip all the operations inside temp's range
+                // along with all the associated state changes but update
+                // the state tree to the first operation outside temp's range.
+
+                uint32_t skipTo;
+                do {
+                    skipTo = iter->nextDraw();
+                    if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
+                        break;
+                    }
+
+                    if (skipTo <= temp->fStop) {
+                        reader->setOffset(skipTo);
+                        uint32_t size;
+                        DrawType op = ReadOpAndSize(reader, &size);
+                        // Since we are relying on the normal SkPictureStateTree
+                        // playback we need to convert any nested saveLayer calls
+                        // it may issue into saves (so that all its internal
+                        // restores will be balanced).
+                        if (SAVE_LAYER == op) {
+                            canvas->save();
+                        }
+                    }
+                } while (skipTo <= temp->fStop);
+
+                if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
+                    reader->setOffset(reader->size());      // skip to end
+                    return true;
                 }
 
-                activeOps = &(activeOpsList.get()->fOps);
+                reader->setOffset(skipTo);
+            } else {
+                reader->setOffset(temp->fStop);
+                uint32_t size;
+                SkDEBUGCODE(DrawType op = ) ReadOpAndSize(reader, &size);
+                SkASSERT(RESTORE == op);
             }
+
+            return true;
         }
     }
 
-    SkPictureStateTree::Iterator it = (NULL == activeOps) ?
-        SkPictureStateTree::Iterator() :
-        fPictureData->fStateTree->getIterator(*activeOps, canvas);
+    return false;
+}
 
-    if (it.isValid()) {
-        uint32_t skipTo = it.nextDraw();
-        if (kDrawComplete == skipTo) {
-            return;
+// If 'iter' is valid use it to skip forward through the picture.
+void SkPicturePlayback::StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader) {
+    if (iter->isValid()) {
+        uint32_t skipTo = iter->nextDraw();
+        if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
+            reader->setOffset(reader->size());  // skip to end
+        } else {
+            reader->setOffset(skipTo);
         }
-        reader.setOffset(skipTo);
     }
+}
+
+// Update the iterator and state tree to catch up with the skipped ops.
+void SkPicturePlayback::SkipIterTo(SkPictureStateTree::Iterator* iter,
+                                   SkReader32* reader,
+                                   uint32_t skipTo) {
+    SkASSERT(skipTo <= reader->size());
+    SkASSERT(reader->offset() <= skipTo); // should only be skipping forward
+
+    if (iter->isValid()) {
+        // If using a bounding box hierarchy, advance the state tree
+        // iterator until at or after skipTo
+        uint32_t adjustedSkipTo;
+        do {
+            adjustedSkipTo = iter->nextDraw();
+        } while (adjustedSkipTo < skipTo);
+        skipTo = adjustedSkipTo;
+    }
+    if (SkPictureStateTree::Iterator::kDrawComplete == skipTo) {
+        reader->setOffset(reader->size());  // skip to end
+    } else {
+        reader->setOffset(skipTo);
+    }
+}
+
+void SkPicturePlayback::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) {
+    AutoResetOpID aroi(this);
+    SkASSERT(0 == fCurOffset);
+
+    SkAutoTDelete<const SkPicture::OperationList> activeOpsList(this->getActiveOps(canvas));
+    SkPictureStateTree::Iterator it;
+
+    if (!this->initIterator(&it, canvas, activeOpsList.get())) {
+        return;  // nothing to draw
+    }
+
+    SkReader32 reader(fPictureData->opData()->bytes(), fPictureData->opData()->size());
+
+    StepIterator(&it, &reader);
 
     // Record this, so we can concat w/ it if we encounter a setMatrix()
     SkMatrix initialMatrix = canvas->getTotalMatrix();
@@ -153,109 +266,26 @@
     SkAutoCanvasRestore acr(canvas, false);
 
     while (!reader.eof()) {
-        if (callback && callback->abortDrawing()) {
+        if (NULL != callback && callback->abortDrawing()) {
             return;
         }
 
-        if (NULL != fReplacements) {
-            // Potentially replace a block of operations with a single drawBitmap call
-            SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp =
-                fReplacements->lookupByStart(reader.offset());
-            if (NULL != temp) {
-                SkASSERT(NULL != temp->fBM);
-                SkASSERT(NULL != temp->fPaint);
-                canvas->save();
-                canvas->setMatrix(initialMatrix);
-                SkRect src = SkRect::Make(temp->fSrcRect);
-                SkRect dst = SkRect::MakeXYWH(temp->fPos.fX, temp->fPos.fY,
-                                              temp->fSrcRect.width(),
-                                              temp->fSrcRect.height());
-                canvas->drawBitmapRectToRect(*temp->fBM, &src, dst, temp->fPaint);
-                canvas->restore();
-
-                if (it.isValid()) {
-                    // This save is needed since the BBH will automatically issue
-                    // a restore to balanced the saveLayer we're skipping
-                    canvas->save();
-
-                    // At this point we know that the PictureStateTree was aiming
-                    // for some draw op within temp's saveLayer (although potentially
-                    // in a separate saveLayer nested inside it).
-                    // We need to skip all the operations inside temp's range
-                    // along with all the associated state changes but update
-                    // the state tree to the first operation outside temp's range.
-
-                    uint32_t skipTo;
-                    do {
-                        skipTo = it.nextDraw();
-                        if (kDrawComplete == skipTo) {
-                            break;
-                        }
-
-                        if (skipTo <= temp->fStop) {
-                            reader.setOffset(skipTo);
-                            uint32_t size;
-                            DrawType op = ReadOpAndSize(&reader, &size);
-                            // Since we are relying on the normal SkPictureStateTree
-                            // playback we need to convert any nested saveLayer calls
-                            // it may issue into saves (so that all its internal
-                            // restores will be balanced).
-                            if (SAVE_LAYER == op) {
-                                canvas->save();
-                            }
-                        }
-                    } while (skipTo <= temp->fStop);
-
-                    if (kDrawComplete == skipTo) {
-                        break;
-                    }
-
-                    reader.setOffset(skipTo);
-                } else {
-                    reader.setOffset(temp->fStop);
-                    uint32_t size;
-                    SkDEBUGCODE(DrawType op = ) ReadOpAndSize(&reader, &size);
-                    SkASSERT(RESTORE == op);
-                }
-                continue;
-            }
+        if (this->replaceOps(&it, &reader, canvas, initialMatrix)) {
+            continue;
         }
 
         fCurOffset = reader.offset();
         uint32_t size;
         DrawType op = ReadOpAndSize(&reader, &size);
-        size_t skipTo = 0;
         if (NOOP == op) {
             // NOOPs are to be ignored - do not propagate them any further
-            skipTo = fCurOffset + size;
-        }
-
-        if (0 != skipTo) {
-            if (it.isValid()) {
-                // If using a bounding box hierarchy, advance the state tree
-                // iterator until at or after skipTo
-                uint32_t adjustedSkipTo;
-                do {
-                    adjustedSkipTo = it.nextDraw();
-                } while (adjustedSkipTo < skipTo);
-                skipTo = adjustedSkipTo;
-            }
-            if (kDrawComplete == skipTo) {
-                break;
-            }
-            reader.setOffset(skipTo);
+            SkipIterTo(&it, &reader, fCurOffset + size);
             continue;
         }
 
         this->handleOp(&reader, op, size, canvas, initialMatrix);
 
-        if (it.isValid()) {
-            uint32_t skipTo = it.nextDraw();
-            if (kDrawComplete == skipTo) {
-                break;
-            }
-            reader.setOffset(skipTo);
-        }
+        StepIterator(&it, &reader);
     }
 }
 
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index 4248e2d..6c32b15 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -9,6 +9,7 @@
 #define SkPicturePlayback_DEFINED
 
 #include "SkPictureFlat.h"  // for DrawType
+#include "SkPictureStateTree.h"
 
 class SkBitmap;
 class SkCanvas;
@@ -96,6 +97,18 @@
                   SkCanvas* canvas,
                   const SkMatrix& initialMatrix);
 
+    const SkPicture::OperationList* getActiveOps(const SkCanvas* canvas);
+    bool initIterator(SkPictureStateTree::Iterator* iter, 
+                      SkCanvas* canvas,
+                      const SkPicture::OperationList *activeOpsList);
+    static void StepIterator(SkPictureStateTree::Iterator* iter, SkReader32* reader);
+    static void SkipIterTo(SkPictureStateTree::Iterator* iter, 
+                           SkReader32* reader, uint32_t skipTo);
+    bool replaceOps(SkPictureStateTree::Iterator* iter, 
+                    SkReader32* reader, 
+                    SkCanvas* canvas,
+                    const SkMatrix& initialMatrix);
+
     static DrawType ReadOpAndSize(SkReader32* reader, uint32_t* size);
 
     class AutoResetOpID {
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
index fdd8646..d2f0e6e 100644
--- a/src/core/SkPictureStateTree.cpp
+++ b/src/core/SkPictureStateTree.cpp
@@ -73,9 +73,10 @@
     this->appendNode(offset);
 }
 
-SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
-                                                             SkCanvas* canvas) {
-    return Iterator(draws, canvas, &fRoot);
+void SkPictureStateTree::initIterator(SkPictureStateTree::Iterator* iter,
+                                      const SkTDArray<void*>& draws,
+                                      SkCanvas* canvas) {
+    iter->init(draws, canvas, &fRoot);
 }
 
 void SkPictureStateTree::appendNode(size_t offset) {
@@ -88,15 +89,16 @@
     fCurrentState.fNode = n;
 }
 
-SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
-    : fDraws(&draws)
-    , fCanvas(canvas)
-    , fCurrentNode(root)
-    , fPlaybackMatrix(canvas->getTotalMatrix())
-    , fCurrentMatrix(NULL)
-    , fPlaybackIndex(0)
-    , fSave(false)
-    , fValid(true) {
+void SkPictureStateTree::Iterator::init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root) {
+    SkASSERT(!fValid);
+    fDraws = &draws;
+    fCanvas = canvas;
+    fCurrentNode = root;
+    fPlaybackMatrix = canvas->getTotalMatrix();
+    fCurrentMatrix = NULL;
+    fPlaybackIndex = 0;
+    fSave = false;
+    fValid = true;
 }
 
 void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) {
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
index da51a5b..15bb02f 100644
--- a/src/core/SkPictureStateTree.h
+++ b/src/core/SkPictureStateTree.h
@@ -50,11 +50,13 @@
     Draw* appendDraw(size_t offset);
 
     /**
-     * Given a list of draws, and a canvas, returns an iterator that produces the correct sequence
-     * of offsets into the command buffer to carry out those calls with correct matrix/clip state.
-     * This handles saves/restores, and does all necessary matrix setup.
+     * Given a list of draws, and a canvas, initialize an iterator that produces the correct 
+     * sequence of offsets into the command buffer to carry out those calls with correct 
+     * matrix/clip state. This handles saves/restores, and does all necessary matrix setup.
      */
-    Iterator getIterator(const SkTDArray<void*>& draws, SkCanvas* canvas);
+    void initIterator(SkPictureStateTree::Iterator* iter, 
+                      const SkTDArray<void*>& draws, 
+                      SkCanvas* canvas);
 
     void appendSave();
     void appendSaveLayer(size_t offset);
@@ -83,11 +85,11 @@
         */
         uint32_t nextDraw();
         static const uint32_t kDrawComplete = SK_MaxU32;
-        Iterator() : fPlaybackMatrix(), fValid(false) { }
+        Iterator() : fValid(false) { }
         bool isValid() const { return fValid; }
 
     private:
-        Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
+        void init(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
 
         void setCurrentMatrix(const SkMatrix*);
 
@@ -104,7 +106,7 @@
         SkTDArray<Node*> fNodes;
 
         // The matrix of the canvas we're playing back into
-        const SkMatrix fPlaybackMatrix;
+        SkMatrix fPlaybackMatrix;
 
         // Cache of current matrix, so we can avoid redundantly setting it
         const SkMatrix* fCurrentMatrix;