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 */ 56 class GrGLConicEffect; 57 58 class GrConicEffect : public GrGeometryProcessor { 59 public: 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 name()94 const char* name() const override { return "Conic"; } 95 inPosition()96 inline const Attribute* inPosition() const { return fInPosition; } inConicCoeffs()97 inline const Attribute* inConicCoeffs() const { return fInConicCoeffs; } isAntiAliased()98 inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); } isFilled()99 inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); } getEdgeType()100 inline GrClipEdgeType getEdgeType() const { return fEdgeType; } color()101 GrColor color() const { return fColor; } viewMatrix()102 const SkMatrix& viewMatrix() const { return fViewMatrix; } localMatrix()103 const SkMatrix& localMatrix() const { return fLocalMatrix; } usesLocalCoords()104 bool usesLocalCoords() const { return fUsesLocalCoords; } coverageScale()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 111 private: 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 */ 138 class GrGLQuadEffect; 139 140 class GrQuadEffect : public GrGeometryProcessor { 141 public: 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 name()176 const char* name() const override { return "Quad"; } 177 inPosition()178 inline const Attribute* inPosition() const { return fInPosition; } inHairQuadEdge()179 inline const Attribute* inHairQuadEdge() const { return fInHairQuadEdge; } isAntiAliased()180 inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); } isFilled()181 inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); } getEdgeType()182 inline GrClipEdgeType getEdgeType() const { return fEdgeType; } color()183 GrColor color() const { return fColor; } viewMatrix()184 const SkMatrix& viewMatrix() const { return fViewMatrix; } localMatrix()185 const SkMatrix& localMatrix() const { return fLocalMatrix; } usesLocalCoords()186 bool usesLocalCoords() const { return fUsesLocalCoords; } coverageScale()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 193 private: 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 */ 222 class GrGLCubicEffect; 223 224 class GrCubicEffect : public GrGeometryProcessor { 225 public: Make(GrColor color,const SkMatrix & viewMatrix,const SkMatrix & klm,bool flipKL,const GrClipEdgeType edgeType,const GrCaps & caps)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 name()259 const char* name() const override { return "Cubic"; } 260 inPosition()261 inline const Attribute* inPosition() const { return fInPosition; } isAntiAliased()262 inline bool isAntiAliased() const { return GrProcessorEdgeTypeIsAA(fEdgeType); } isFilled()263 inline bool isFilled() const { return GrProcessorEdgeTypeIsFill(fEdgeType); } getEdgeType()264 inline GrClipEdgeType getEdgeType() const { return fEdgeType; } color()265 GrColor color() const { return fColor; } colorIgnored()266 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; } viewMatrix()267 const SkMatrix& viewMatrix() const { return fViewMatrix; } devKLMMatrix()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 274 private: 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