1/*
2 * Copyright 2013 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#ifndef GrBezierEffect_DEFINED
9#define GrBezierEffect_DEFINED
10
11#include "GrCaps.h"
12#include "GrProcessor.h"
13#include "GrGeometryProcessor.h"
14#include "GrTypesPriv.h"
15
16/**
17 * Shader is based off of Loop-Blinn Quadratic GPU Rendering
18 * The output of this effect is a hairline edge for conics.
19 * Conics specified by implicit equation K^2 - LM.
20 * K, L, and M, are the first three values of the vertex attribute,
21 * the fourth value is not used. Distance is calculated using a
22 * first order approximation from the taylor series.
23 * Coverage for AA is max(0, 1-distance).
24 *
25 * Test were also run using a second order distance approximation.
26 * There were two versions of the second order approx. The first version
27 * is of roughly the form:
28 * f(q) = |f(p)| - ||f'(p)||*||q-p|| - ||f''(p)||*||q-p||^2.
29 * The second is similar:
30 * f(q) = |f(p)| + ||f'(p)||*||q-p|| + ||f''(p)||*||q-p||^2.
31 * The exact version of the equations can be found in the paper
32 * "Distance Approximations for Rasterizing Implicit Curves" by Gabriel Taubin
33 *
34 * In both versions we solve the quadratic for ||q-p||.
35 * Version 1:
36 * gFM is magnitude of first partials and gFM2 is magnitude of 2nd partials (as derived from paper)
37 * builder->fsCodeAppend("\t\tedgeAlpha = (sqrt(gFM*gFM+4.0*func*gF2M) - gFM)/(2.0*gF2M);\n");
38 * Version 2:
39 * builder->fsCodeAppend("\t\tedgeAlpha = (gFM - sqrt(gFM*gFM-4.0*func*gF2M))/(2.0*gF2M);\n");
40 *
41 * Also note that 2nd partials of k,l,m are zero
42 *
43 * When comparing the two second order approximations to the first order approximations,
44 * the following results were found. Version 1 tends to underestimate the distances, thus it
45 * basically increases all the error that we were already seeing in the first order
46 * approx. So this version is not the one to use. Version 2 has the opposite effect
47 * and tends to overestimate the distances. This is much closer to what we are
48 * looking for. It is able to render ellipses (even thin ones) without the need to chop.
49 * However, it can not handle thin hyperbolas well and thus would still rely on
50 * chopping to tighten the clipping. Another side effect of the overestimating is
51 * that the curves become much thinner and "ropey". If all that was ever rendered
52 * were "not too thin" curves and ellipses then 2nd order may have an advantage since
53 * only one geometry would need to be rendered. However no benches were run comparing
54 * chopped first order and non chopped 2nd order.
55 */
56class GrGLConicEffect;
57
58class GrConicEffect : public GrGeometryProcessor {
59public:
60    static sk_sp<GrGeometryProcessor> Make(GrColor color,
61                                           const SkMatrix& viewMatrix,
62                                           const GrClipEdgeType edgeType,
63                                           const GrCaps& caps,
64                                           const SkMatrix& localMatrix,
65                                           bool usesLocalCoords,
66                                           uint8_t coverage = 0xff) {
67        switch (edgeType) {
68            case GrClipEdgeType::kFillAA:
69                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
70                    return nullptr;
71                }
72                return sk_sp<GrGeometryProcessor>(
73                    new GrConicEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillAA,
74                                      localMatrix, usesLocalCoords));
75            case GrClipEdgeType::kHairlineAA:
76                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
77                    return nullptr;
78                }
79                return sk_sp<GrGeometryProcessor>(
80                    new GrConicEffect(color, viewMatrix, coverage,
81                                      GrClipEdgeType::kHairlineAA, localMatrix,
82                                      usesLocalCoords));
83            case GrClipEdgeType::kFillBW:
84                return sk_sp<GrGeometryProcessor>(
85                    new GrConicEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillBW,
86                                      localMatrix, usesLocalCoords));
87            default:
88                return nullptr;
89        }
90    }
91
92    ~GrConicEffect() override;
93
94    const char* name() const override { return "Conic"; }
95
96    inline const Attribute* inPosition() const { return fInPosition; }
97    inline const Attribute* inConicCoeffs() const { return fInConicCoeffs; }
98    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
99    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
100    inline GrClipEdgeType getEdgeType() const { return fEdgeType; }
101    GrColor color() const { return fColor; }
102    const SkMatrix& viewMatrix() const { return fViewMatrix; }
103    const SkMatrix& localMatrix() const { return fLocalMatrix; }
104    bool usesLocalCoords() const { return fUsesLocalCoords; }
105    uint8_t coverageScale() const { return fCoverageScale; }
106
107    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
108
109    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
110
111private:
112    GrConicEffect(GrColor, const SkMatrix& viewMatrix, uint8_t coverage, GrClipEdgeType,
113                  const SkMatrix& localMatrix, bool usesLocalCoords);
114
115    GrColor             fColor;
116    SkMatrix            fViewMatrix;
117    SkMatrix            fLocalMatrix;
118    bool                fUsesLocalCoords;
119    uint8_t             fCoverageScale;
120    GrClipEdgeType fEdgeType;
121    const Attribute*    fInPosition;
122    const Attribute*    fInConicCoeffs;
123
124    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
125
126    typedef GrGeometryProcessor INHERITED;
127};
128
129///////////////////////////////////////////////////////////////////////////////
130/**
131 * The output of this effect is a hairline edge for quadratics.
132 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
133 * two components of the vertex attribute. At the three control points that define
134 * the Quadratic, u, v have the values {0,0}, {1/2, 0}, and {1, 1} respectively.
135 * Coverage for AA is min(0, 1-distance). 3rd & 4th cimponent unused.
136 * Requires shader derivative instruction support.
137 */
138class GrGLQuadEffect;
139
140class GrQuadEffect : public GrGeometryProcessor {
141public:
142    static sk_sp<GrGeometryProcessor> Make(GrColor color,
143                                           const SkMatrix& viewMatrix,
144                                           const GrClipEdgeType edgeType,
145                                           const GrCaps& caps,
146                                           const SkMatrix& localMatrix,
147                                           bool usesLocalCoords,
148                                           uint8_t coverage = 0xff) {
149        switch (edgeType) {
150            case GrClipEdgeType::kFillAA:
151                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
152                    return nullptr;
153                }
154                return sk_sp<GrGeometryProcessor>(
155                    new GrQuadEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillAA,
156                                     localMatrix, usesLocalCoords));
157            case GrClipEdgeType::kHairlineAA:
158                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
159                    return nullptr;
160                }
161                return sk_sp<GrGeometryProcessor>(
162                    new GrQuadEffect(color, viewMatrix, coverage,
163                                     GrClipEdgeType::kHairlineAA, localMatrix,
164                                     usesLocalCoords));
165            case GrClipEdgeType::kFillBW:
166                return sk_sp<GrGeometryProcessor>(
167                    new GrQuadEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillBW,
168                                     localMatrix, usesLocalCoords));
169            default:
170                return nullptr;
171        }
172    }
173
174    ~GrQuadEffect() override;
175
176    const char* name() const override { return "Quad"; }
177
178    inline const Attribute* inPosition() const { return fInPosition; }
179    inline const Attribute* inHairQuadEdge() const { return fInHairQuadEdge; }
180    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
181    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
182    inline GrClipEdgeType getEdgeType() const { return fEdgeType; }
183    GrColor color() const { return fColor; }
184    const SkMatrix& viewMatrix() const { return fViewMatrix; }
185    const SkMatrix& localMatrix() const { return fLocalMatrix; }
186    bool usesLocalCoords() const { return fUsesLocalCoords; }
187    uint8_t coverageScale() const { return fCoverageScale; }
188
189    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
190
191    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
192
193private:
194    GrQuadEffect(GrColor, const SkMatrix& viewMatrix, uint8_t coverage, GrClipEdgeType,
195                 const SkMatrix& localMatrix, bool usesLocalCoords);
196
197    GrColor             fColor;
198    SkMatrix            fViewMatrix;
199    SkMatrix            fLocalMatrix;
200    bool                fUsesLocalCoords;
201    uint8_t             fCoverageScale;
202    GrClipEdgeType fEdgeType;
203    const Attribute*    fInPosition;
204    const Attribute*    fInHairQuadEdge;
205
206    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
207
208    typedef GrGeometryProcessor INHERITED;
209};
210
211//////////////////////////////////////////////////////////////////////////////
212/**
213 * Shader is based off of "Resolution Independent Curve Rendering using
214 * Programmable Graphics Hardware" by Loop and Blinn.
215 * The output of this effect is a hairline edge for non rational cubics.
216 * Cubics are specified by implicit equation K^3 - LM.
217 * K, L, and M, are the first three values of the vertex attribute,
218 * the fourth value is not used. Distance is calculated using a
219 * first order approximation from the taylor series.
220 * Coverage for AA is max(0, 1-distance).
221 */
222class GrGLCubicEffect;
223
224class GrCubicEffect : public GrGeometryProcessor {
225public:
226    static sk_sp<GrGeometryProcessor> Make(GrColor color,
227                                           const SkMatrix& viewMatrix,
228                                           const SkMatrix& klm,
229                                           bool flipKL,
230                                           const GrClipEdgeType edgeType,
231                                           const GrCaps& caps) {
232        // Map KLM to something that operates in device space.
233        SkMatrix devKLM;
234        if (!viewMatrix.invert(&devKLM)) {
235            return nullptr;
236        }
237        devKLM.postConcat(klm);
238        if (flipKL) {
239            devKLM.postScale(-1, -1);
240        }
241
242        switch (edgeType) {
243            case GrClipEdgeType::kFillAA:
244                return sk_sp<GrGeometryProcessor>(
245                    new GrCubicEffect(color, viewMatrix, devKLM, GrClipEdgeType::kFillAA));
246            case GrClipEdgeType::kHairlineAA:
247                return sk_sp<GrGeometryProcessor>(
248                    new GrCubicEffect(color, viewMatrix, devKLM, GrClipEdgeType::kHairlineAA));
249            case GrClipEdgeType::kFillBW:
250                return sk_sp<GrGeometryProcessor>(
251                    new GrCubicEffect(color, viewMatrix, devKLM, GrClipEdgeType::kFillBW));
252            default:
253                return nullptr;
254        }
255    }
256
257    ~GrCubicEffect() override;
258
259    const char* name() const override { return "Cubic"; }
260
261    inline const Attribute* inPosition() const { return fInPosition; }
262    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
263    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
264    inline GrClipEdgeType getEdgeType() const { return fEdgeType; }
265    GrColor color() const { return fColor; }
266    bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
267    const SkMatrix& viewMatrix() const { return fViewMatrix; }
268    const SkMatrix& devKLMMatrix() const { return fDevKLMMatrix; }
269
270    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
271
272    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
273
274private:
275    GrCubicEffect(GrColor, const SkMatrix& viewMatrix, const SkMatrix& devKLMMatrix,
276                  GrClipEdgeType);
277
278    GrColor             fColor;
279    SkMatrix            fViewMatrix;
280    SkMatrix            fDevKLMMatrix;
281    GrClipEdgeType fEdgeType;
282    const Attribute*    fInPosition;
283
284    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
285
286    typedef GrGeometryProcessor INHERITED;
287};
288
289#endif
290