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