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