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