• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2014 Google Inc.
3   *
4   * Use of this source code is governed by a BSD-style license that can be
5   * found in the LICENSE file.
6   */
7  
8  #include "gm/gm.h"
9  #include "include/core/SkBBHFactory.h"
10  #include "include/core/SkBlendMode.h"
11  #include "include/core/SkCanvas.h"
12  #include "include/core/SkColor.h"
13  #include "include/core/SkColorFilter.h"
14  #include "include/core/SkImageInfo.h"
15  #include "include/core/SkMatrix.h"
16  #include "include/core/SkMultiPictureDraw.h"
17  #include "include/core/SkPaint.h"
18  #include "include/core/SkPath.h"
19  #include "include/core/SkPicture.h"
20  #include "include/core/SkPictureRecorder.h"
21  #include "include/core/SkRRect.h"
22  #include "include/core/SkRect.h"
23  #include "include/core/SkRefCnt.h"
24  #include "include/core/SkScalar.h"
25  #include "include/core/SkSize.h"
26  #include "include/core/SkString.h"
27  #include "include/core/SkSurface.h"
28  #include "include/core/SkTypes.h"
29  #include "include/private/SkTArray.h"
30  #include "tools/ToolUtils.h"
31  
32  constexpr SkScalar kRoot3Over2 = 0.86602545f;  // sin(60)
33  constexpr SkScalar kRoot3      = 1.73205081f;
34  
35  constexpr int kHexSide = 30;
36  constexpr int kNumHexX = 6;
37  constexpr int kNumHexY = 6;
38  constexpr int kPicWidth = kNumHexX * kHexSide;
39  constexpr int kPicHeight = (int)((kNumHexY - 0.5f) * 2 * kHexSide * kRoot3Over2 + 0.5f);
40  constexpr SkScalar kInset = 20.0f;
41  constexpr int kNumPictures = 4;
42  
43  constexpr int kTriSide = 40;
44  
45  // Create a hexagon centered at (originX, originY)
make_hex_path(SkScalar originX,SkScalar originY)46  static SkPath make_hex_path(SkScalar originX, SkScalar originY) {
47      SkPath hex;
48      hex.moveTo(originX-kHexSide, originY);
49      hex.rLineTo(SkScalarHalf(kHexSide), kRoot3Over2 * kHexSide);
50      hex.rLineTo(SkIntToScalar(kHexSide), 0);
51      hex.rLineTo(SkScalarHalf(kHexSide), -kHexSide * kRoot3Over2);
52      hex.rLineTo(-SkScalarHalf(kHexSide), -kHexSide * kRoot3Over2);
53      hex.rLineTo(-SkIntToScalar(kHexSide), 0);
54      hex.close();
55      return hex;
56  }
57  
58  // Make a picture that is a tiling of the plane with stroked hexagons where
59  // each hexagon is in its own layer. The layers are to exercise Ganesh's
60  // layer hoisting.
make_hex_plane_picture(SkColor fillColor)61  static sk_sp<SkPicture> make_hex_plane_picture(SkColor fillColor) {
62  
63      // Create a hexagon with its center at the origin
64      SkPath hex = make_hex_path(0, 0);
65  
66      SkPaint fill;
67      fill.setStyle(SkPaint::kFill_Style);
68      fill.setColor(fillColor);
69  
70      SkPaint stroke;
71      stroke.setStyle(SkPaint::kStroke_Style);
72      stroke.setStrokeWidth(3);
73  
74      SkPictureRecorder recorder;
75      SkRTreeFactory bbhFactory;
76  
77      SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
78                                                 SkIntToScalar(kPicHeight),
79                                                 &bbhFactory);
80  
81      SkScalar xPos, yPos = 0;
82  
83      for (int y = 0; y < kNumHexY; ++y) {
84          xPos = 0;
85  
86          for (int x = 0; x < kNumHexX; ++x) {
87              canvas->saveLayer(nullptr, nullptr);
88              canvas->translate(xPos, yPos + ((x % 2) ? kRoot3Over2 * kHexSide : 0));
89              canvas->drawPath(hex, fill);
90              canvas->drawPath(hex, stroke);
91              canvas->restore();
92  
93              xPos += 1.5f * kHexSide;
94          }
95  
96          yPos += 2 * kHexSide * kRoot3Over2;
97      }
98  
99      return recorder.finishRecordingAsPicture();
100  }
101  
102  // Create a picture that consists of a single large layer that is tiled
103  // with hexagons.
104  // This is intended to exercise the layer hoisting code's clip handling (in
105  // tile mode).
make_single_layer_hex_plane_picture()106  static sk_sp<SkPicture> make_single_layer_hex_plane_picture() {
107  
108      // Create a hexagon with its center at the origin
109      SkPath hex = make_hex_path(0, 0);
110  
111      SkPaint whiteFill;
112      whiteFill.setStyle(SkPaint::kFill_Style);
113      whiteFill.setColor(SK_ColorWHITE);
114  
115      SkPaint greyFill;
116      greyFill.setStyle(SkPaint::kFill_Style);
117      greyFill.setColor(SK_ColorLTGRAY);
118  
119      SkPaint stroke;
120      stroke.setStyle(SkPaint::kStroke_Style);
121      stroke.setStrokeWidth(3);
122  
123      SkPictureRecorder recorder;
124      SkRTreeFactory bbhFactory;
125  
126      constexpr SkScalar kBig = 10000.0f;
127      SkCanvas* canvas = recorder.beginRecording(kBig, kBig, &bbhFactory);
128  
129      canvas->saveLayer(nullptr, nullptr);
130  
131      SkScalar xPos = 0.0f, yPos = 0.0f;
132  
133      for (int y = 0; yPos < kBig; ++y) {
134          xPos = 0;
135  
136          for (int x = 0; xPos < kBig; ++x) {
137              canvas->save();
138              canvas->translate(xPos, yPos + ((x % 2) ? kRoot3Over2 * kHexSide : 0));
139              // The color of the filled hex is swapped to yield a different
140              // pattern in each tile. This allows an error in layer hoisting (e.g.,
141              // the clip isn't blocking cache reuse) to cause a visual discrepancy.
142              canvas->drawPath(hex, ((x+y) % 3) ? whiteFill : greyFill);
143              canvas->drawPath(hex, stroke);
144              canvas->restore();
145  
146              xPos += 1.5f * kHexSide;
147          }
148  
149          yPos += 2 * kHexSide * kRoot3Over2;
150      }
151  
152      canvas->restore();
153  
154      return recorder.finishRecordingAsPicture();
155  }
156  
157  // Make an equilateral triangle path with its top corner at (originX, originY)
make_tri_path(SkScalar originX,SkScalar originY)158  static SkPath make_tri_path(SkScalar originX, SkScalar originY) {
159      SkPath tri;
160      tri.moveTo(originX, originY);
161      tri.rLineTo(SkScalarHalf(kTriSide), 1.5f * kTriSide / kRoot3);
162      tri.rLineTo(-kTriSide, 0);
163      tri.close();
164      return tri;
165  }
166  
make_tri_picture()167  static sk_sp<SkPicture> make_tri_picture() {
168      SkPath tri = make_tri_path(SkScalarHalf(kTriSide), 0);
169  
170      SkPaint fill;
171      fill.setStyle(SkPaint::kFill_Style);
172      fill.setColor(SK_ColorLTGRAY);
173  
174      SkPaint stroke;
175      stroke.setStyle(SkPaint::kStroke_Style);
176      stroke.setStrokeWidth(3);
177  
178      SkPictureRecorder recorder;
179      SkRTreeFactory bbhFactory;
180  
181      SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
182                                                 SkIntToScalar(kPicHeight),
183                                                 &bbhFactory);
184      SkRect r = tri.getBounds();
185      r.outset(2.0f, 2.0f);       // outset for stroke
186      canvas->clipRect(r);
187      // The saveLayer/restore block is to exercise layer hoisting
188      canvas->saveLayer(nullptr, nullptr);
189          canvas->drawPath(tri, fill);
190          canvas->drawPath(tri, stroke);
191      canvas->restore();
192  
193      return recorder.finishRecordingAsPicture();
194  }
195  
make_sub_picture(const SkPicture * tri)196  static sk_sp<SkPicture> make_sub_picture(const SkPicture* tri) {
197      SkPictureRecorder recorder;
198      SkRTreeFactory bbhFactory;
199  
200      SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
201                                                 SkIntToScalar(kPicHeight),
202                                                 &bbhFactory);
203  
204      canvas->scale(1.0f/2.0f, 1.0f/2.0f);
205  
206      canvas->save();
207      canvas->translate(SkScalarHalf(kTriSide), 0);
208      canvas->drawPicture(tri);
209      canvas->restore();
210  
211      canvas->save();
212      canvas->translate(SkIntToScalar(kTriSide), 1.5f * kTriSide / kRoot3);
213      canvas->drawPicture(tri);
214      canvas->restore();
215  
216      canvas->save();
217      canvas->translate(0, 1.5f * kTriSide / kRoot3);
218      canvas->drawPicture(tri);
219      canvas->restore();
220  
221      return recorder.finishRecordingAsPicture();
222  }
223  
224  // Create a Sierpinkski-like picture that starts with a top row with a picture
225  // that just contains a triangle. Subsequent rows take the prior row's picture,
226  // shrinks it and replicates it 3 times then draws and appropriate number of
227  // copies of it.
make_sierpinski_picture()228  static sk_sp<SkPicture> make_sierpinski_picture() {
229      sk_sp<SkPicture> pic(make_tri_picture());
230  
231      SkPictureRecorder recorder;
232      SkRTreeFactory bbhFactory;
233  
234      SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
235                                                 SkIntToScalar(kPicHeight),
236                                                 &bbhFactory);
237  
238      constexpr int kNumLevels = 4;
239      for (int i = 0; i < kNumLevels; ++i) {
240          canvas->save();
241              canvas->translate(kPicWidth/2 - (i+1) * (kTriSide/2.0f), 0.0f);
242              for (int j = 0; j < i+1; ++j) {
243                  canvas->drawPicture(pic);
244                  canvas->translate(SkIntToScalar(kTriSide), 0);
245              }
246          canvas->restore();
247  
248          pic = make_sub_picture(pic.get());
249  
250          canvas->translate(0, 1.5f * kTriSide / kRoot3);
251      }
252  
253      return recorder.finishRecordingAsPicture();
254  }
255  
create_compat_surface(SkCanvas * canvas,int width,int height)256  static sk_sp<SkSurface> create_compat_surface(SkCanvas* canvas, int width, int height) {
257      SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
258  
259      return ToolUtils::makeSurface(canvas, info);
260  }
261  
262  // This class stores the information required to compose all the result
263  // fragments potentially generated by the MultiPictureDraw object
264  class ComposeStep {
265  public:
ComposeStep()266      ComposeStep() : fX(0.0f), fY(0.0f), fPaint(nullptr) { }
~ComposeStep()267      ~ComposeStep() {
268          delete fPaint;
269      }
270  
271      sk_sp<SkSurface> fSurf;
272      SkScalar   fX;
273      SkScalar   fY;
274      SkPaint*   fPaint;
275  };
276  
277  typedef void (*PFContentMtd)(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]);
278  
279  // Just a single picture with no clip
no_clip(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])280  static void no_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
281      canvas->drawPicture(pictures[0]);
282  }
283  
284  // Two pictures with a rect clip on the second one
rect_clip(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])285  static void rect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
286      canvas->drawPicture(pictures[0]);
287  
288      SkRect rect = pictures[0]->cullRect();
289      rect.inset(kInset, kInset);
290  
291      canvas->clipRect(rect);
292  
293      canvas->drawPicture(pictures[1]);
294  }
295  
296  // Two pictures with a round rect clip on the second one
rrect_clip(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])297  static void rrect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
298      canvas->drawPicture(pictures[0]);
299  
300      SkRect rect = pictures[0]->cullRect();
301      rect.inset(kInset, kInset);
302  
303      SkRRect rrect;
304      rrect.setRectXY(rect, kInset, kInset);
305  
306      canvas->clipRRect(rrect);
307  
308      canvas->drawPicture(pictures[1]);
309  }
310  
311  // Two pictures with a clip path on the second one
path_clip(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])312  static void path_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
313      canvas->drawPicture(pictures[0]);
314  
315      // Create a hexagon centered on the middle of the hex grid
316      SkPath hex = make_hex_path((kNumHexX / 2.0f) * kHexSide, kNumHexY * kHexSide * kRoot3Over2);
317  
318      canvas->clipPath(hex);
319  
320      canvas->drawPicture(pictures[1]);
321  }
322  
323  // Two pictures with an inverse clip path on the second one
invpath_clip(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])324  static void invpath_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
325      canvas->drawPicture(pictures[0]);
326  
327      // Create a hexagon centered on the middle of the hex grid
328      SkPath hex = make_hex_path((kNumHexX / 2.0f) * kHexSide, kNumHexY * kHexSide * kRoot3Over2);
329      hex.setFillType(SkPath::kInverseEvenOdd_FillType);
330  
331      canvas->clipPath(hex);
332  
333      canvas->drawPicture(pictures[1]);
334  }
335  
336  // Reuse a single base (triangular) picture a _lot_ (rotated, scaled and translated).
sierpinski(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])337  static void sierpinski(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
338      canvas->save();
339          canvas->drawPicture(pictures[2]);
340  
341          canvas->rotate(180.0f);
342          canvas->translate(-SkIntToScalar(kPicWidth), -SkIntToScalar(kPicHeight));
343          canvas->drawPicture(pictures[2]);
344      canvas->restore();
345  }
346  
big_layer(SkCanvas * canvas,const SkPicture * pictures[kNumPictures])347  static void big_layer(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
348      canvas->drawPicture(pictures[3]);
349  }
350  
351  constexpr PFContentMtd gContentMthds[] = {
352      no_clip,
353      rect_clip,
354      rrect_clip,
355      path_clip,
356      invpath_clip,
357      sierpinski,
358      big_layer,
359  };
360  
create_content(SkMultiPictureDraw * mpd,PFContentMtd pfGen,const SkPicture * pictures[kNumPictures],SkCanvas * dest,const SkMatrix & xform)361  static void create_content(SkMultiPictureDraw* mpd, PFContentMtd pfGen,
362                             const SkPicture* pictures[kNumPictures],
363                             SkCanvas* dest, const SkMatrix& xform) {
364      sk_sp<SkPicture> composite;
365  
366      {
367          SkPictureRecorder recorder;
368          SkRTreeFactory bbhFactory;
369  
370          SkCanvas* pictureCanvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
371                                                            SkIntToScalar(kPicHeight),
372                                                            &bbhFactory);
373  
374          (*pfGen)(pictureCanvas, pictures);
375  
376          composite = recorder.finishRecordingAsPicture();
377      }
378  
379      mpd->add(dest, composite.get(), &xform);
380  }
381  
382  typedef void(*PFLayoutMtd)(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
383                             PFContentMtd pfGen, const SkPicture* pictures[kNumPictures],
384                             SkTArray<ComposeStep>* composeSteps);
385  
386  // Draw the content into a single canvas
simple(SkCanvas * finalCanvas,SkMultiPictureDraw * mpd,PFContentMtd pfGen,const SkPicture * pictures[kNumPictures],SkTArray<ComposeStep> * composeSteps)387  static void simple(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
388                     PFContentMtd pfGen,
389                     const SkPicture* pictures[kNumPictures],
390                     SkTArray<ComposeStep> *composeSteps) {
391  
392      ComposeStep& step = composeSteps->push_back();
393  
394      step.fSurf = create_compat_surface(finalCanvas, kPicWidth, kPicHeight);
395  
396      SkCanvas* subCanvas = step.fSurf->getCanvas();
397  
398      create_content(mpd, pfGen, pictures, subCanvas, SkMatrix::I());
399  }
400  
401  // Draw the content into multiple canvases/tiles
tiled(SkCanvas * finalCanvas,SkMultiPictureDraw * mpd,PFContentMtd pfGen,const SkPicture * pictures[kNumPictures],SkTArray<ComposeStep> * composeSteps)402  static void tiled(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
403                    PFContentMtd pfGen,
404                    const SkPicture* pictures[kNumPictures],
405                    SkTArray<ComposeStep> *composeSteps) {
406      const int kNumTilesX = 2;
407      const int kNumTilesY = 2;
408      const int kTileWidth = kPicWidth / kNumTilesX;
409      const int kTileHeight = kPicHeight / kNumTilesY;
410  
411      SkASSERT(kPicWidth == kNumTilesX * kTileWidth);
412      SkASSERT(kPicHeight == kNumTilesY * kTileHeight);
413  
414      const SkColor colors[kNumTilesX][kNumTilesY] = {
415          { SK_ColorCYAN,   SK_ColorMAGENTA },
416          { SK_ColorYELLOW, SK_ColorGREEN   }
417      };
418  
419      for (int y = 0; y < kNumTilesY; ++y) {
420          for (int x = 0; x < kNumTilesX; ++x) {
421              ComposeStep& step = composeSteps->push_back();
422  
423              step.fX = SkIntToScalar(x*kTileWidth);
424              step.fY = SkIntToScalar(y*kTileHeight);
425              step.fPaint = new SkPaint;
426              step.fPaint->setColorFilter(
427                  SkColorFilters::Blend(colors[x][y], SkBlendMode::kModulate));
428  
429              step.fSurf = create_compat_surface(finalCanvas, kTileWidth, kTileHeight);
430  
431              SkCanvas* subCanvas = step.fSurf->getCanvas();
432  
433              const SkMatrix trans = SkMatrix::MakeTrans(-SkIntToScalar(x*kTileWidth),
434                                                         -SkIntToScalar(y*kTileHeight));
435  
436              create_content(mpd, pfGen, pictures, subCanvas, trans);
437          }
438      }
439  }
440  
441  constexpr PFLayoutMtd gLayoutMthds[] = { simple, tiled };
442  
443  namespace skiagm {
444      /**
445       * This GM exercises the SkMultiPictureDraw object. It tests the
446       * cross product of:
447       *      tiled vs. all-at-once rendering (e.g., into many or just 1 canvas)
448       *      different clips (e.g., none, rect, rrect)
449       *      single vs. multiple pictures (e.g., normal vs. picture-pile-style content)
450       */
451      class MultiPictureDraw : public GM {
452      public:
453          enum Content {
454              kNoClipSingle_Content,
455              kRectClipMulti_Content,
456              kRRectClipMulti_Content,
457              kPathClipMulti_Content,
458              kInvPathClipMulti_Content,
459              kSierpinski_Content,
460              kBigLayer_Content,
461  
462              kLast_Content = kBigLayer_Content
463          };
464  
465          const int kContentCnt = kLast_Content + 1;
466  
467          enum Layout {
468              kSimple_Layout,
469              kTiled_Layout,
470  
471              kLast_Layout = kTiled_Layout
472          };
473  
474          const int kLayoutCnt = kLast_Layout + 1;
475  
MultiPictureDraw(Content content,Layout layout)476          MultiPictureDraw(Content content, Layout layout) : fContent(content), fLayout(layout) {
477              SkASSERT(SK_ARRAY_COUNT(gLayoutMthds) == kLayoutCnt);
478              SkASSERT(SK_ARRAY_COUNT(gContentMthds) == kContentCnt);
479  
480              for (int i = 0; i < kNumPictures; ++i) {
481                  fPictures[i] = nullptr;
482              }
483          }
484  
~MultiPictureDraw()485          ~MultiPictureDraw() override {
486              for (int i = 0; i < kNumPictures; ++i) {
487                  SkSafeUnref(fPictures[i]);
488              }
489          }
490  
491      protected:
492          Content          fContent;
493          Layout           fLayout;
494          const SkPicture* fPictures[kNumPictures];
495  
onOnceBeforeDraw()496          void onOnceBeforeDraw() override {
497              fPictures[0] = make_hex_plane_picture(SK_ColorWHITE).release();
498              fPictures[1] = make_hex_plane_picture(SK_ColorGRAY).release();
499              fPictures[2] = make_sierpinski_picture().release();
500              fPictures[3] = make_single_layer_hex_plane_picture().release();
501          }
502  
onDraw(SkCanvas * canvas)503          void onDraw(SkCanvas* canvas) override {
504              SkMultiPictureDraw mpd;
505              SkTArray<ComposeStep> composeSteps;
506  
507              // Fill up the MultiPictureDraw
508              (*gLayoutMthds[fLayout])(canvas, &mpd,
509                                       gContentMthds[fContent],
510                                       fPictures, &composeSteps);
511  
512              mpd.draw();
513  
514              // Compose all the drawn canvases into the final canvas
515              for (int i = 0; i < composeSteps.count(); ++i) {
516                  const ComposeStep& step = composeSteps[i];
517  
518                  canvas->drawImage(step.fSurf->makeImageSnapshot().get(),
519                                    step.fX, step.fY, step.fPaint);
520              }
521          }
522  
onISize()523          SkISize onISize() override { return SkISize::Make(kPicWidth, kPicHeight); }
524  
onShortName()525          SkString onShortName() override {
526              const char* gContentNames[] = {
527                  "noclip", "rectclip", "rrectclip", "pathclip",
528                  "invpathclip", "sierpinski", "biglayer"
529              };
530              const char* gLayoutNames[] = { "simple", "tiled" };
531  
532              SkASSERT(SK_ARRAY_COUNT(gLayoutNames) == kLayoutCnt);
533              SkASSERT(SK_ARRAY_COUNT(gContentNames) == kContentCnt);
534  
535              SkString name("multipicturedraw_");
536  
537              name.append(gContentNames[fContent]);
538              name.append("_");
539              name.append(gLayoutNames[fLayout]);
540              return name;
541          }
542  
runAsBench() const543          bool runAsBench() const override { return true; }
544  
545      private:
546          typedef GM INHERITED;
547      };
548  
549      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kNoClipSingle_Content,
550                                         MultiPictureDraw::kSimple_Layout);)
551      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRectClipMulti_Content,
552                                         MultiPictureDraw::kSimple_Layout);)
553      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRRectClipMulti_Content,
554                                         MultiPictureDraw::kSimple_Layout);)
555      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kPathClipMulti_Content,
556                                         MultiPictureDraw::kSimple_Layout);)
557      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kInvPathClipMulti_Content,
558                                         MultiPictureDraw::kSimple_Layout);)
559      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kSierpinski_Content,
560                                         MultiPictureDraw::kSimple_Layout);)
561      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kBigLayer_Content,
562                                         MultiPictureDraw::kSimple_Layout);)
563  
564      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kNoClipSingle_Content,
565                                         MultiPictureDraw::kTiled_Layout);)
566      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRectClipMulti_Content,
567                                         MultiPictureDraw::kTiled_Layout);)
568      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRRectClipMulti_Content,
569                                         MultiPictureDraw::kTiled_Layout);)
570      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kPathClipMulti_Content,
571                                         MultiPictureDraw::kTiled_Layout);)
572      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kInvPathClipMulti_Content,
573                                         MultiPictureDraw::kTiled_Layout);)
574      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kSierpinski_Content,
575                                         MultiPictureDraw::kTiled_Layout);)
576      DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kBigLayer_Content,
577                                         MultiPictureDraw::kTiled_Layout);)
578  }
579