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 "GrDrawTargetCaps.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 GrGeometryProcessor* Create(const GrPrimitiveEdgeType edgeType,
61                                       const GrDrawTargetCaps& caps) {
62        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gConicFillAA, GrConicEffect,
63                                            (kFillAA_GrProcessorEdgeType));
64        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gConicHairAA, GrConicEffect,
65                                            (kHairlineAA_GrProcessorEdgeType));
66        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gConicFillBW, GrConicEffect,
67                                            (kFillBW_GrProcessorEdgeType));
68        switch (edgeType) {
69            case kFillAA_GrProcessorEdgeType:
70                if (!caps.shaderDerivativeSupport()) {
71                    return NULL;
72                }
73                gConicFillAA->ref();
74                return gConicFillAA;
75            case kHairlineAA_GrProcessorEdgeType:
76                if (!caps.shaderDerivativeSupport()) {
77                    return NULL;
78                }
79                gConicHairAA->ref();
80                return gConicHairAA;
81            case kFillBW_GrProcessorEdgeType:
82                gConicFillBW->ref();
83                return gConicFillBW;
84            default:
85                return NULL;
86        }
87    }
88
89    virtual ~GrConicEffect();
90
91    static const char* Name() { return "Conic"; }
92
93    inline const GrShaderVar& inConicCoeffs() const { return fInConicCoeffs; }
94    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
95    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
96    inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
97
98    typedef GrGLConicEffect GLProcessor;
99
100    virtual void getConstantColorComponents(GrColor* color,
101                                            uint32_t* validFlags) const SK_OVERRIDE {
102        *validFlags = 0;
103    }
104
105    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
106
107private:
108    GrConicEffect(GrPrimitiveEdgeType);
109
110    virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
111
112    GrPrimitiveEdgeType   fEdgeType;
113    const GrShaderVar& fInConicCoeffs;
114
115    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
116
117    typedef GrGeometryProcessor INHERITED;
118};
119
120///////////////////////////////////////////////////////////////////////////////
121/**
122 * The output of this effect is a hairline edge for quadratics.
123 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
124 * two components of the vertex attribute. At the three control points that define
125 * the Quadratic, u, v have the values {0,0}, {1/2, 0}, and {1, 1} respectively.
126 * Coverage for AA is min(0, 1-distance). 3rd & 4th cimponent unused.
127 * Requires shader derivative instruction support.
128 */
129class GrGLQuadEffect;
130
131class GrQuadEffect : public GrGeometryProcessor {
132public:
133    static GrGeometryProcessor* Create(const GrPrimitiveEdgeType edgeType,
134                                       const GrDrawTargetCaps& caps) {
135        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gQuadFillAA, GrQuadEffect,
136                                            (kFillAA_GrProcessorEdgeType));
137        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gQuadHairAA, GrQuadEffect,
138                                            (kHairlineAA_GrProcessorEdgeType));
139        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gQuadFillBW, GrQuadEffect,
140                                            (kFillBW_GrProcessorEdgeType));
141        switch (edgeType) {
142            case kFillAA_GrProcessorEdgeType:
143                if (!caps.shaderDerivativeSupport()) {
144                    return NULL;
145                }
146                gQuadFillAA->ref();
147                return gQuadFillAA;
148            case kHairlineAA_GrProcessorEdgeType:
149                if (!caps.shaderDerivativeSupport()) {
150                    return NULL;
151                }
152                gQuadHairAA->ref();
153                return gQuadHairAA;
154            case kFillBW_GrProcessorEdgeType:
155                gQuadFillBW->ref();
156                return gQuadFillBW;
157            default:
158                return NULL;
159        }
160    }
161
162    virtual ~GrQuadEffect();
163
164    static const char* Name() { return "Quad"; }
165
166    inline const GrShaderVar& inHairQuadEdge() const { return fInHairQuadEdge; }
167    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
168    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
169    inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
170
171    typedef GrGLQuadEffect GLProcessor;
172
173    virtual void getConstantColorComponents(GrColor* color,
174                                            uint32_t* validFlags) const SK_OVERRIDE {
175        *validFlags = 0;
176    }
177
178    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
179
180private:
181    GrQuadEffect(GrPrimitiveEdgeType);
182
183    virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
184
185    GrPrimitiveEdgeType   fEdgeType;
186    const GrShaderVar& fInHairQuadEdge;
187
188    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
189
190    typedef GrGeometryProcessor INHERITED;
191};
192
193//////////////////////////////////////////////////////////////////////////////
194/**
195 * Shader is based off of "Resolution Independent Curve Rendering using
196 * Programmable Graphics Hardware" by Loop and Blinn.
197 * The output of this effect is a hairline edge for non rational cubics.
198 * Cubics are specified by implicit equation K^3 - LM.
199 * K, L, and M, are the first three values of the vertex attribute,
200 * the fourth value is not used. Distance is calculated using a
201 * first order approximation from the taylor series.
202 * Coverage for AA is max(0, 1-distance).
203 */
204class GrGLCubicEffect;
205
206class GrCubicEffect : public GrGeometryProcessor {
207public:
208    static GrGeometryProcessor* Create(const GrPrimitiveEdgeType edgeType,
209                                       const GrDrawTargetCaps& caps) {
210        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gCubicFillAA, GrCubicEffect,
211                                            (kFillAA_GrProcessorEdgeType));
212        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gCubicHairAA, GrCubicEffect,
213                                            (kHairlineAA_GrProcessorEdgeType));
214        GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gCubicFillBW, GrCubicEffect,
215                                            (kFillBW_GrProcessorEdgeType));
216        switch (edgeType) {
217            case kFillAA_GrProcessorEdgeType:
218                if (!caps.shaderDerivativeSupport()) {
219                    return NULL;
220                }
221                gCubicFillAA->ref();
222                return gCubicFillAA;
223            case kHairlineAA_GrProcessorEdgeType:
224                if (!caps.shaderDerivativeSupport()) {
225                    return NULL;
226                }
227                gCubicHairAA->ref();
228                return gCubicHairAA;
229            case kFillBW_GrProcessorEdgeType:
230                gCubicFillBW->ref();
231                return gCubicFillBW;
232            default:
233                return NULL;
234        }
235    }
236
237    virtual ~GrCubicEffect();
238
239    static const char* Name() { return "Cubic"; }
240
241    inline const GrShaderVar& inCubicCoeffs() const { return fInCubicCoeffs; }
242    inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); }
243    inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); }
244    inline GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
245
246    typedef GrGLCubicEffect GLProcessor;
247
248    virtual void getConstantColorComponents(GrColor* color,
249                                            uint32_t* validFlags) const SK_OVERRIDE {
250        *validFlags = 0;
251    }
252
253    virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE;
254
255private:
256    GrCubicEffect(GrPrimitiveEdgeType);
257
258    virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE;
259
260    GrPrimitiveEdgeType   fEdgeType;
261    const GrShaderVar& fInCubicCoeffs;
262
263    GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
264
265    typedef GrGeometryProcessor INHERITED;
266};
267
268#endif
269