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