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