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