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