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