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
17GrFPResult 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
86GrConvexPolyEffect::~GrConvexPolyEffect() {}
87
88SkString GrConvexPolyEffect::getShaderDfxInfo() const
89{
90    SkString format;
91    format.printf("ShaderDfx_GrConvexPolyEffect_%d_%d", fEdgeCount, fEdgeType);
92    return format;
93}
94
95void 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
101std::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
152GrConvexPolyEffect::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
173GrConvexPolyEffect::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
180std::unique_ptr<GrFragmentProcessor> GrConvexPolyEffect::clone() const {
181    return std::unique_ptr<GrFragmentProcessor>(new GrConvexPolyEffect(*this));
182}
183
184bool 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
194GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConvexPolyEffect);
195
196#if GR_TEST_UTILS
197std::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