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