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 */
outset(const SkRect & unsorted)29 static 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 */
sorted_rect(const SkRect & unsorted)36 static SkRect sorted_rect(const SkRect& unsorted) {
37     SkRect r = unsorted;
38     r.sort();
39     return r;
40 }
41 
42 namespace skiagm {
43 class PolyBoundsOp : public GrTestMeshDrawOp {
44 public:
45     DEFINE_OP_CLASS_ID
46 
name() const47     const char* name() const override { return "PolyBoundsOp"; }
48 
Make(const SkRect & rect,GrColor color)49     static std::unique_ptr<GrMeshDrawOp> Make(const SkRect& rect, GrColor color) {
50         return std::unique_ptr<GrMeshDrawOp>(new PolyBoundsOp(rect, color));
51     }
52 
53 private:
PolyBoundsOp(const SkRect & rect,GrColor color)54     PolyBoundsOp(const SkRect& rect, GrColor color)
55             : INHERITED(ClassID(), outset(sorted_rect(rect)), color), fRect(outset(rect)) {}
56 
onPrepareDraws(Target * target) const57     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  */
85 class ConvexPolyEffect : public GM {
86 public:
ConvexPolyEffect()87     ConvexPolyEffect() {
88         this->setBGColor(0xFFFFFFFF);
89     }
90 
91 protected:
onShortName()92     SkString onShortName() override {
93         return SkString("convex_poly_effect");
94     }
95 
onISize()96     SkISize onISize() override {
97         return SkISize::Make(720, 800);
98     }
99 
onOnceBeforeDraw()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 
onDraw(SkCanvas * canvas)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 
248 private:
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 
257 DEF_GM(return new ConvexPolyEffect;)
258 }
259 
260 #endif
261