Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2013 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 // This test only works with the GPU backend.
      9 
     10 #include "gm.h"
     11 
     12 #if SK_SUPPORT_GPU
     13 
     14 #include "GrContext.h"
     15 #include "GrOpFlushState.h"
     16 #include "GrPathUtils.h"
     17 #include "GrRenderTargetContextPriv.h"
     18 #include "GrTest.h"
     19 #include "SkColorPriv.h"
     20 #include "SkGeometry.h"
     21 #include "effects/GrBezierEffect.h"
     22 #include "ops/GrMeshDrawOp.h"
     23 
     24 namespace skiagm {
     25 
     26 class BezierTestOp : public GrMeshDrawOp {
     27 public:
     28     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
     29 
     30     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
     31         auto analysis = fProcessorSet.finalize(fColor, GrProcessorAnalysisCoverage::kSingleChannel,
     32                                                clip, false, caps, &fColor);
     33         return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
     34     }
     35 
     36 protected:
     37     BezierTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color, int32_t classID)
     38             : INHERITED(classID)
     39             , fRect(rect)
     40             , fColor(color)
     41             , fGeometryProcessor(std::move(gp))
     42             , fProcessorSet(SkBlendMode::kSrc) {
     43         this->setBounds(rect, HasAABloat::kYes, IsZeroArea::kNo);
     44     }
     45 
     46     const GrPipeline* makePipeline(Target* target) const {
     47         return target->makePipeline(0, &fProcessorSet);
     48     }
     49 
     50     const GrGeometryProcessor* gp() const { return fGeometryProcessor.get(); }
     51 
     52     const SkRect& rect() const { return fRect; }
     53     GrColor color() const { return fColor; }
     54 
     55 private:
     56     bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; }
     57 
     58     SkRect fRect;
     59     GrColor fColor;
     60     sk_sp<GrGeometryProcessor> fGeometryProcessor;
     61     GrProcessorSet fProcessorSet;
     62 
     63     typedef GrMeshDrawOp INHERITED;
     64 };
     65 
     66 class BezierCubicTestOp : public BezierTestOp {
     67 public:
     68     DEFINE_OP_CLASS_ID
     69 
     70     const char* name() const override { return "BezierCubicTestOp"; }
     71 
     72     static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
     73                                           GrColor color) {
     74         return std::unique_ptr<GrDrawOp>(new BezierCubicTestOp(std::move(gp), rect, color));
     75     }
     76 
     77 private:
     78     BezierCubicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color)
     79             : INHERITED(std::move(gp), rect, color, ClassID()) {}
     80 
     81     void onPrepareDraws(Target* target) const override {
     82         QuadHelper helper;
     83         size_t vertexStride = this->gp()->getVertexStride();
     84         SkASSERT(vertexStride == sizeof(SkPoint));
     85         SkPoint* pts = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
     86         if (!pts) {
     87             return;
     88         }
     89         SkRect rect = this->rect();
     90         pts[0].setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
     91         helper.recordDraw(target, this->gp(), this->makePipeline(target));
     92     }
     93 
     94     static constexpr int kVertsPerCubic = 4;
     95     static constexpr int kIndicesPerCubic = 6;
     96 
     97     typedef BezierTestOp INHERITED;
     98 };
     99 
    100 /**
    101  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
    102  */
    103 class BezierCubicEffects : public GM {
    104 public:
    105     BezierCubicEffects() {
    106         this->setBGColor(0xFFFFFFFF);
    107     }
    108 
    109 protected:
    110     SkString onShortName() override {
    111         return SkString("bezier_cubic_effects");
    112     }
    113 
    114     SkISize onISize() override {
    115         return SkISize::Make(800, 800);
    116     }
    117 
    118     void onDraw(SkCanvas* canvas) override {
    119         GrRenderTargetContext* renderTargetContext =
    120             canvas->internal_private_accessTopLayerRenderTargetContext();
    121         if (!renderTargetContext) {
    122             skiagm::GM::DrawGpuOnlyMessage(canvas);
    123             return;
    124         }
    125 
    126         GrContext* context = canvas->getGrContext();
    127         if (!context) {
    128             return;
    129         }
    130 
    131         struct Vertex {
    132             SkPoint fPosition;
    133             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
    134         };
    135 
    136         constexpr int kNumCubics = 15;
    137         SkRandom rand;
    138 
    139         // Mult by 3 for each edge effect type
    140         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
    141         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
    142         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
    143         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
    144         int row = 0;
    145         int col = 0;
    146         constexpr GrColor color = 0xff000000;
    147 
    148         for (int i = 0; i < kNumCubics; ++i) {
    149             SkPoint baseControlPts[] = {
    150                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    151                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    152                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    153                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
    154             };
    155             for(GrPrimitiveEdgeType edgeType : {kFillBW_GrProcessorEdgeType,
    156                                                 kFillAA_GrProcessorEdgeType,
    157                                                 kHairlineAA_GrProcessorEdgeType}) {
    158                 SkScalar x = col * w;
    159                 SkScalar y = row * h;
    160                 SkPoint controlPts[] = {
    161                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
    162                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    163                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
    164                     {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
    165                 };
    166                 SkPoint chopped[10];
    167                 SkMatrix klm;
    168                 int loopIndex;
    169                 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
    170                                                                    chopped,
    171                                                                    &klm,
    172                                                                    &loopIndex);
    173 
    174                 SkPaint ctrlPtPaint;
    175                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    176                 canvas->drawCircle(controlPts[0], 8.f, ctrlPtPaint);
    177                 for (int i = 1; i < 4; ++i) {
    178                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
    179                 }
    180 
    181                 SkPaint polyPaint;
    182                 polyPaint.setColor(0xffA0A0A0);
    183                 polyPaint.setStrokeWidth(0);
    184                 polyPaint.setStyle(SkPaint::kStroke_Style);
    185                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
    186 
    187                 SkPaint choppedPtPaint;
    188                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    189 
    190                 for (int c = 0; c < cnt; ++c) {
    191                     SkPoint* pts = chopped + 3 * c;
    192 
    193                     for (int i = 0; i < 4; ++i) {
    194                         canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
    195                     }
    196 
    197                     SkRect bounds;
    198                     bounds.set(pts, 4);
    199 
    200                     SkPaint boundsPaint;
    201                     boundsPaint.setColor(0xff808080);
    202                     boundsPaint.setStrokeWidth(0);
    203                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    204                     canvas->drawRect(bounds, boundsPaint);
    205 
    206 
    207                     bool flipKL = (c == loopIndex && cnt != 3);
    208                     sk_sp<GrGeometryProcessor> gp = GrCubicEffect::Make(color, SkMatrix::I(), klm,
    209                                                                         flipKL, edgeType,
    210                                                                         *context->caps());
    211                     if (!gp) {
    212                         break;
    213                     }
    214 
    215                     std::unique_ptr<GrDrawOp> op =
    216                             BezierCubicTestOp::Make(std::move(gp), bounds, color);
    217                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
    218                 }
    219                 ++col;
    220                 if (numCols == col) {
    221                     col = 0;
    222                     ++row;
    223                 }
    224             }
    225         }
    226     }
    227 
    228 private:
    229     typedef GM INHERITED;
    230 };
    231 
    232 //////////////////////////////////////////////////////////////////////////////
    233 
    234 class BezierConicTestOp : public BezierTestOp {
    235 public:
    236     DEFINE_OP_CLASS_ID
    237 
    238     const char* name() const override { return "BezierConicTestOp"; }
    239 
    240     static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
    241                                           GrColor color, const SkMatrix& klm) {
    242         return std::unique_ptr<GrMeshDrawOp>(
    243                 new BezierConicTestOp(std::move(gp), rect, color, klm));
    244     }
    245 
    246 private:
    247     BezierConicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
    248                       const SkMatrix& klm)
    249             : INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {}
    250 
    251     struct Vertex {
    252         SkPoint fPosition;
    253         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
    254     };
    255 
    256     void onPrepareDraws(Target* target) const override {
    257         QuadHelper helper;
    258         size_t vertexStride = this->gp()->getVertexStride();
    259         SkASSERT(vertexStride == sizeof(Vertex));
    260         Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
    261         if (!verts) {
    262             return;
    263         }
    264         SkRect rect = this->rect();
    265         verts[0].fPosition.setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    266                                       sizeof(Vertex));
    267         for (int v = 0; v < 4; ++v) {
    268             SkScalar pt3[3] = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
    269             fKLM.mapHomogeneousPoints(verts[v].fKLM, pt3, 1);
    270         }
    271         helper.recordDraw(target, this->gp(), this->makePipeline(target));
    272     }
    273 
    274     SkMatrix fKLM;
    275 
    276     static constexpr int kVertsPerCubic = 4;
    277     static constexpr int kIndicesPerCubic = 6;
    278 
    279     typedef BezierTestOp INHERITED;
    280 };
    281 
    282 
    283 /**
    284  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
    285  */
    286 class BezierConicEffects : public GM {
    287 public:
    288     BezierConicEffects() {
    289         this->setBGColor(0xFFFFFFFF);
    290     }
    291 
    292 protected:
    293     SkString onShortName() override {
    294         return SkString("bezier_conic_effects");
    295     }
    296 
    297     SkISize onISize() override {
    298         return SkISize::Make(800, 800);
    299     }
    300 
    301 
    302     void onDraw(SkCanvas* canvas) override {
    303         GrRenderTargetContext* renderTargetContext =
    304             canvas->internal_private_accessTopLayerRenderTargetContext();
    305         if (!renderTargetContext) {
    306             skiagm::GM::DrawGpuOnlyMessage(canvas);
    307             return;
    308         }
    309 
    310         GrContext* context = canvas->getGrContext();
    311         if (!context) {
    312             return;
    313         }
    314 
    315         struct Vertex {
    316             SkPoint fPosition;
    317             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
    318         };
    319 
    320         constexpr int kNumConics = 10;
    321         SkRandom rand;
    322 
    323         // Mult by 3 for each edge effect type
    324         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
    325         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
    326         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
    327         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
    328         int row = 0;
    329         int col = 0;
    330         constexpr GrColor color = 0xff000000;
    331 
    332         for (int i = 0; i < kNumConics; ++i) {
    333             SkPoint baseControlPts[] = {
    334                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    335                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    336                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
    337             };
    338             SkScalar weight = rand.nextRangeF(0.f, 2.f);
    339             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
    340                 sk_sp<GrGeometryProcessor> gp;
    341                 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
    342                 gp = GrConicEffect::Make(color, SkMatrix::I(), et,
    343                                          *context->caps(), SkMatrix::I(), false);
    344                 if (!gp) {
    345                     continue;
    346                 }
    347 
    348                 SkScalar x = col * w;
    349                 SkScalar y = row * h;
    350                 SkPoint controlPts[] = {
    351                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
    352                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    353                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
    354                 };
    355                 SkConic dst[4];
    356                 SkMatrix klm;
    357                 int cnt = chop_conic(controlPts, dst, weight);
    358                 GrPathUtils::getConicKLM(controlPts, weight, &klm);
    359 
    360                 SkPaint ctrlPtPaint;
    361                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    362                 for (int i = 0; i < 3; ++i) {
    363                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
    364                 }
    365 
    366                 SkPaint polyPaint;
    367                 polyPaint.setColor(0xffA0A0A0);
    368                 polyPaint.setStrokeWidth(0);
    369                 polyPaint.setStyle(SkPaint::kStroke_Style);
    370                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
    371 
    372                 SkPaint choppedPtPaint;
    373                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    374 
    375                 for (int c = 0; c < cnt; ++c) {
    376                     SkPoint* pts = dst[c].fPts;
    377                     for (int i = 0; i < 3; ++i) {
    378                         canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
    379                     }
    380 
    381                     SkRect bounds;
    382                     //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
    383                     //bounds.set(bPts, 2);
    384                     bounds.set(pts, 3);
    385 
    386                     SkPaint boundsPaint;
    387                     boundsPaint.setColor(0xff808080);
    388                     boundsPaint.setStrokeWidth(0);
    389                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    390                     canvas->drawRect(bounds, boundsPaint);
    391 
    392                     std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(gp, bounds, color, klm);
    393                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
    394                 }
    395                 ++col;
    396                 if (numCols == col) {
    397                     col = 0;
    398                     ++row;
    399                 }
    400             }
    401         }
    402     }
    403 
    404 private:
    405     // Uses the max curvature function for quads to estimate
    406     // where to chop the conic. If the max curvature is not
    407     // found along the curve segment it will return 1 and
    408     // dst[0] is the original conic. If it returns 2 the dst[0]
    409     // and dst[1] are the two new conics.
    410     int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
    411         SkScalar t = SkFindQuadMaxCurvature(src);
    412         if (t == 0) {
    413             if (dst) {
    414                 dst[0].set(src, weight);
    415             }
    416             return 1;
    417         } else {
    418             if (dst) {
    419                 SkConic conic;
    420                 conic.set(src, weight);
    421                 if (!conic.chopAt(t, dst)) {
    422                     dst[0].set(src, weight);
    423                     return 1;
    424                 }
    425             }
    426             return 2;
    427         }
    428     }
    429 
    430     // Calls split_conic on the entire conic and then once more on each subsection.
    431     // Most cases will result in either 1 conic (chop point is not within t range)
    432     // or 3 points (split once and then one subsection is split again).
    433     int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
    434         SkConic dstTemp[2];
    435         int conicCnt = split_conic(src, dstTemp, weight);
    436         if (2 == conicCnt) {
    437             int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
    438             conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
    439         } else {
    440             dst[0] = dstTemp[0];
    441         }
    442         return conicCnt;
    443     }
    444 
    445     typedef GM INHERITED;
    446 };
    447 
    448 //////////////////////////////////////////////////////////////////////////////
    449 
    450 class BezierQuadTestOp : public BezierTestOp {
    451 public:
    452     DEFINE_OP_CLASS_ID
    453     const char* name() const override { return "BezierQuadTestOp"; }
    454 
    455     static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
    456                                           GrColor color, const GrPathUtils::QuadUVMatrix& devToUV) {
    457         return std::unique_ptr<GrDrawOp>(new BezierQuadTestOp(std::move(gp), rect, color, devToUV));
    458     }
    459 
    460 private:
    461     BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
    462                      const GrPathUtils::QuadUVMatrix& devToUV)
    463             : INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {}
    464 
    465     struct Vertex {
    466         SkPoint fPosition;
    467         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
    468     };
    469 
    470     void onPrepareDraws(Target* target) const override {
    471         QuadHelper helper;
    472         size_t vertexStride = this->gp()->getVertexStride();
    473         SkASSERT(vertexStride == sizeof(Vertex));
    474         Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
    475         if (!verts) {
    476             return;
    477         }
    478         SkRect rect = this->rect();
    479         verts[0].fPosition.setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    480                                       sizeof(Vertex));
    481         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
    482         helper.recordDraw(target, this->gp(), this->makePipeline(target));
    483     }
    484 
    485     GrPathUtils::QuadUVMatrix fDevToUV;
    486 
    487     static constexpr int kVertsPerCubic = 4;
    488     static constexpr int kIndicesPerCubic = 6;
    489 
    490     typedef BezierTestOp INHERITED;
    491 };
    492 
    493 /**
    494  * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
    495  */
    496 class BezierQuadEffects : public GM {
    497 public:
    498     BezierQuadEffects() {
    499         this->setBGColor(0xFFFFFFFF);
    500     }
    501 
    502 protected:
    503     SkString onShortName() override {
    504         return SkString("bezier_quad_effects");
    505     }
    506 
    507     SkISize onISize() override {
    508         return SkISize::Make(800, 800);
    509     }
    510 
    511 
    512     void onDraw(SkCanvas* canvas) override {
    513         GrRenderTargetContext* renderTargetContext =
    514             canvas->internal_private_accessTopLayerRenderTargetContext();
    515         if (!renderTargetContext) {
    516             skiagm::GM::DrawGpuOnlyMessage(canvas);
    517             return;
    518         }
    519 
    520         GrContext* context = canvas->getGrContext();
    521         if (!context) {
    522             return;
    523         }
    524 
    525         struct Vertex {
    526             SkPoint fPosition;
    527             float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
    528         };
    529 
    530         constexpr int kNumQuads = 5;
    531         SkRandom rand;
    532 
    533         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
    534         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
    535         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
    536         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
    537         int row = 0;
    538         int col = 0;
    539         constexpr GrColor color = 0xff000000;
    540 
    541         for (int i = 0; i < kNumQuads; ++i) {
    542             SkPoint baseControlPts[] = {
    543                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    544                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    545                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
    546             };
    547             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
    548                 sk_sp<GrGeometryProcessor> gp;
    549                 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
    550                 gp = GrQuadEffect::Make(color, SkMatrix::I(), et,
    551                                         *context->caps(), SkMatrix::I(), false);
    552                 if (!gp) {
    553                     continue;
    554                 }
    555 
    556                 SkScalar x = col * w;
    557                 SkScalar y = row * h;
    558                 SkPoint controlPts[] = {
    559                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
    560                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    561                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
    562                 };
    563                 SkPoint chopped[5];
    564                 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
    565 
    566                 SkPaint ctrlPtPaint;
    567                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    568                 for (int i = 0; i < 3; ++i) {
    569                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
    570                 }
    571 
    572                 SkPaint polyPaint;
    573                 polyPaint.setColor(0xffA0A0A0);
    574                 polyPaint.setStrokeWidth(0);
    575                 polyPaint.setStyle(SkPaint::kStroke_Style);
    576                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
    577 
    578                 SkPaint choppedPtPaint;
    579                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    580 
    581                 for (int c = 0; c < cnt; ++c) {
    582                     SkPoint* pts = chopped + 2 * c;
    583 
    584                     for (int i = 0; i < 3; ++i) {
    585                         canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
    586                     }
    587 
    588                     SkRect bounds;
    589                     bounds.set(pts, 3);
    590 
    591                     SkPaint boundsPaint;
    592                     boundsPaint.setColor(0xff808080);
    593                     boundsPaint.setStrokeWidth(0);
    594                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    595                     canvas->drawRect(bounds, boundsPaint);
    596 
    597                     GrPaint grPaint;
    598                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
    599 
    600                     GrPathUtils::QuadUVMatrix DevToUV(pts);
    601 
    602                     std::unique_ptr<GrDrawOp> op =
    603                             BezierQuadTestOp::Make(gp, bounds, color, DevToUV);
    604                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
    605                 }
    606                 ++col;
    607                 if (numCols == col) {
    608                     col = 0;
    609                     ++row;
    610                 }
    611             }
    612         }
    613     }
    614 
    615 private:
    616     typedef GM INHERITED;
    617 };
    618 
    619 DEF_GM(return new BezierCubicEffects;)
    620 DEF_GM(return new BezierConicEffects;)
    621 DEF_GM(return new BezierQuadEffects;)
    622 }
    623 
    624 #endif
    625