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