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
26static 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
30namespace skiagm {
31
32class BezierCubicOrConicTestBatch : public GrTestBatch {
33public:
34    struct Geometry : public GrTestBatch::Geometry {
35        SkRect fBounds;
36    };
37
38    const char* name() const override { return "BezierCubicOrConicTestBatch"; }
39
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
45private:
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
62    Geometry* geoData(int index) override {
63        SkASSERT(0 == index);
64        return &fGeometry;
65    }
66
67    const Geometry* geoData(int index) const override {
68        SkASSERT(0 == index);
69        return &fGeometry;
70    }
71
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 */
105class BezierCubicEffects : public GM {
106public:
107    BezierCubicEffects() {
108        this->setBGColor(0xFFFFFFFF);
109    }
110
111protected:
112    SkString onShortName() override {
113        return SkString("bezier_cubic_effects");
114    }
115
116    SkISize onISize() override {
117        return SkISize::Make(800, 800);
118    }
119
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
243private:
244    typedef GM INHERITED;
245};
246
247//////////////////////////////////////////////////////////////////////////////
248
249/**
250 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
251 */
252class BezierConicEffects : public GM {
253public:
254    BezierConicEffects() {
255        this->setBGColor(0xFFFFFFFF);
256    }
257
258protected:
259    SkString onShortName() override {
260        return SkString("bezier_conic_effects");
261    }
262
263    SkISize onISize() override {
264        return SkISize::Make(800, 800);
265    }
266
267
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
388private:
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.
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).
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
431class BezierQuadTestBatch : public GrTestBatch {
432public:
433    struct Geometry : public GrTestBatch::Geometry {
434        SkRect fBounds;
435    };
436
437    const char* name() const override { return "BezierQuadTestBatch"; }
438
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
444private:
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
457    Geometry* geoData(int index) override {
458        SkASSERT(0 == index);
459        return &fGeometry;
460    }
461
462    const Geometry* geoData(int index) const override {
463        SkASSERT(0 == index);
464        return &fGeometry;
465    }
466
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 */
494class BezierQuadEffects : public GM {
495public:
496    BezierQuadEffects() {
497        this->setBGColor(0xFFFFFFFF);
498    }
499
500protected:
501    SkString onShortName() override {
502        return SkString("bezier_quad_effects");
503    }
504
505    SkISize onISize() override {
506        return SkISize::Make(800, 800);
507    }
508
509
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
626private:
627    typedef GM INHERITED;
628};
629
630DEF_GM( return SkNEW(BezierCubicEffects); )
631DEF_GM( return SkNEW(BezierConicEffects); )
632DEF_GM( return SkNEW(BezierQuadEffects); )
633
634}
635
636#endif
637