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