d86a8b8163ace9eff83a5fd1df320ba361d719da
[platform/upstream/libSkiaSharp.git] / gm / beziereffects.cpp
1
2 /*
3  * Copyright 2013 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8
9 // This test only works with the GPU backend.
10
11 #include "gm.h"
12
13 #if SK_SUPPORT_GPU
14
15 #include "GrBatchTarget.h"
16 #include "GrBufferAllocPool.h"
17 #include "GrContext.h"
18 #include "GrPathUtils.h"
19 #include "GrTest.h"
20 #include "GrTestBatch.h"
21 #include "SkColorPriv.h"
22 #include "SkDevice.h"
23 #include "SkGeometry.h"
24
25 #include "effects/GrBezierEffect.h"
26
27 static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) {
28     return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]);
29 }
30
31 namespace skiagm {
32
33 class BezierCubicOrConicTestBatch : public GrTestBatch {
34 public:
35     struct Geometry : public GrTestBatch::Geometry {
36         SkRect fBounds;
37     };
38
39     const char* name() const SK_OVERRIDE { return "BezierCubicOrConicTestBatch"; }
40
41     static GrBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo,
42                            const SkScalar klmEqs[9], SkScalar sign) {
43         return SkNEW_ARGS(BezierCubicOrConicTestBatch, (gp, geo, klmEqs, sign));
44     }
45
46 private:
47     BezierCubicOrConicTestBatch(const GrGeometryProcessor* gp, const Geometry& geo,
48                                 const SkScalar klmEqs[9], SkScalar sign)
49         : INHERITED(gp) {
50         for (int i = 0; i < 9; i++) {
51             fKlmEqs[i] = klmEqs[i];
52         }
53
54         fGeometry = geo;
55         fSign = sign;
56     }
57
58     struct Vertex {
59         SkPoint fPosition;
60         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
61     };
62
63     Geometry* geoData(int index) SK_OVERRIDE {
64         SkASSERT(0 == index);
65         return &fGeometry;
66     }
67
68     void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
69         size_t vertexStride = this->geometryProcessor()->getVertexStride();
70
71         const GrVertexBuffer* vertexBuffer;
72         int firstVertex;
73
74         void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
75                                                               kVertsPerCubic,
76                                                               &vertexBuffer,
77                                                               &firstVertex);
78
79         if (!vertices || !batchTarget->quadIndexBuffer()) {
80             SkDebugf("Could not allocate buffers\n");
81             return;
82         }
83
84         SkASSERT(vertexStride == sizeof(Vertex));
85         Vertex* verts = reinterpret_cast<Vertex*>(vertices);
86
87         verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop,
88                                       fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom,
89                                       sizeof(Vertex));
90         for (int v = 0; v < 4; ++v) {
91             verts[v].fKLM[0] = eval_line(verts[v].fPosition, fKlmEqs + 0, fSign);
92             verts[v].fKLM[1] = eval_line(verts[v].fPosition, fKlmEqs + 3, fSign);
93             verts[v].fKLM[2] = eval_line(verts[v].fPosition, fKlmEqs + 6, 1.f);
94         }
95
96         GrDrawTarget::DrawInfo drawInfo;
97         drawInfo.setPrimitiveType(kTriangleFan_GrPrimitiveType);
98         drawInfo.setVertexBuffer(vertexBuffer);
99         drawInfo.setStartVertex(firstVertex);
100         drawInfo.setVertexCount(kVertsPerCubic);
101         drawInfo.setStartIndex(0);
102         drawInfo.setIndexCount(kIndicesPerCubic);
103         drawInfo.setIndexBuffer(batchTarget->quadIndexBuffer());
104         batchTarget->draw(drawInfo);
105     }
106
107     Geometry fGeometry;
108     SkScalar fKlmEqs[9];
109     SkScalar fSign;
110
111     static const int kVertsPerCubic = 4;
112     static const int kIndicesPerCubic = 6;
113
114     typedef GrTestBatch INHERITED;
115 };
116
117 /**
118  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
119  */
120 class BezierCubicEffects : public GM {
121 public:
122     BezierCubicEffects() {
123         this->setBGColor(0xFFFFFFFF);
124     }
125
126 protected:
127     SkString onShortName() SK_OVERRIDE {
128         return SkString("bezier_cubic_effects");
129     }
130
131     SkISize onISize() SK_OVERRIDE {
132         return SkISize::Make(800, 800);
133     }
134
135     void onDraw(SkCanvas* canvas) SK_OVERRIDE {
136         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
137         if (NULL == rt) {
138             this->drawGpuOnlyMessage(canvas);
139             return;
140         }
141         GrContext* context = rt->getContext();
142         if (NULL == context) {
143             return;
144         }
145
146         struct Vertex {
147             SkPoint fPosition;
148             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
149         };
150
151         static const int kNumCubics = 15;
152         SkRandom rand;
153
154         // Mult by 3 for each edge effect type
155         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
156         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
157         SkScalar w = SkIntToScalar(rt->width()) / numCols;
158         SkScalar h = SkIntToScalar(rt->height()) / numRows;
159         int row = 0;
160         int col = 0;
161
162         for (int i = 0; i < kNumCubics; ++i) {
163             SkPoint baseControlPts[] = {
164                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
165                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
166                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
167                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
168             };
169             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
170                 SkAutoTUnref<GrGeometryProcessor> gp;
171                 {   // scope to contain GrTestTarget
172                     GrTestTarget tt;
173                     context->getTestTarget(&tt);
174                     if (NULL == tt.target()) {
175                         continue;
176                     }
177                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
178                     gp.reset(GrCubicEffect::Create(0xff000000, SkMatrix::I(), et,
179                                                    *tt.target()->caps()));
180                     if (!gp) {
181                         continue;
182                     }
183                 }
184
185                 SkScalar x = SkScalarMul(col, w);
186                 SkScalar y = SkScalarMul(row, h);
187                 SkPoint controlPts[] = {
188                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
189                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
190                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
191                     {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
192                 };
193                 SkPoint chopped[10];
194                 SkScalar klmEqs[9];
195                 SkScalar klmSigns[3];
196                 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
197                                                                    chopped,
198                                                                    klmEqs,
199                                                                    klmSigns);
200
201                 SkPaint ctrlPtPaint;
202                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
203                 for (int i = 0; i < 4; ++i) {
204                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
205                 }
206
207                 SkPaint polyPaint;
208                 polyPaint.setColor(0xffA0A0A0);
209                 polyPaint.setStrokeWidth(0);
210                 polyPaint.setStyle(SkPaint::kStroke_Style);
211                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
212
213                 SkPaint choppedPtPaint;
214                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
215
216                 for (int c = 0; c < cnt; ++c) {
217                     SkPoint* pts = chopped + 3 * c;
218
219                     for (int i = 0; i < 4; ++i) {
220                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
221                     }
222
223                     SkRect bounds;
224                     bounds.set(pts, 4);
225
226                     SkPaint boundsPaint;
227                     boundsPaint.setColor(0xff808080);
228                     boundsPaint.setStrokeWidth(0);
229                     boundsPaint.setStyle(SkPaint::kStroke_Style);
230                     canvas->drawRect(bounds, boundsPaint);
231
232                     GrTestTarget tt;
233                     context->getTestTarget(&tt);
234                     SkASSERT(tt.target());
235
236                     GrPipelineBuilder pipelineBuilder;
237                     pipelineBuilder.setRenderTarget(rt);
238
239                     BezierCubicOrConicTestBatch::Geometry geometry;
240                     geometry.fColor = gp->color();
241                     geometry.fBounds = bounds;
242
243                     SkAutoTUnref<GrBatch> batch(
244                             BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, klmSigns[c]));
245
246                     tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
247                 }
248                 ++col;
249                 if (numCols == col) {
250                     col = 0;
251                     ++row;
252                 }
253             }
254         }
255     }
256
257 private:
258     typedef GM INHERITED;
259 };
260
261 //////////////////////////////////////////////////////////////////////////////
262
263 /**
264  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
265  */
266 class BezierConicEffects : public GM {
267 public:
268     BezierConicEffects() {
269         this->setBGColor(0xFFFFFFFF);
270     }
271
272 protected:
273     SkString onShortName() SK_OVERRIDE {
274         return SkString("bezier_conic_effects");
275     }
276
277     SkISize onISize() SK_OVERRIDE {
278         return SkISize::Make(800, 800);
279     }
280
281
282     void onDraw(SkCanvas* canvas) SK_OVERRIDE {
283         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
284         if (NULL == rt) {
285             this->drawGpuOnlyMessage(canvas);
286             return;
287         }
288         GrContext* context = rt->getContext();
289         if (NULL == context) {
290             return;
291         }
292
293         struct Vertex {
294             SkPoint fPosition;
295             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
296         };
297
298         static const int kNumConics = 10;
299         SkRandom rand;
300
301         // Mult by 3 for each edge effect type
302         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
303         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
304         SkScalar w = SkIntToScalar(rt->width()) / numCols;
305         SkScalar h = SkIntToScalar(rt->height()) / numRows;
306         int row = 0;
307         int col = 0;
308
309         for (int i = 0; i < kNumConics; ++i) {
310             SkPoint baseControlPts[] = {
311                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
312                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
313                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
314             };
315             SkScalar weight = rand.nextRangeF(0.f, 2.f);
316             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
317                 SkAutoTUnref<GrGeometryProcessor> gp;
318                 {   // scope to contain GrTestTarget
319                     GrTestTarget tt;
320                     context->getTestTarget(&tt);
321                     if (NULL == tt.target()) {
322                         continue;
323                     }
324                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
325                     gp.reset(GrConicEffect::Create(0xff000000, SkMatrix::I(), et,
326                                                    *tt.target()->caps(), SkMatrix::I()));
327                     if (!gp) {
328                         continue;
329                     }
330                 }
331
332                 SkScalar x = SkScalarMul(col, w);
333                 SkScalar y = SkScalarMul(row, h);
334                 SkPoint controlPts[] = {
335                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
336                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
337                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
338                 };
339                 SkConic dst[4];
340                 SkScalar klmEqs[9];
341                 int cnt = chop_conic(controlPts, dst, weight);
342                 GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
343
344                 SkPaint ctrlPtPaint;
345                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
346                 for (int i = 0; i < 3; ++i) {
347                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
348                 }
349
350                 SkPaint polyPaint;
351                 polyPaint.setColor(0xffA0A0A0);
352                 polyPaint.setStrokeWidth(0);
353                 polyPaint.setStyle(SkPaint::kStroke_Style);
354                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
355
356                 SkPaint choppedPtPaint;
357                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
358
359                 for (int c = 0; c < cnt; ++c) {
360                     SkPoint* pts = dst[c].fPts;
361                     for (int i = 0; i < 3; ++i) {
362                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
363                     }
364
365                     SkRect bounds;
366                     //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
367                     //bounds.set(bPts, 2);
368                     bounds.set(pts, 3);
369
370                     SkPaint boundsPaint;
371                     boundsPaint.setColor(0xff808080);
372                     boundsPaint.setStrokeWidth(0);
373                     boundsPaint.setStyle(SkPaint::kStroke_Style);
374                     canvas->drawRect(bounds, boundsPaint);
375
376                     GrTestTarget tt;
377                     context->getTestTarget(&tt);
378                     SkASSERT(tt.target());
379
380                     GrPipelineBuilder pipelineBuilder;
381                     pipelineBuilder.setRenderTarget(rt);
382
383                     BezierCubicOrConicTestBatch::Geometry geometry;
384                     geometry.fColor = gp->color();
385                     geometry.fBounds = bounds;
386
387                     SkAutoTUnref<GrBatch> batch(
388                             BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, 1.f));
389
390                     tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
391                 }
392                 ++col;
393                 if (numCols == col) {
394                     col = 0;
395                     ++row;
396                 }
397             }
398         }
399     }
400
401 private:
402     // Uses the max curvature function for quads to estimate
403     // where to chop the conic. If the max curvature is not
404     // found along the curve segment it will return 1 and
405     // dst[0] is the original conic. If it returns 2 the dst[0]
406     // and dst[1] are the two new conics.
407     int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
408         SkScalar t = SkFindQuadMaxCurvature(src);
409         if (t == 0) {
410             if (dst) {
411                 dst[0].set(src, weight);
412             }
413             return 1;
414         } else {
415             if (dst) {
416                 SkConic conic;
417                 conic.set(src, weight);
418                 conic.chopAt(t, dst);
419             }
420             return 2;
421         }
422     }
423
424     // Calls split_conic on the entire conic and then once more on each subsection.
425     // Most cases will result in either 1 conic (chop point is not within t range)
426     // or 3 points (split once and then one subsection is split again).
427     int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
428         SkConic dstTemp[2];
429         int conicCnt = split_conic(src, dstTemp, weight);
430         if (2 == conicCnt) {
431             int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
432             conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
433         } else {
434             dst[0] = dstTemp[0];
435         }
436         return conicCnt;
437     }
438
439     typedef GM INHERITED;
440 };
441
442 //////////////////////////////////////////////////////////////////////////////
443
444 class BezierQuadTestBatch : public GrTestBatch {
445 public:
446     struct Geometry : public GrTestBatch::Geometry {
447         SkRect fBounds;
448     };
449
450     const char* name() const SK_OVERRIDE { return "BezierQuadTestBatch"; }
451
452     static GrBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo,
453                            const GrPathUtils::QuadUVMatrix& devToUV) {
454         return SkNEW_ARGS(BezierQuadTestBatch, (gp, geo, devToUV));
455     }
456
457 private:
458     BezierQuadTestBatch(const GrGeometryProcessor* gp, const Geometry& geo,
459                         const GrPathUtils::QuadUVMatrix& devToUV)
460         : INHERITED(gp)
461         , fGeometry(geo)
462         , fDevToUV(devToUV) {
463     }
464
465     struct Vertex {
466         SkPoint fPosition;
467         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
468     };
469
470     Geometry* geoData(int index) SK_OVERRIDE {
471         SkASSERT(0 == index);
472         return &fGeometry;
473     }
474
475     void onGenerateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) SK_OVERRIDE {
476         size_t vertexStride = this->geometryProcessor()->getVertexStride();
477
478         const GrVertexBuffer* vertexBuffer;
479         int firstVertex;
480
481         void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride,
482                                                               kVertsPerCubic,
483                                                               &vertexBuffer,
484                                                               &firstVertex);
485
486         if (!vertices || !batchTarget->quadIndexBuffer()) {
487             SkDebugf("Could not allocate buffers\n");
488             return;
489         }
490
491         SkASSERT(vertexStride == sizeof(Vertex));
492         Vertex* verts = reinterpret_cast<Vertex*>(vertices);
493
494         verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop,
495                                       fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom,
496                                       sizeof(Vertex));
497
498         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
499
500
501         GrDrawTarget::DrawInfo drawInfo;
502         drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType);
503         drawInfo.setVertexBuffer(vertexBuffer);
504         drawInfo.setStartVertex(firstVertex);
505         drawInfo.setVertexCount(kVertsPerCubic);
506         drawInfo.setStartIndex(0);
507         drawInfo.setIndexCount(kIndicesPerCubic);
508         drawInfo.setIndexBuffer(batchTarget->quadIndexBuffer());
509         batchTarget->draw(drawInfo);
510     }
511
512     Geometry fGeometry;
513     GrPathUtils::QuadUVMatrix fDevToUV;
514
515     static const int kVertsPerCubic = 4;
516     static const int kIndicesPerCubic = 6;
517
518     typedef GrTestBatch INHERITED;
519 };
520
521 /**
522  * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
523  */
524 class BezierQuadEffects : public GM {
525 public:
526     BezierQuadEffects() {
527         this->setBGColor(0xFFFFFFFF);
528     }
529
530 protected:
531     SkString onShortName() SK_OVERRIDE {
532         return SkString("bezier_quad_effects");
533     }
534
535     SkISize onISize() SK_OVERRIDE {
536         return SkISize::Make(800, 800);
537     }
538
539
540     void onDraw(SkCanvas* canvas) SK_OVERRIDE {
541         GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
542         if (NULL == rt) {
543             this->drawGpuOnlyMessage(canvas);
544             return;
545         }
546         GrContext* context = rt->getContext();
547         if (NULL == context) {
548             return;
549         }
550
551         struct Vertex {
552             SkPoint fPosition;
553             float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
554         };
555
556         static const int kNumQuads = 5;
557         SkRandom rand;
558
559         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
560         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
561         SkScalar w = SkIntToScalar(rt->width()) / numCols;
562         SkScalar h = SkIntToScalar(rt->height()) / numRows;
563         int row = 0;
564         int col = 0;
565
566         for (int i = 0; i < kNumQuads; ++i) {
567             SkPoint baseControlPts[] = {
568                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
569                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
570                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
571             };
572             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
573                 SkAutoTUnref<GrGeometryProcessor> gp;
574                 {   // scope to contain GrTestTarget
575                     GrTestTarget tt;
576                     context->getTestTarget(&tt);
577                     if (NULL == tt.target()) {
578                         continue;
579                     }
580                     GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
581                     gp.reset(GrQuadEffect::Create(0xff000000, SkMatrix::I(), et,
582                                                   *tt.target()->caps(), SkMatrix::I()));
583                     if (!gp) {
584                         continue;
585                     }
586                 }
587
588                 SkScalar x = SkScalarMul(col, w);
589                 SkScalar y = SkScalarMul(row, h);
590                 SkPoint controlPts[] = {
591                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
592                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
593                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
594                 };
595                 SkPoint chopped[5];
596                 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
597
598                 SkPaint ctrlPtPaint;
599                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
600                 for (int i = 0; i < 3; ++i) {
601                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
602                 }
603
604                 SkPaint polyPaint;
605                 polyPaint.setColor(0xffA0A0A0);
606                 polyPaint.setStrokeWidth(0);
607                 polyPaint.setStyle(SkPaint::kStroke_Style);
608                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
609
610                 SkPaint choppedPtPaint;
611                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
612
613                 for (int c = 0; c < cnt; ++c) {
614                     SkPoint* pts = chopped + 2 * c;
615
616                     for (int i = 0; i < 3; ++i) {
617                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
618                     }
619
620                     SkRect bounds;
621                     bounds.set(pts, 3);
622
623                     SkPaint boundsPaint;
624                     boundsPaint.setColor(0xff808080);
625                     boundsPaint.setStrokeWidth(0);
626                     boundsPaint.setStyle(SkPaint::kStroke_Style);
627                     canvas->drawRect(bounds, boundsPaint);
628
629                     GrTestTarget tt;
630                     context->getTestTarget(&tt);
631                     SkASSERT(tt.target());
632
633                     GrPipelineBuilder pipelineBuilder;
634                     pipelineBuilder.setRenderTarget(rt);
635
636                     GrPathUtils::QuadUVMatrix DevToUV(pts);
637
638                     BezierQuadTestBatch::Geometry geometry;
639                     geometry.fColor = gp->color();
640                     geometry.fBounds = bounds;
641
642                     SkAutoTUnref<GrBatch> batch(BezierQuadTestBatch::Create(gp, geometry, DevToUV));
643
644                     tt.target()->drawBatch(&pipelineBuilder, batch, NULL);
645                 }
646                 ++col;
647                 if (numCols == col) {
648                     col = 0;
649                     ++row;
650                 }
651             }
652         }
653     }
654
655 private:
656     typedef GM INHERITED;
657 };
658
659 DEF_GM( return SkNEW(BezierCubicEffects); )
660 DEF_GM( return SkNEW(BezierConicEffects); )
661 DEF_GM( return SkNEW(BezierQuadEffects); )
662
663 }
664
665 #endif