Fixing SkPicture command pattern optimizations to make them work correctly with bounding box hierarchies

BUG=https://code.google.com/p/chromium/issues/detail?id=180645
TEST=render_pictures -r <skp_dir> --validate --bbh <grid|rtree> --mode tile 256 256

Author: junov@chromium.org

Reviewed By: robertphillips@google.com

Review URL: https://chromiumcodereview.appspot.com/12817011

git-svn-id: http://skia.googlecode.com/svn/trunk@8171 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 45412fe..38bbe33 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -502,20 +502,48 @@
 
 typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
                                      SkPaintDictionary* paintDict);
+enum PictureRecordOptType {
+    kRewind_OptType,  // Optimization rewinds the command stream
+    kCollapseSaveLayer_OptType,  // Optimization eliminates a save/restore pair
+};
 
+struct PictureRecordOpt {
+    PictureRecordOptProc fProc;
+    PictureRecordOptType fType;
+};
 /*
  * A list of the optimizations that are tried upon seeing a restore
  * TODO: add a real API for such optimizations
  *       Add the ability to fire optimizations on any op (not just RESTORE)
  */
-static const PictureRecordOptProc gPictureRecordOpts[] = {
-    collapse_save_clip_restore,
-#ifndef SK_IGNORE_PICTURE_RECORD_SAVE_LAYER_OPT
-    remove_save_layer1,
-    remove_save_layer2,
-#endif
+static const PictureRecordOpt gPictureRecordOpts[] = {
+    { collapse_save_clip_restore, kRewind_OptType },
+    { remove_save_layer1,         kCollapseSaveLayer_OptType },
+    { remove_save_layer2,         kCollapseSaveLayer_OptType }
 };
 
+// This is called after an optimization has been applied to the command stream
+// in order to adjust the contents and state of the bounding box hierarchy and
+// state tree to reflect the optimization.
+static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
+                                      SkBBoxHierarchy* boundingHierarchy) {
+    switch (opt) {
+    case kCollapseSaveLayer_OptType:
+        stateTree->saveCollapsed();
+        break;
+    case kRewind_OptType:
+        if (NULL != boundingHierarchy) {
+            boundingHierarchy->rewindInserts();
+        }
+        // Note: No need to touch the state tree for this to work correctly.
+        // Unused branches do not burden the playback, and pruning the tree
+        // would be O(N^2), so it is best to leave it alone.
+        break;
+    default:
+        SkASSERT(0);
+    }
+}
+
 void SkPictureRecord::restore() {
     // FIXME: SkDeferredCanvas needs to be refactored to respect
     // save/restore balancing so that the following test can be
@@ -536,10 +564,12 @@
     uint32_t initialOffset, size;
     size_t opt;
     for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
-        if ((*gPictureRecordOpts[opt])(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+        if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
             // Some optimization fired so don't add the RESTORE
             size = 0;
             initialOffset = fWriter.size();
+            apply_optimization_to_bbh(gPictureRecordOpts[opt].fType, 
+                                      fStateTree, fBoundingHierarchy);
             break;
         }
     }