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