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/SkScalar.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkString.h"
21 #include "include/private/GrSharedEnums.h"
22 #include "include/private/GrTypesPriv.h"
23 #include "src/core/SkTLList.h"
24 #include "src/gpu/GrFragmentProcessor.h"
25 #include "src/gpu/GrPaint.h"
26 #include "src/gpu/GrRenderTargetContext.h"
27 #include "src/gpu/GrRenderTargetContextPriv.h"
28 #include "src/gpu/effects/GrConvexPolyEffect.h"
29 #include "tools/gpu/TestOps.h"
30 
31 #include <memory>
32 #include <utility>
33 
34 class GrAppliedClip;
35 
36 namespace skiagm {
37 
38 /**
39  * This GM directly exercises a GrProcessor that draws convex polygons.
40  */
41 class ConvexPolyEffect : public GpuGM {
42 public:
ConvexPolyEffect()43     ConvexPolyEffect() {
44         this->setBGColor(0xFFFFFFFF);
45     }
46 
47 protected:
onShortName()48     SkString onShortName() override {
49         return SkString("convex_poly_effect");
50     }
51 
onISize()52     SkISize onISize() override {
53         return SkISize::Make(720, 800);
54     }
55 
onOnceBeforeDraw()56     void onOnceBeforeDraw() override {
57         SkPath tri;
58         tri.moveTo(5.f, 5.f);
59         tri.lineTo(100.f, 20.f);
60         tri.lineTo(15.f, 100.f);
61 
62         fPaths.addToTail(tri);
63         fPaths.addToTail(SkPath())->reverseAddPath(tri);
64 
65         tri.close();
66         fPaths.addToTail(tri);
67 
68         SkPath ngon;
69         constexpr SkScalar kRadius = 50.f;
70         const SkPoint center = { kRadius, kRadius };
71         for (int i = 0; i < GrConvexPolyEffect::kMaxEdges; ++i) {
72             SkScalar angle = 2 * SK_ScalarPI * i / GrConvexPolyEffect::kMaxEdges;
73             SkPoint point = { SkScalarCos(angle), SkScalarSin(angle) };
74             point.scale(kRadius);
75             point = center + point;
76             if (0 == i) {
77                 ngon.moveTo(point);
78             } else {
79                 ngon.lineTo(point);
80             }
81         }
82 
83         fPaths.addToTail(ngon);
84         SkMatrix scaleM;
85         scaleM.setScale(1.1f, 0.4f);
86         ngon.transform(scaleM);
87         fPaths.addToTail(ngon);
88 
89         SkPath linePath;
90         linePath.moveTo(5.f, 5.f);
91         linePath.lineTo(6.f, 6.f);
92         fPaths.addToTail(linePath);
93 
94         // integer edges
95         fRects.addToTail(SkRect::MakeLTRB(5.f, 1.f, 30.f, 25.f));
96         // half-integer edges
97         fRects.addToTail(SkRect::MakeLTRB(5.5f, 0.5f, 29.5f, 24.5f));
98         // vertically/horizontally thin rects that cover pixel centers
99         fRects.addToTail(SkRect::MakeLTRB(5.25f, 0.5f, 5.75f, 24.5f));
100         fRects.addToTail(SkRect::MakeLTRB(5.5f,  0.5f, 29.5f, 0.75f));
101         // vertically/horizontally thin rects that don't cover pixel centers
102         fRects.addToTail(SkRect::MakeLTRB(5.55f, 0.5f, 5.75f, 24.5f));
103         fRects.addToTail(SkRect::MakeLTRB(5.5f, .05f, 29.5f, .25f));
104         // small in x and y
105         fRects.addToTail(SkRect::MakeLTRB(5.05f, .55f, 5.45f, .85f));
106         // inverted in x and y
107         fRects.addToTail(SkRect::MakeLTRB(100.f, 50.5f, 5.f, 0.5f));
108     }
109 
onDraw(GrRecordingContext * context,GrRenderTargetContext * renderTargetContext,SkCanvas * canvas)110     void onDraw(GrRecordingContext* context, GrRenderTargetContext* renderTargetContext,
111                 SkCanvas* canvas) override {
112         SkScalar y = 0;
113         static constexpr SkScalar kDX = 12.f;
114         static constexpr SkScalar kOutset = 5.f;
115 
116         for (PathList::Iter iter(fPaths, PathList::Iter::kHead_IterStart);
117              iter.get();
118              iter.next()) {
119             const SkPath* path = iter.get();
120             SkScalar x = 0;
121 
122             for (int et = 0; et < kGrClipEdgeTypeCnt; ++et) {
123                 const SkMatrix m = SkMatrix::Translate(x, y);
124                 SkPath p;
125                 path->transform(m, &p);
126 
127                 GrClipEdgeType edgeType = (GrClipEdgeType) et;
128                 auto [success, fp] = GrConvexPolyEffect::Make(/*inputFP=*/nullptr, edgeType, p);
129                 if (!success) {
130                     continue;
131                 }
132 
133                 GrPaint grPaint;
134                 grPaint.setColor4f({ 0, 0, 0, 1.f });
135                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
136                 grPaint.setCoverageFragmentProcessor(std::move(fp));
137 
138                 auto rect = p.getBounds().makeOutset(kOutset, kOutset);
139                 auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(grPaint), rect);
140                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
141 
142                 x += SkScalarCeilToScalar(path->getBounds().width() + kDX);
143             }
144 
145             // Draw AA and non AA paths using normal API for reference.
146             canvas->save();
147             canvas->translate(x, y);
148             SkPaint paint;
149             canvas->drawPath(*path, paint);
150             canvas->translate(path->getBounds().width() + 10.f, 0);
151             paint.setAntiAlias(true);
152             canvas->drawPath(*path, paint);
153             canvas->restore();
154 
155             y += SkScalarCeilToScalar(path->getBounds().height() + 20.f);
156         }
157 
158         for (RectList::Iter iter(fRects, RectList::Iter::kHead_IterStart);
159              iter.get();
160              iter.next()) {
161 
162             SkScalar x = 0;
163 
164             for (int et = 0; et < kGrClipEdgeTypeCnt; ++et) {
165                 SkRect rect = iter.get()->makeOffset(x, y);
166                 GrClipEdgeType edgeType = (GrClipEdgeType) et;
167                 auto [success, fp] = GrConvexPolyEffect::Make(/*inputFP=*/nullptr, edgeType, rect);
168                 if (!success) {
169                     continue;
170                 }
171 
172                 GrPaint grPaint;
173                 grPaint.setColor4f({ 0, 0, 0, 1.f });
174                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
175                 grPaint.setCoverageFragmentProcessor(std::move(fp));
176 
177                 auto drawRect = rect.makeOutset(kOutset, kOutset);
178                 auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(grPaint), drawRect);
179 
180                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
181 
182                 x += SkScalarCeilToScalar(rect.width() + kDX);
183             }
184 
185             // Draw rect without and with AA using normal API for reference
186             canvas->save();
187             canvas->translate(x, y);
188             SkPaint paint;
189             canvas->drawRect(*iter.get(), paint);
190             x += SkScalarCeilToScalar(iter.get()->width() + kDX);
191             paint.setAntiAlias(true);
192             canvas->drawRect(*iter.get(), paint);
193             canvas->restore();
194 
195             y += SkScalarCeilToScalar(iter.get()->height() + 20.f);
196         }
197     }
198 
199 private:
200     typedef SkTLList<SkPath, 1> PathList;
201     typedef SkTLList<SkRect, 1> RectList;
202     PathList fPaths;
203     RectList fRects;
204 
205     using INHERITED = GM;
206 };
207 
208 DEF_GM(return new ConvexPolyEffect;)
209 }  // namespace skiagm
210