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