• 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