blob: a0440f0bd66914f492a0d0304e6187dd21336612 [file] [log] [blame]
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +00001/*
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 "GrConvexPolyEffect.h"
reed026beb52015-06-10 14:23:15 -07009#include "SkPathPriv.h"
bsalomon7888de02016-03-28 15:04:45 -070010#include "effects/GrConstColorProcessor.h"
Ethan Nicholas222e2752018-10-11 11:21:34 -040011#include "effects/GrSkSLFP.h"
egdaniel64c47282015-11-13 06:54:19 -080012#include "glsl/GrGLSLFragmentProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080013#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070014#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080015#include "glsl/GrGLSLUniformHandler.h"
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000016
Ethan Nicholas222e2752018-10-11 11:21:34 -040017GR_FP_SRC_STRING SKSL_AARECT_SRC = R"(
18layout(key) in GrClipEdgeType edgeType;
19layout(ctype=SkRect) in float4 rect;
20uniform float4 rectUniform;
21
22void main(int x, int y, inout half4 color) {
23 half alpha;
24 @switch (edgeType) {
25 case GrClipEdgeType::kFillBW: // fall through
26 case GrClipEdgeType::kInverseFillBW:
27 // non-AA
28 alpha = all(greaterThan(float4(x, y, rectUniform.zw),
29 float4(rectUniform.xy, x, y))) ? 1 : 0;
30 break;
31 default:
32 // The amount of coverage removed in x and y by the edges is computed as a pair of
33 // negative numbers, xSub and ySub.
34 half xSub, ySub;
35 xSub = min(x - rectUniform.x, 0.0);
36 xSub += min(rectUniform.z - x, 0.0);
37 ySub = min(y - rectUniform.y, 0.0);
38 ySub += min(rectUniform.w - y, 0.0);
39 // Now compute coverage in x and y and multiply them to get the fraction of the pixel
40 // covered.
41 alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));
42 }
43
44 @if (edgeType == GrClipEdgeType::kInverseFillBW || edgeType == GrClipEdgeType::kInverseFillAA) {
45 alpha = 1.0 - alpha;
46 }
47 color *= alpha;
48}
49)";
50
51struct AARectInputs {
52 GrClipEdgeType fEdgeType;
53 SkRect fRect;
54};
55
commit-bot@chromium.orgf0539802014-02-08 19:31:05 +000056//////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.orgf0539802014-02-08 19:31:05 +000057
egdaniel64c47282015-11-13 06:54:19 -080058class GrGLConvexPolyEffect : public GrGLSLFragmentProcessor {
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000059public:
robertphillips9cdb9922016-02-03 12:25:40 -080060 GrGLConvexPolyEffect() {
Brian Salomon73a850f2017-03-27 10:17:38 -040061 for (size_t i = 0; i < SK_ARRAY_COUNT(fPrevEdges); ++i) {
62 fPrevEdges[i] = SK_ScalarNaN;
63 }
robertphillips9cdb9922016-02-03 12:25:40 -080064 }
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000065
robertphillips9cdb9922016-02-03 12:25:40 -080066 void emitCode(EmitArgs&) override;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000067
Brian Salomon94efbf52016-11-29 13:43:05 -050068 static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000069
wangyixb1daa862015-08-18 11:29:31 -070070protected:
Brian Salomonab015ef2017-04-04 10:15:51 -040071 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000072
73private:
egdaniel018fb622015-10-28 07:26:40 -070074 GrGLSLProgramDataManager::UniformHandle fEdgeUniform;
robertphillipsbf536af2016-02-04 06:11:53 -080075 SkScalar fPrevEdges[3 * GrConvexPolyEffect::kMaxEdges];
egdaniel64c47282015-11-13 06:54:19 -080076 typedef GrGLSLFragmentProcessor INHERITED;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000077};
78
wangyix7c157a92015-07-22 15:08:53 -070079void GrGLConvexPolyEffect::emitCode(EmitArgs& args) {
80 const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>();
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000081
82 const char *edgeArrayName;
cdalton5e58cee2016-02-11 12:49:47 -080083 fEdgeUniform = args.fUniformHandler->addUniformArray(kFragment_GrShaderFlag,
Ethan Nicholasf7b88202017-09-18 14:10:39 -040084 kHalf3_GrSLType,
egdaniel7ea439b2015-12-03 09:20:44 -080085 "edges",
86 cpe.getEdgeCount(),
87 &edgeArrayName);
cdalton85285412016-02-18 12:37:07 -080088 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
Ethan Nicholasf7b88202017-09-18 14:10:39 -040089 fragBuilder->codeAppend("\t\thalf alpha = 1.0;\n");
90 fragBuilder->codeAppend("\t\thalf edge;\n");
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000091 for (int i = 0; i < cpe.getEdgeCount(); ++i) {
Ethan Nicholasf7b88202017-09-18 14:10:39 -040092 fragBuilder->codeAppendf("\t\tedge = dot(%s[%d], half3(sk_FragCoord.x, sk_FragCoord.y, "
Ethan Nicholas38657112017-02-09 17:01:22 -050093 "1));\n",
94 edgeArrayName, i);
joshualittb0a8a372014-09-23 09:50:21 -070095 if (GrProcessorEdgeTypeIsAA(cpe.getEdgeType())) {
Ethan Nicholas12fb9cf2018-08-03 16:16:57 -040096 fragBuilder->codeAppend("\t\tedge = saturate(edge);\n");
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +000097 } else {
egdaniel4ca2e602015-11-18 08:01:26 -080098 fragBuilder->codeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n");
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +000099 }
egdaniel4ca2e602015-11-18 08:01:26 -0800100 fragBuilder->codeAppend("\t\talpha *= edge;\n");
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000101 }
102
joshualittb0a8a372014-09-23 09:50:21 -0700103 if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) {
egdaniel4ca2e602015-11-18 08:01:26 -0800104 fragBuilder->codeAppend("\talpha = 1.0 - alpha;\n");
commit-bot@chromium.orgd85f32c2014-02-28 14:43:26 +0000105 }
Ethan Nicholas2983f402017-05-08 09:36:08 -0400106 fragBuilder->codeAppendf("\t%s = %s * alpha;\n", args.fOutputColor, args.fInputColor);
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000107}
108
egdaniel018fb622015-10-28 07:26:40 -0700109void GrGLConvexPolyEffect::onSetData(const GrGLSLProgramDataManager& pdman,
Brian Salomonab015ef2017-04-04 10:15:51 -0400110 const GrFragmentProcessor& effect) {
joshualitt49586be2014-09-16 08:21:41 -0700111 const GrConvexPolyEffect& cpe = effect.cast<GrConvexPolyEffect>();
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000112 size_t byteSize = 3 * cpe.getEdgeCount() * sizeof(SkScalar);
113 if (0 != memcmp(fPrevEdges, cpe.getEdges(), byteSize)) {
kkinnunen7510b222014-07-30 00:04:16 -0700114 pdman.set3fv(fEdgeUniform, cpe.getEdgeCount(), cpe.getEdges());
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000115 memcpy(fPrevEdges, cpe.getEdges(), byteSize);
116 }
117}
118
Brian Salomon94efbf52016-11-29 13:43:05 -0500119void GrGLConvexPolyEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700120 GrProcessorKeyBuilder* b) {
121 const GrConvexPolyEffect& cpe = processor.cast<GrConvexPolyEffect>();
Ethan Nicholas1706f842017-11-10 11:58:19 -0500122 GR_STATIC_ASSERT(kGrClipEdgeTypeCnt <= 8);
123 uint32_t key = (cpe.getEdgeCount() << 3) | (int) cpe.getEdgeType();
bsalomon63e99f72014-07-21 08:03:14 -0700124 b->add32(key);
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000125}
126
127//////////////////////////////////////////////////////////////////////////////
128
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500129std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::Make(GrClipEdgeType type,
Ethan Nicholas222e2752018-10-11 11:21:34 -0400130 const SkPath& path,
131 GrContext* context) {
Ethan Nicholas1706f842017-11-10 11:58:19 -0500132 if (GrClipEdgeType::kHairlineAA == type) {
halcanary96fcdcc2015-08-27 07:41:13 -0700133 return nullptr;
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000134 }
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000135 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask ||
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000136 !path.isConvex()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700137 return nullptr;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000138 }
139
reed026beb52015-06-10 14:23:15 -0700140 SkPathPriv::FirstDirection dir;
bsalomon7888de02016-03-28 15:04:45 -0700141 // The only way this should fail is if the clip is effectively a infinitely thin line. In that
142 // case nothing is inside the clip. It'd be nice to detect this at a higher level and either
143 // skip the draw or omit the clip element.
144 if (!SkPathPriv::CheapComputeFirstDirection(path, &dir)) {
145 if (GrProcessorEdgeTypeIsInverseFill(type)) {
Brian Osmanf28e55d2018-10-03 16:35:54 -0400146 return GrConstColorProcessor::Make(SK_PMColor4fWHITE,
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500147 GrConstColorProcessor::InputMode::kModulateRGBA);
bsalomon7888de02016-03-28 15:04:45 -0700148 }
Brian Salomoneb628292017-02-15 14:12:26 -0500149 // This could use kIgnore instead of kModulateRGBA but it would trigger a debug print
150 // about a coverage processor not being compatible with the alpha-as-coverage optimization.
151 // We don't really care about this unlikely case so we just use kModulateRGBA to suppress
152 // the print.
Brian Osmanf28e55d2018-10-03 16:35:54 -0400153 return GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500154 GrConstColorProcessor::InputMode::kModulateRGBA);
bsalomon7888de02016-03-28 15:04:45 -0700155 }
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000156
lsalzmand15947e2016-05-31 09:46:00 -0700157 SkScalar edges[3 * kMaxEdges];
158 SkPoint pts[4];
159 SkPath::Verb verb;
160 SkPath::Iter iter(path, true);
161
162 // SkPath considers itself convex so long as there is a convex contour within it,
163 // regardless of any degenerate contours such as a string of moveTos before it.
164 // Iterate here to consume any degenerate contours and only process the points
165 // on the actual convex contour.
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000166 int n = 0;
lsalzmand15947e2016-05-31 09:46:00 -0700167 while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
168 switch (verb) {
169 case SkPath::kMove_Verb:
170 SkASSERT(n == 0);
171 case SkPath::kClose_Verb:
172 break;
173 case SkPath::kLine_Verb: {
174 if (n >= kMaxEdges) {
175 return nullptr;
176 }
177 SkVector v = pts[1] - pts[0];
178 v.normalize();
179 if (SkPathPriv::kCCW_FirstDirection == dir) {
180 edges[3 * n] = v.fY;
181 edges[3 * n + 1] = -v.fX;
182 } else {
183 edges[3 * n] = -v.fY;
184 edges[3 * n + 1] = v.fX;
185 }
Brian Salomon9a767722017-03-13 17:57:28 -0400186 edges[3 * n + 2] = -(edges[3 * n] * pts[1].fX + edges[3 * n + 1] * pts[1].fY);
lsalzmand15947e2016-05-31 09:46:00 -0700187 ++n;
188 break;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000189 }
lsalzmand15947e2016-05-31 09:46:00 -0700190 default:
191 return nullptr;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000192 }
193 }
lsalzmand15947e2016-05-31 09:46:00 -0700194
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000195 if (path.isInverseFillType()) {
joshualittb0a8a372014-09-23 09:50:21 -0700196 type = GrInvertProcessorEdgeType(type);
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000197 }
bungeman06ca8ec2016-06-09 08:01:03 -0700198 return Make(type, n, edges);
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000199}
200
Ethan Nicholas222e2752018-10-11 11:21:34 -0400201static void aa_rect_set_data_hook(const GrGLSLProgramDataManager& pdman,
202 const GrGLSLSkSLFP& glslProc,
203 const AARectInputs& inputs,
204 SkRect* prevRect) {
205 if (inputs.fRect != *prevRect) {
206 const SkRect& newRect = GrProcessorEdgeTypeIsAA(inputs.fEdgeType) ?
207 inputs.fRect.makeInset(.5f, .5f) : inputs.fRect;
208 pdman.set4f(glslProc.uniformHandle(0), newRect.fLeft, newRect.fTop, newRect.fRight,
209 newRect.fBottom);
210 *prevRect = inputs.fRect;
211 }
212}
213
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500214std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::Make(GrClipEdgeType edgeType,
Ethan Nicholas222e2752018-10-11 11:21:34 -0400215 const SkRect& rect,
216 GrContext* context) {
Ethan Nicholas1706f842017-11-10 11:58:19 -0500217 if (GrClipEdgeType::kHairlineAA == edgeType){
halcanary96fcdcc2015-08-27 07:41:13 -0700218 return nullptr;
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000219 }
Ethan Nicholas222e2752018-10-11 11:21:34 -0400220 static int aaRectIndex = GrSkSLFP::NewIndex();
221 AARectInputs inputs;
222 inputs.fEdgeType = edgeType;
223 inputs.fRect = rect;
224 std::unique_ptr<GrSkSLFP> result = GrSkSLFP::Make(context, aaRectIndex, "AARect",
225 SKSL_AARECT_SRC, inputs,
226 SkRect::MakeXYWH(-1, -1, -1, -1),
227 aa_rect_set_data_hook);
228 return std::unique_ptr<GrFragmentProcessor>(result.release());
commit-bot@chromium.orgf0539802014-02-08 19:31:05 +0000229}
230
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000231GrConvexPolyEffect::~GrConvexPolyEffect() {}
232
Brian Salomon94efbf52016-11-29 13:43:05 -0500233void GrConvexPolyEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -0800234 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -0800235 GrGLConvexPolyEffect::GenKey(*this, caps, b);
236}
237
egdaniel57d3b032015-11-13 11:57:27 -0800238GrGLSLFragmentProcessor* GrConvexPolyEffect::onCreateGLSLInstance() const {
robertphillips9cdb9922016-02-03 12:25:40 -0800239 return new GrGLConvexPolyEffect;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000240}
241
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500242GrConvexPolyEffect::GrConvexPolyEffect(GrClipEdgeType edgeType, int n, const SkScalar edges[])
Ethan Nicholasabff9562017-10-09 10:54:08 -0400243 : INHERITED(kGrConvexPolyEffect_ClassID, kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomonf3b995b2017-02-15 10:22:23 -0500244 , fEdgeType(edgeType)
245 , fEdgeCount(n) {
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000246 // Factory function should have already ensured this.
247 SkASSERT(n <= kMaxEdges);
248 memcpy(fEdges, edges, 3 * n * sizeof(SkScalar));
249 // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case
250 // and 100% covered in the non-AA case.
251 for (int i = 0; i < n; ++i) {
252 fEdges[3 * i + 2] += SK_ScalarHalf;
253 }
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000254}
255
Brian Salomonfcc527b2017-07-26 12:21:21 -0400256GrConvexPolyEffect::GrConvexPolyEffect(const GrConvexPolyEffect& that)
Ethan Nicholasabff9562017-10-09 10:54:08 -0400257 : INHERITED(kGrConvexPolyEffect_ClassID, kCompatibleWithCoverageAsAlpha_OptimizationFlag)
Brian Salomonfcc527b2017-07-26 12:21:21 -0400258 , fEdgeType(that.fEdgeType)
259 , fEdgeCount(that.fEdgeCount) {
Brian Salomonfcc527b2017-07-26 12:21:21 -0400260 memcpy(fEdges, that.fEdges, 3 * that.fEdgeCount * sizeof(SkScalar));
261}
262
Brian Salomonaff329b2017-08-11 09:40:37 -0400263std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const {
264 return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this));
Brian Salomonfcc527b2017-07-26 12:21:21 -0400265}
266
bsalomon0e08fc12014-10-15 08:19:04 -0700267bool GrConvexPolyEffect::onIsEqual(const GrFragmentProcessor& other) const {
joshualitt49586be2014-09-16 08:21:41 -0700268 const GrConvexPolyEffect& cpe = other.cast<GrConvexPolyEffect>();
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000269 // ignore the fact that 0 == -0 and just use memcmp.
270 return (cpe.fEdgeType == fEdgeType && cpe.fEdgeCount == fEdgeCount &&
271 0 == memcmp(cpe.fEdges, fEdges, 3 * fEdgeCount * sizeof(SkScalar)));
272}
273
274//////////////////////////////////////////////////////////////////////////////
275
joshualittb0a8a372014-09-23 09:50:21 -0700276GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect);
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000277
Hal Canary6f6961e2017-01-31 13:50:44 -0500278#if GR_TEST_UTILS
Brian Salomonaff329b2017-08-11 09:40:37 -0400279std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700280 int count = d->fRandom->nextULessThan(kMaxEdges) + 1;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000281 SkScalar edges[kMaxEdges * 3];
282 for (int i = 0; i < 3 * count; ++i) {
joshualitt0067ff52015-07-08 14:26:19 -0700283 edges[i] = d->fRandom->nextSScalar1();
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000284 }
285
Brian Salomonaff329b2017-08-11 09:40:37 -0400286 std::unique_ptr<GrFragmentProcessor> fp;
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000287 do {
Ethan Nicholas0f3c7322017-11-09 14:51:17 -0500288 GrClipEdgeType edgeType = static_cast<GrClipEdgeType>(
Ethan Nicholas1706f842017-11-10 11:58:19 -0500289 d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
bungeman06ca8ec2016-06-09 08:01:03 -0700290 fp = GrConvexPolyEffect::Make(edgeType, count, edges);
halcanary96fcdcc2015-08-27 07:41:13 -0700291 } while (nullptr == fp);
joshualittb0a8a372014-09-23 09:50:21 -0700292 return fp;
commit-bot@chromium.orgc3fe5492014-01-30 18:15:51 +0000293}
Hal Canary6f6961e2017-01-31 13:50:44 -0500294#endif