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