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