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 #include "src/gpu/effects/GrConvexPolyEffect.h"
9
10 #include "src/core/SkPathPriv.h"
11 #include "src/gpu/KeyBuilder.h"
12 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
13 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
14 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
15 #include "src/sksl/dsl/priv/DSLFPs.h"
16
17 //////////////////////////////////////////////////////////////////////////////
18
Make(std::unique_ptr<GrFragmentProcessor> inputFP,GrClipEdgeType type,const SkPath & path)19 GrFPResult GrConvexPolyEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
20 GrClipEdgeType type, const SkPath& path) {
21 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) {
22 return GrFPFailure(std::move(inputFP));
23 }
24
25 SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(path);
26 // The only way this should fail is if the clip is effectively a infinitely thin line. In that
27 // case nothing is inside the clip. It'd be nice to detect this at a higher level and either
28 // skip the draw or omit the clip element.
29 if (dir == SkPathFirstDirection::kUnknown) {
30 if (GrClipEdgeTypeIsInverseFill(type)) {
31 return GrFPSuccess(
32 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fWHITE));
33 }
34 // This could use ConstColor instead of ModulateRGBA but it would trigger a debug print
35 // about a coverage processor not being compatible with the alpha-as-coverage optimization.
36 // We don't really care about this unlikely case so we just use ModulateRGBA to suppress
37 // the print.
38 return GrFPSuccess(
39 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fTRANSPARENT));
40 }
41
42 SkScalar edges[3 * kMaxEdges];
43 SkPoint pts[4];
44 SkPath::Verb verb;
45 SkPath::Iter iter(path, true);
46
47 // SkPath considers itself convex so long as there is a convex contour within it,
48 // regardless of any degenerate contours such as a string of moveTos before it.
49 // Iterate here to consume any degenerate contours and only process the points
50 // on the actual convex contour.
51 int n = 0;
52 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
53 switch (verb) {
54 case SkPath::kMove_Verb:
55 case SkPath::kClose_Verb:
56 break;
57 case SkPath::kLine_Verb: {
58 if (n >= kMaxEdges) {
59 return GrFPFailure(std::move(inputFP));
60 }
61 if (pts[0] != pts[1]) {
62 SkVector v = pts[1] - pts[0];
63 v.normalize();
64 if (SkPathFirstDirection::kCCW == dir) {
65 edges[3 * n] = v.fY;
66 edges[3 * n + 1] = -v.fX;
67 } else {
68 edges[3 * n] = -v.fY;
69 edges[3 * n + 1] = v.fX;
70 }
71 edges[3 * n + 2] = -(edges[3 * n] * pts[1].fX + edges[3 * n + 1] * pts[1].fY);
72 ++n;
73 }
74 break;
75 }
76 default:
77 // Non-linear segment so not a polygon.
78 return GrFPFailure(std::move(inputFP));
79 }
80 }
81
82 if (path.isInverseFillType()) {
83 type = GrInvertClipEdgeType(type);
84 }
85 return GrConvexPolyEffect::Make(std::move(inputFP), type, n, edges);
86 }
87
~GrConvexPolyEffect()88 GrConvexPolyEffect::~GrConvexPolyEffect() {}
89
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const90 void GrConvexPolyEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
91 static_assert(kGrClipEdgeTypeCnt <= 8);
92 uint32_t key = (fEdgeCount << 3) | static_cast<int>(fEdgeType);
93 b->add32(key);
94 }
95
onMakeProgramImpl() const96 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrConvexPolyEffect::onMakeProgramImpl() const {
97 class Impl : public ProgramImpl {
98 public:
99 void emitCode(EmitArgs& args) override {
100 const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>();
101
102 using namespace SkSL::dsl;
103 StartFragmentProcessor(this, &args);
104 GlobalVar edgeArray(kUniform_Modifier, Array(kHalf3_Type, cpe.fEdgeCount), "edgeArray");
105 Declare(edgeArray);
106 fEdgeUniform = VarUniformHandle(edgeArray);
107 Var alpha(kHalf_Type, "alpha", 1);
108 Declare(alpha);
109 Var edge(kHalf_Type, "edge");
110 Declare(edge);
111 for (int i = 0; i < cpe.fEdgeCount; ++i) {
112 edge = Dot(edgeArray[i], Half3(Swizzle(sk_FragCoord(), X, Y, ONE)));
113 if (GrClipEdgeTypeIsAA(cpe.fEdgeType)) {
114 edge = Saturate(edge);
115 } else {
116 edge = Select(edge >= 0.5, 1.0, 0.0);
117 }
118 alpha *= edge;
119 }
120
121 if (GrClipEdgeTypeIsInverseFill(cpe.fEdgeType)) {
122 alpha = 1.0 - alpha;
123 }
124
125 Return(SampleChild(0) * alpha);
126 EndFragmentProcessor();
127 }
128
129 private:
130 void onSetData(const GrGLSLProgramDataManager& pdman,
131 const GrFragmentProcessor& fp) override {
132 const GrConvexPolyEffect& cpe = fp.cast<GrConvexPolyEffect>();
133 size_t n = 3*cpe.fEdgeCount;
134 if (!std::equal(fPrevEdges.begin(), fPrevEdges.begin() + n, cpe.fEdges.begin())) {
135 pdman.set3fv(fEdgeUniform, cpe.fEdgeCount, cpe.fEdges.data());
136 std::copy_n(cpe.fEdges.begin(), n, fPrevEdges.begin());
137 }
138 }
139
140 GrGLSLProgramDataManager::UniformHandle fEdgeUniform;
141 std::array<float, 3 * GrConvexPolyEffect::kMaxEdges> fPrevEdges = {SK_FloatNaN};
142 };
143
144 return std::make_unique<Impl>();
145 }
146
GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,GrClipEdgeType edgeType,int n,const float edges[])147 GrConvexPolyEffect::GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
148 GrClipEdgeType edgeType,
149 int n,
150 const float edges[])
151 : INHERITED(kGrConvexPolyEffect_ClassID,
152 ProcessorOptimizationFlags(inputFP.get()) &
153 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
154 , fEdgeType(edgeType)
155 , fEdgeCount(n) {
156 // Factory function should have already ensured this.
157 SkASSERT(n <= kMaxEdges);
158 std::copy_n(edges, 3*n, fEdges.begin());
159 // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case
160 // and 100% covered in the non-AA case.
161 for (int i = 0; i < n; ++i) {
162 fEdges[3 * i + 2] += SK_ScalarHalf;
163 }
164
165 this->registerChild(std::move(inputFP));
166 }
167
GrConvexPolyEffect(const GrConvexPolyEffect & that)168 GrConvexPolyEffect::GrConvexPolyEffect(const GrConvexPolyEffect& that)
169 : INHERITED(that)
170 , fEdgeType(that.fEdgeType)
171 , fEdgeCount(that.fEdgeCount) {
172 std::copy_n(that.fEdges.begin(), 3*that.fEdgeCount, fEdges.begin());
173 }
174
clone() const175 std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const {
176 return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this));
177 }
178
onIsEqual(const GrFragmentProcessor & other) const179 bool GrConvexPolyEffect::onIsEqual(const GrFragmentProcessor& other) const {
180 const GrConvexPolyEffect& cpe = other.cast<GrConvexPolyEffect>();
181 int n = 3*cpe.fEdgeCount;
182 return cpe.fEdgeType == fEdgeType &&
183 cpe.fEdgeCount == fEdgeCount &&
184 std::equal(cpe.fEdges.begin(), cpe.fEdges.begin() + n, fEdges.begin());
185 }
186
187 //////////////////////////////////////////////////////////////////////////////
188
189 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect);
190
191 #if GR_TEST_UTILS
TestCreate(GrProcessorTestData * d)192 std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::TestCreate(GrProcessorTestData* d) {
193 int count = d->fRandom->nextULessThan(kMaxEdges) + 1;
194 SkScalar edges[kMaxEdges * 3];
195 for (int i = 0; i < 3 * count; ++i) {
196 edges[i] = d->fRandom->nextSScalar1();
197 }
198
199 bool success;
200 std::unique_ptr<GrFragmentProcessor> fp = d->inputFP();
201 do {
202 GrClipEdgeType edgeType =
203 static_cast<GrClipEdgeType>(d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
204 std::tie(success, fp) = GrConvexPolyEffect::Make(std::move(fp), edgeType, count, edges);
205 } while (!success);
206 return fp;
207 }
208 #endif
209