Extend MultiPictureDraw GM

This new GM case is intended to exercise the matrix oriented aspects of layer caching.

R=jvanverth@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/582113004
diff --git a/gm/multipicturedraw.cpp b/gm/multipicturedraw.cpp
index 11756a7..eeb5909 100644
--- a/gm/multipicturedraw.cpp
+++ b/gm/multipicturedraw.cpp
@@ -13,6 +13,7 @@
 #include "SkSurface.h"
 
 static const SkScalar kRoot3Over2 = 0.86602545f;  // sin(60)
+static const SkScalar kRoot3      = 1.73205081f;
 
 static const int kHexSide = 30;
 static const int kNumHexX = 6;
@@ -20,6 +21,9 @@
 static const int kPicWidth = kNumHexX * kHexSide;
 static const int kPicHeight = SkScalarCeilToInt((kNumHexY - 0.5f) * 2 * kHexSide * kRoot3Over2);
 static const SkScalar kInset = 20.0f;
+static const int kNumPictures = 3;
+
+static const int kTriSide = 40;
 
 // Create a hexagon centered at (originX, originY)
 static SkPath make_hex_path(SkScalar originX, SkScalar originY) {
@@ -37,7 +41,7 @@
 // Make a picture that is a tiling of the plane with stroked hexagons where
 // each hexagon is in its own layer. The layers are to exercise Ganesh's
 // layer hoisting.
-static const SkPicture* make_picture(SkColor fillColor) {
+static const SkPicture* make_hex_plane_picture(SkColor fillColor) {
 
     // Create a hexagon with its center at the origin
     SkPath hex = make_hex_path(0, 0);
@@ -76,6 +80,93 @@
     return recorder.endRecording();
 }
 
+// Make an equilateral triangle path with its top corner at (originX, originY)
+static SkPath make_tri_path(SkScalar originX, SkScalar originY) {
+    SkPath tri;
+    tri.moveTo(originX, originY);
+    tri.rLineTo(SkScalarHalf(kTriSide), 1.5f * kTriSide / kRoot3);
+    tri.rLineTo(-kTriSide, 0);
+    tri.close();
+    return tri;
+}
+
+static const SkPicture* make_tri_picture() {
+    SkPath tri = make_tri_path(0, 0);
+
+    SkPaint fill;
+    fill.setStyle(SkPaint::kFill_Style);
+    fill.setColor(SK_ColorLTGRAY);;
+
+    SkPaint stroke;
+    stroke.setStyle(SkPaint::kStroke_Style);
+    stroke.setStrokeWidth(3);
+
+    SkPictureRecorder recorder;
+
+    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
+                                               SkIntToScalar(kPicHeight));
+    // The saveLayer/restore block is to exercise layer hoisting
+    canvas->saveLayer(NULL, NULL);
+        canvas->drawPath(tri, fill);
+        canvas->drawPath(tri, stroke);
+    canvas->restore();
+
+    return recorder.endRecording();
+}
+
+static const SkPicture* make_sub_picture(const SkPicture* tri) {
+    SkPictureRecorder recorder;
+
+    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
+                                               SkIntToScalar(kPicHeight));
+
+    canvas->scale(1.0f/2.0f, 1.0f/2.0f);
+
+    canvas->drawPicture(tri);
+
+    canvas->save();
+    canvas->translate(SkScalarHalf(kTriSide), 1.5f * kTriSide / kRoot3);
+    canvas->drawPicture(tri);
+    canvas->restore();
+
+    canvas->save();
+    canvas->translate(-SkScalarHalf(kTriSide), 1.5f * kTriSide / kRoot3);
+    canvas->drawPicture(tri);
+    canvas->restore();
+
+    return recorder.endRecording();
+}
+
+// Create a Sierpinkski-like picture that starts with a top row with a picture
+// that just contains a triangle. Subsequent rows take the prior row's picture, 
+// shrinks it and replicates it 3 times then draws and appropriate number of
+// copies of it.
+static const SkPicture* make_sierpinski_picture() {
+    SkAutoTUnref<const SkPicture> pic(make_tri_picture());
+
+    SkPictureRecorder recorder;
+
+    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
+                                               SkIntToScalar(kPicHeight));
+
+    static const int kNumLevels = 4;
+    for (int i = 0; i < kNumLevels; ++i) {
+        canvas->save();
+            canvas->translate(-i*kTriSide / 2.0f, 0);
+            for (int j = 0; j < i+1; ++j) {
+                canvas->drawPicture(pic);
+                canvas->translate(SkIntToScalar(kTriSide), 0);
+            }
+        canvas->restore();
+
+        pic.reset(make_sub_picture(pic));
+
+        canvas->translate(0, 1.5f * kTriSide / kRoot3);
+    }
+
+    return recorder.endRecording();
+}
+
 static SkSurface* create_compat_surface(SkCanvas* canvas, int width, int height) {
     SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
 
@@ -101,15 +192,15 @@
     SkPaint*   fPaint;
 };
 
-typedef void (*PFContentMtd)(SkCanvas* canvas, const SkPicture* pictures[2]);
+typedef void (*PFContentMtd)(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]);
 
 // Just a single picture with no clip
-static void no_clip(SkCanvas* canvas, const SkPicture* pictures[2]) {
+static void no_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
     canvas->drawPicture(pictures[0]);
 }
 
 // Two pictures with a rect clip on the second one
-static void rect_clip(SkCanvas* canvas, const SkPicture* pictures[2]) {
+static void rect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
     canvas->drawPicture(pictures[0]);
 
     SkRect rect = pictures[0]->cullRect();
@@ -121,7 +212,7 @@
 }
 
 // Two pictures with a round rect clip on the second one
-static void rrect_clip(SkCanvas* canvas, const SkPicture* pictures[2]) {
+static void rrect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
     canvas->drawPicture(pictures[0]);
 
     SkRect rect = pictures[0]->cullRect();
@@ -136,7 +227,7 @@
 }
 
 // Two pictures with a clip path on the second one
-static void path_clip(SkCanvas* canvas, const SkPicture* pictures[2]) {
+static void path_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
     canvas->drawPicture(pictures[0]);
 
     // Create a hexagon centered on the middle of the hex grid
@@ -148,7 +239,7 @@
 }
 
 // Two pictures with an inverse clip path on the second one
-static void invpath_clip(SkCanvas* canvas, const SkPicture* pictures[2]) {
+static void invpath_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
     canvas->drawPicture(pictures[0]);
 
     // Create a hexagon centered on the middle of the hex grid
@@ -160,16 +251,29 @@
     canvas->drawPicture(pictures[1]);
 }
 
+// Reuse a single base (triangular) picture a _lot_ (rotated, scaled and translated).
+static void sierpinski(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
+    canvas->save();
+        canvas->translate(kPicWidth / 2.0f, 0.0f);
+        canvas->drawPicture(pictures[2]);
+
+        canvas->rotate(180.0f);
+        canvas->translate(0.0f, -SkIntToScalar(kPicHeight));
+        canvas->drawPicture(pictures[2]);
+    canvas->restore();
+}
+
 static const PFContentMtd gContentMthds[] = {
     no_clip,
     rect_clip,
     rrect_clip,
     path_clip,
-    invpath_clip
+    invpath_clip,
+    sierpinski
 };
 
 static void create_content(SkMultiPictureDraw* mpd, PFContentMtd pfGen,
-                           const SkPicture* pictures[2],
+                           const SkPicture* pictures[kNumPictures],
                            SkCanvas* dest, const SkMatrix& xform) {
     SkAutoTUnref<SkPicture> composite;
 
@@ -188,13 +292,13 @@
 }
 
 typedef void(*PFLayoutMtd)(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd, 
-                           PFContentMtd pfGen, const SkPicture* pictures[2],
+                           PFContentMtd pfGen, const SkPicture* pictures[kNumPictures],
                            SkTArray<ComposeStep>* composeSteps);
 
 // Draw the content into a single canvas
 static void simple(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd, 
                    PFContentMtd pfGen, 
-                   const SkPicture* pictures[2],
+                   const SkPicture* pictures[kNumPictures],
                    SkTArray<ComposeStep> *composeSteps) {
 
     ComposeStep& step = composeSteps->push_back();
@@ -209,7 +313,7 @@
 // Draw the content into multiple canvases/tiles
 static void tiled(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
                   PFContentMtd pfGen, 
-                  const SkPicture* pictures[2],
+                  const SkPicture* pictures[kNumPictures],
                   SkTArray<ComposeStep> *composeSteps) {
     static const int kNumTilesX = 2;
     static const int kNumTilesY = 2;
@@ -264,8 +368,9 @@
             kRRectClipMulti_Content,
             kPathClipMulti_Content,
             kInvPathClipMulti_Content,
+            kSierpinski_Content,
 
-            kLast_Content = kInvPathClipMulti_Content
+            kLast_Content = kSierpinski_Content
         };
 
         static const int kContentCnt = kLast_Content + 1;
@@ -283,22 +388,26 @@
             SkASSERT(SK_ARRAY_COUNT(gLayoutMthds) == kLayoutCnt);
             SkASSERT(SK_ARRAY_COUNT(gContentMthds) == kContentCnt);
 
-            fPictures[0] = fPictures[1] = NULL;
+            for (int i = 0; i < kNumPictures; ++i) {
+                fPictures[i] = NULL;
+            }
         }
 
         virtual ~MultiPictureDraw() {
-            SkSafeUnref(fPictures[0]);
-            SkSafeUnref(fPictures[1]);
+            for (int i = 0; i < kNumPictures; ++i) {
+                SkSafeUnref(fPictures[i]);
+            }
         }
 
     protected:
         Content          fContent;
         Layout           fLayout;
-        const SkPicture* fPictures[2];
+        const SkPicture* fPictures[kNumPictures];
 
         virtual void onOnceBeforeDraw() SK_OVERRIDE {
-            fPictures[0] = make_picture(SK_ColorWHITE);
-            fPictures[1] = make_picture(SK_ColorGRAY);
+            fPictures[0] = make_hex_plane_picture(SK_ColorWHITE);
+            fPictures[1] = make_hex_plane_picture(SK_ColorGRAY);
+            fPictures[2] = SkRef(make_sierpinski_picture());
         }
 
         virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
@@ -326,7 +435,7 @@
 
         virtual SkString onShortName() SK_OVERRIDE {
             static const char* gContentNames[] = { 
-                "noclip", "rectclip", "rrectclip", "pathclip", "invpathclip" 
+                "noclip", "rectclip", "rrectclip", "pathclip", "invpathclip", "sierpinski"
             };
             static const char* gLayoutNames[] = { "simple", "tiled" };
 
@@ -357,6 +466,8 @@
                                                 MultiPictureDraw::kSimple_Layout));)
     DEF_GM(return SkNEW_ARGS(MultiPictureDraw, (MultiPictureDraw::kInvPathClipMulti_Content, 
                                                 MultiPictureDraw::kSimple_Layout));)
+    DEF_GM(return SkNEW_ARGS(MultiPictureDraw, (MultiPictureDraw::kSierpinski_Content,
+                                                MultiPictureDraw::kSimple_Layout));)
 
     DEF_GM(return SkNEW_ARGS(MultiPictureDraw, (MultiPictureDraw::kNoClipSingle_Content,     
                                                 MultiPictureDraw::kTiled_Layout));)
@@ -368,4 +479,6 @@
                                                 MultiPictureDraw::kTiled_Layout));)
     DEF_GM(return SkNEW_ARGS(MultiPictureDraw, (MultiPictureDraw::kInvPathClipMulti_Content, 
                                                 MultiPictureDraw::kTiled_Layout));)
+    DEF_GM(return SkNEW_ARGS(MultiPictureDraw, (MultiPictureDraw::kSierpinski_Content,
+                                                MultiPictureDraw::kTiled_Layout));)
 }