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/effects/generated/GrAARectEffect.h"
11#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
12#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
13#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
14#include "src/gpu/glsl/GrGLSLUniformHandler.h"
15
16//////////////////////////////////////////////////////////////////////////////
17
18class GrGLConvexPolyEffect : public GrGLSLFragmentProcessor {
19public:
20 GrGLConvexPolyEffect() {
21 for (size_t i = 0; i < SK_ARRAY_COUNT(fPrevEdges); ++i) {
22 fPrevEdges[i] = SK_ScalarNaN;
23 }
24 }
25
26 void emitCode(EmitArgs&) override;
27
28 static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
29
30protected:
31 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
32
33private:
34 GrGLSLProgramDataManager::UniformHandle fEdgeUniform;
35 SkScalar fPrevEdges[3 * GrConvexPolyEffect::kMaxEdges];
36 typedef GrGLSLFragmentProcessor INHERITED;
37};
38
39void GrGLConvexPolyEffect::emitCode(EmitArgs& args) {
40 const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>();
41
42 const char *edgeArrayName;
43 fEdgeUniform = args.fUniformHandler->addUniformArray(&cpe,
44 kFragment_GrShaderFlag,
45 kHalf3_GrSLType,
46 "edges",
47 cpe.getEdgeCount(),
48 &edgeArrayName);
49 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
50 fragBuilder->codeAppend("\t\thalf alpha = 1.0;\n");
51 fragBuilder->codeAppend("\t\thalf edge;\n");
52 for (int i = 0; i < cpe.getEdgeCount(); ++i) {
53 fragBuilder->codeAppendf("\t\tedge = dot(%s[%d], half3(half(sk_FragCoord.x), "
54 "half(sk_FragCoord.y), "
55 "1));\n",
56 edgeArrayName, i);
57 if (GrProcessorEdgeTypeIsAA(cpe.getEdgeType())) {
58 fragBuilder->codeAppend("\t\tedge = saturate(edge);\n");
59 } else {
60 fragBuilder->codeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n");
61 }
62 fragBuilder->codeAppend("\t\talpha *= edge;\n");
63 }
64
65 if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) {
66 fragBuilder->codeAppend("\talpha = 1.0 - alpha;\n");
67 }
68
69 SkString inputSample = this->invokeChild(/*childIndex=*/0, args);
70
71 fragBuilder->codeAppendf("\t%s = %s * alpha;\n", args.fOutputColor, inputSample.c_str());
72}
73
74void GrGLConvexPolyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
75 const GrFragmentProcessor& effect) {
76 const GrConvexPolyEffect& cpe = effect.cast<GrConvexPolyEffect>();
77 size_t byteSize = 3 * cpe.getEdgeCount() * sizeof(SkScalar);
78 if (0 != memcmp(fPrevEdges, cpe.getEdges(), byteSize)) {
79 pdman.set3fv(fEdgeUniform, cpe.getEdgeCount(), cpe.getEdges());
80 memcpy(fPrevEdges, cpe.getEdges(), byteSize);
81 }
82}
83
84void GrGLConvexPolyEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
85 GrProcessorKeyBuilder* b) {
86 const GrConvexPolyEffect& cpe = processor.cast<GrConvexPolyEffect>();
87 static_assert(kGrClipEdgeTypeCnt <= 8);
88 uint32_t key = (cpe.getEdgeCount() << 3) | (int) cpe.getEdgeType();
89 b->add32(key);
90}
91
92//////////////////////////////////////////////////////////////////////////////
93
94GrFPResult GrConvexPolyEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
95 GrClipEdgeType type, const SkPath& path) {
96 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) {
97 return GrFPFailure(std::move(inputFP));
98 }
99
100 SkPathPriv::FirstDirection dir;
101 // The only way this should fail is if the clip is effectively a infinitely thin line. In that
102 // case nothing is inside the clip. It'd be nice to detect this at a higher level and either
103 // skip the draw or omit the clip element.
104 if (!SkPathPriv::CheapComputeFirstDirection(path, &dir)) {
105 if (GrProcessorEdgeTypeIsInverseFill(type)) {
106 return GrFPSuccess(
107 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fWHITE));
108 }
109 // This could use ConstColor instead of ModulateRGBA but it would trigger a debug print
110 // about a coverage processor not being compatible with the alpha-as-coverage optimization.
111 // We don't really care about this unlikely case so we just use ModulateRGBA to suppress
112 // the print.
113 return GrFPSuccess(
114 GrFragmentProcessor::ModulateRGBA(std::move(inputFP), SK_PMColor4fTRANSPARENT));
115 }
116
117 SkScalar edges[3 * kMaxEdges];
118 SkPoint pts[4];
119 SkPath::Verb verb;
120 SkPath::Iter iter(path, true);
121
122 // SkPath considers itself convex so long as there is a convex contour within it,
123 // regardless of any degenerate contours such as a string of moveTos before it.
124 // Iterate here to consume any degenerate contours and only process the points
125 // on the actual convex contour.
126 int n = 0;
127 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
128 switch (verb) {
129 case SkPath::kMove_Verb:
130 SkASSERT(n == 0);
131 break;
132 case SkPath::kClose_Verb:
133 break;
134 case SkPath::kLine_Verb: {
135 if (n >= kMaxEdges) {
136 return GrFPFailure(std::move(inputFP));
137 }
138 if (pts[0] != pts[1]) {
139 SkVector v = pts[1] - pts[0];
140 v.normalize();
141 if (SkPathPriv::kCCW_FirstDirection == dir) {
142 edges[3 * n] = v.fY;
143 edges[3 * n + 1] = -v.fX;
144 } else {
145 edges[3 * n] = -v.fY;
146 edges[3 * n + 1] = v.fX;
147 }
148 edges[3 * n + 2] = -(edges[3 * n] * pts[1].fX + edges[3 * n + 1] * pts[1].fY);
149 ++n;
150 }
151 break;
152 }
153 default:
154 return GrFPFailure(std::move(inputFP));
155 }
156 }
157
158 if (path.isInverseFillType()) {
159 type = GrInvertProcessorEdgeType(type);
160 }
161 return GrConvexPolyEffect::Make(std::move(inputFP), type, n, edges);
162}
163
164GrFPResult GrConvexPolyEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
165 GrClipEdgeType edgeType, const SkRect& rect) {
166 // TODO: Replace calls to this method with calling GrAARectEffect::Make directly
167 return GrFPSuccess(GrAARectEffect::Make(std::move(inputFP), edgeType, rect));
168}
169
170GrConvexPolyEffect::~GrConvexPolyEffect() {}
171
172void GrConvexPolyEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
173 GrProcessorKeyBuilder* b) const {
174 GrGLConvexPolyEffect::GenKey(*this, caps, b);
175}
176
177GrGLSLFragmentProcessor* GrConvexPolyEffect::onCreateGLSLInstance() const {
178 return new GrGLConvexPolyEffect;
179}
180
181GrConvexPolyEffect::GrConvexPolyEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
182 GrClipEdgeType edgeType, int n, const SkScalar edges[])
183 : INHERITED(kGrConvexPolyEffect_ClassID, kCompatibleWithCoverageAsAlpha_OptimizationFlag)
184 , fEdgeType(edgeType)
185 , fEdgeCount(n) {
186 // Factory function should have already ensured this.
187 SkASSERT(n <= kMaxEdges);
188 memcpy(fEdges, edges, 3 * n * sizeof(SkScalar));
189 // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case
190 // and 100% covered in the non-AA case.
191 for (int i = 0; i < n; ++i) {
192 fEdges[3 * i + 2] += SK_ScalarHalf;
193 }
194
195 this->registerChild(std::move(inputFP));
196}
197
198GrConvexPolyEffect::GrConvexPolyEffect(const GrConvexPolyEffect& that)
199 : INHERITED(kGrConvexPolyEffect_ClassID, kCompatibleWithCoverageAsAlpha_OptimizationFlag)
200 , fEdgeType(that.fEdgeType)
201 , fEdgeCount(that.fEdgeCount) {
202 this->cloneAndRegisterAllChildProcessors(that);
203 memcpy(fEdges, that.fEdges, 3 * that.fEdgeCount * sizeof(SkScalar));
204}
205
206std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const {
207 return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this));
208}
209
210bool GrConvexPolyEffect::onIsEqual(const GrFragmentProcessor& other) const {
211 const GrConvexPolyEffect& cpe = other.cast<GrConvexPolyEffect>();
212 // ignore the fact that 0 == -0 and just use memcmp.
213 return (cpe.fEdgeType == fEdgeType && cpe.fEdgeCount == fEdgeCount &&
214 0 == memcmp(cpe.fEdges, fEdges, 3 * fEdgeCount * sizeof(SkScalar)));
215}
216
217//////////////////////////////////////////////////////////////////////////////
218
219GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect);
220
221#if GR_TEST_UTILS
222std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::TestCreate(GrProcessorTestData* d) {
223 int count = d->fRandom->nextULessThan(kMaxEdges) + 1;
224 SkScalar edges[kMaxEdges * 3];
225 for (int i = 0; i < 3 * count; ++i) {
226 edges[i] = d->fRandom->nextSScalar1();
227 }
228
229 bool success;
230 std::unique_ptr<GrFragmentProcessor> fp = d->inputFP();
231 do {
232 GrClipEdgeType edgeType =
233 static_cast<GrClipEdgeType>(d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
234 std::tie(success, fp) = GrConvexPolyEffect::Make(std::move(fp), edgeType, count, edges);
235 } while (!success);
236 return fp;
237}
238#endif
239