1/*
2 * Copyright 2014 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 "GrDefaultGeoProcFactory.h"
16#include "GrRenderTargetContextPriv.h"
17#include "GrPathUtils.h"
18#include "GrTest.h"
19#include "SkColorPriv.h"
20#include "SkGeometry.h"
21#include "SkTLList.h"
22
23#include "ops/GrMeshDrawOp.h"
24#include "ops/GrTestMeshDrawOp.h"
25
26#include "effects/GrConvexPolyEffect.h"
27
28/** outset rendered rect to visualize anti-aliased poly edges */
29static SkRect outset(const SkRect& unsorted) {
30    SkRect r = unsorted;
31    r.outset(5.f, 5.f);
32    return r;
33}
34
35/** sorts a rect */
36static SkRect sorted_rect(const SkRect& unsorted) {
37    SkRect r = unsorted;
38    r.sort();
39    return r;
40}
41
42namespace skiagm {
43class PolyBoundsOp : public GrTestMeshDrawOp {
44public:
45    DEFINE_OP_CLASS_ID
46
47    const char* name() const override { return "PolyBoundsOp"; }
48
49    static std::unique_ptr<GrMeshDrawOp> Make(const SkRect& rect, GrColor color) {
50        return std::unique_ptr<GrMeshDrawOp>(new PolyBoundsOp(rect, color));
51    }
52
53private:
54    PolyBoundsOp(const SkRect& rect, GrColor color)
55            : INHERITED(ClassID(), outset(sorted_rect(rect)), color), fRect(outset(rect)) {}
56
57    void onPrepareDraws(Target* target) const override {
58        using namespace GrDefaultGeoProcFactory;
59
60        Color color(this->color());
61        sk_sp<GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Make(
62                color, Coverage::kSolid_Type, LocalCoords::kUnused_Type, SkMatrix::I()));
63
64        size_t vertexStride = gp->getVertexStride();
65        SkASSERT(vertexStride == sizeof(SkPoint));
66        QuadHelper helper;
67        SkPoint* verts = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
68        if (!verts) {
69            return;
70        }
71
72        fRect.toQuad(verts);
73
74        helper.recordDraw(target, gp.get());
75    }
76
77    SkRect fRect;
78
79    typedef GrTestMeshDrawOp INHERITED;
80};
81
82/**
83 * This GM directly exercises a GrProcessor that draws convex polygons.
84 */
85class ConvexPolyEffect : public GM {
86public:
87    ConvexPolyEffect() {
88        this->setBGColor(0xFFFFFFFF);
89    }
90
91protected:
92    SkString onShortName() override {
93        return SkString("convex_poly_effect");
94    }
95
96    SkISize onISize() override {
97        return SkISize::Make(720, 800);
98    }
99
100    void onOnceBeforeDraw() override {
101        SkPath tri;
102        tri.moveTo(5.f, 5.f);
103        tri.lineTo(100.f, 20.f);
104        tri.lineTo(15.f, 100.f);
105
106        fPaths.addToTail(tri);
107        fPaths.addToTail(SkPath())->reverseAddPath(tri);
108
109        tri.close();
110        fPaths.addToTail(tri);
111
112        SkPath ngon;
113        constexpr SkScalar kRadius = 50.f;
114        const SkPoint center = { kRadius, kRadius };
115        for (int i = 0; i < GrConvexPolyEffect::kMaxEdges; ++i) {
116            SkScalar angle = 2 * SK_ScalarPI * i / GrConvexPolyEffect::kMaxEdges;
117            SkPoint point;
118            point.fY = SkScalarSinCos(angle, &point.fX);
119            point.scale(kRadius);
120            point = center + point;
121            if (0 == i) {
122                ngon.moveTo(point);
123            } else {
124                ngon.lineTo(point);
125            }
126        }
127
128        fPaths.addToTail(ngon);
129        SkMatrix scaleM;
130        scaleM.setScale(1.1f, 0.4f);
131        ngon.transform(scaleM);
132        fPaths.addToTail(ngon);
133
134        SkPath linePath;
135        linePath.moveTo(5.f, 5.f);
136        linePath.lineTo(6.f, 6.f);
137        fPaths.addToTail(linePath);
138
139        // integer edges
140        fRects.addToTail(SkRect::MakeLTRB(5.f, 1.f, 30.f, 25.f));
141        // half-integer edges
142        fRects.addToTail(SkRect::MakeLTRB(5.5f, 0.5f, 29.5f, 24.5f));
143        // vertically/horizontally thin rects that cover pixel centers
144        fRects.addToTail(SkRect::MakeLTRB(5.25f, 0.5f, 5.75f, 24.5f));
145        fRects.addToTail(SkRect::MakeLTRB(5.5f,  0.5f, 29.5f, 0.75f));
146        // vertically/horizontally thin rects that don't cover pixel centers
147        fRects.addToTail(SkRect::MakeLTRB(5.55f, 0.5f, 5.75f, 24.5f));
148        fRects.addToTail(SkRect::MakeLTRB(5.5f, .05f, 29.5f, .25f));
149        // small in x and y
150        fRects.addToTail(SkRect::MakeLTRB(5.05f, .55f, 5.45f, .85f));
151        // inverted in x and y
152        fRects.addToTail(SkRect::MakeLTRB(100.f, 50.5f, 5.f, 0.5f));
153    }
154
155    void onDraw(SkCanvas* canvas) override {
156        GrRenderTargetContext* renderTargetContext =
157            canvas->internal_private_accessTopLayerRenderTargetContext();
158        if (!renderTargetContext) {
159            skiagm::GM::DrawGpuOnlyMessage(canvas);
160            return;
161        }
162
163        SkScalar y = 0;
164        constexpr SkScalar kDX = 12.f;
165        for (PathList::Iter iter(fPaths, PathList::Iter::kHead_IterStart);
166             iter.get();
167             iter.next()) {
168            const SkPath* path = iter.get();
169            SkScalar x = 0;
170
171            for (int et = 0; et < kGrProcessorEdgeTypeCnt; ++et) {
172                const SkMatrix m = SkMatrix::MakeTrans(x, y);
173                SkPath p;
174                path->transform(m, &p);
175
176                GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et;
177                sk_sp<GrFragmentProcessor> fp(GrConvexPolyEffect::Make(edgeType, p));
178                if (!fp) {
179                    continue;
180                }
181
182                GrPaint grPaint;
183                grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
184                grPaint.addCoverageFragmentProcessor(std::move(fp));
185
186                std::unique_ptr<GrMeshDrawOp> op = PolyBoundsOp::Make(p.getBounds(), 0xff000000);
187
188                renderTargetContext->priv().testingOnly_addMeshDrawOp(
189                        std::move(grPaint), GrAAType::kNone, std::move(op));
190
191                x += SkScalarCeilToScalar(path->getBounds().width() + kDX);
192            }
193
194            // Draw AA and non AA paths using normal API for reference.
195            canvas->save();
196            canvas->translate(x, y);
197            SkPaint paint;
198            canvas->drawPath(*path, paint);
199            canvas->translate(path->getBounds().width() + 10.f, 0);
200            paint.setAntiAlias(true);
201            canvas->drawPath(*path, paint);
202            canvas->restore();
203
204            y += SkScalarCeilToScalar(path->getBounds().height() + 20.f);
205        }
206
207        for (RectList::Iter iter(fRects, RectList::Iter::kHead_IterStart);
208             iter.get();
209             iter.next()) {
210
211            SkScalar x = 0;
212
213            for (int et = 0; et < kGrProcessorEdgeTypeCnt; ++et) {
214                SkRect rect = *iter.get();
215                rect.offset(x, y);
216                GrPrimitiveEdgeType edgeType = (GrPrimitiveEdgeType) et;
217                sk_sp<GrFragmentProcessor> fp(GrConvexPolyEffect::Make(edgeType, rect));
218                if (!fp) {
219                    continue;
220                }
221
222                GrPaint grPaint;
223                grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
224                grPaint.addCoverageFragmentProcessor(std::move(fp));
225
226                std::unique_ptr<GrMeshDrawOp> op = PolyBoundsOp::Make(rect, 0xff000000);
227
228                renderTargetContext->priv().testingOnly_addMeshDrawOp(
229                        std::move(grPaint), GrAAType::kNone, std::move(op));
230
231                x += SkScalarCeilToScalar(rect.width() + kDX);
232            }
233
234            // Draw rect without and with AA using normal API for reference
235            canvas->save();
236            canvas->translate(x, y);
237            SkPaint paint;
238            canvas->drawRect(*iter.get(), paint);
239            x += SkScalarCeilToScalar(iter.get()->width() + kDX);
240            paint.setAntiAlias(true);
241            canvas->drawRect(*iter.get(), paint);
242            canvas->restore();
243
244            y += SkScalarCeilToScalar(iter.get()->height() + 20.f);
245        }
246    }
247
248private:
249    typedef SkTLList<SkPath, 1> PathList;
250    typedef SkTLList<SkRect, 1> RectList;
251    PathList fPaths;
252    RectList fRects;
253
254    typedef GM INHERITED;
255};
256
257DEF_GM(return new ConvexPolyEffect;)
258}
259
260#endif
261