OLD | NEW |
(Empty) | |
| 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 #if SK_SUPPORT_GPU |
| 8 #include "SkTwoPointConicalGradient_gpu.h" |
| 9 #include "GrTBackendEffectFactory.h" |
| 10 |
| 11 #include "SkTwoPointConicalGradient.h" |
| 12 |
| 13 // For brevity |
| 14 typedef GrGLUniformManager::UniformHandle UniformHandle; |
| 15 |
| 16 class GrGL2PtConicalGradientEffect : public GrGLGradientEffect { |
| 17 public: |
| 18 |
| 19 GrGL2PtConicalGradientEffect(const GrBackendEffectFactory& factory, const Gr
DrawEffect&); |
| 20 virtual ~GrGL2PtConicalGradientEffect() { } |
| 21 |
| 22 virtual void emitCode(GrGLShaderBuilder*, |
| 23 const GrDrawEffect&, |
| 24 EffectKey, |
| 25 const char* outputColor, |
| 26 const char* inputColor, |
| 27 const TransformedCoordsArray&, |
| 28 const TextureSamplerArray&) SK_OVERRIDE; |
| 29 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVER
RIDE; |
| 30 |
| 31 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); |
| 32 |
| 33 protected: |
| 34 |
| 35 UniformHandle fParamUni; |
| 36 |
| 37 const char* fVSVaryingName; |
| 38 const char* fFSVaryingName; |
| 39 |
| 40 bool fIsDegenerate; |
| 41 |
| 42 // @{ |
| 43 /// Values last uploaded as uniforms |
| 44 |
| 45 SkScalar fCachedCenter; |
| 46 SkScalar fCachedRadius; |
| 47 SkScalar fCachedDiffRadius; |
| 48 |
| 49 // @} |
| 50 |
| 51 private: |
| 52 |
| 53 typedef GrGLGradientEffect INHERITED; |
| 54 |
| 55 }; |
| 56 |
| 57 const GrBackendEffectFactory& Gr2PtConicalGradientEffect::getFactory() const { |
| 58 return GrTBackendEffectFactory<Gr2PtConicalGradientEffect>::getInstance(); |
| 59 } |
| 60 |
| 61 Gr2PtConicalGradientEffect::Gr2PtConicalGradientEffect(GrContext* ctx, |
| 62 const SkTwoPointConical
Gradient& shader, |
| 63 const SkMatrix& matrix, |
| 64 SkShader::TileMode tm)
: |
| 65 INHERITED(ctx, shader, matrix, tm), |
| 66 fCenterX1(shader.getCenterX1()), |
| 67 fRadius0(shader.getStartRadius()), |
| 68 fDiffRadius(shader.getDiffRadius()) { |
| 69 // We pass the linear part of the quadratic as a varying. |
| 70 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) |
| 71 fBTransform = this->getCoordTransform(); |
| 72 SkMatrix& bMatrix = *fBTransform.accessMatrix(); |
| 73 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); |
| 74 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix:
:kMScaleX]) + |
| 75 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPe
rsp0])); |
| 76 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::
kMSkewX]) + |
| 77 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPer
sp1])); |
| 78 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix:
:kMTransX]) + |
| 79 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPe
rsp2])); |
| 80 this->addCoordTransform(&fBTransform); |
| 81 } |
| 82 |
| 83 GR_DEFINE_EFFECT_TEST(Gr2PtConicalGradientEffect); |
| 84 |
| 85 GrEffectRef* Gr2PtConicalGradientEffect::TestCreate(SkRandom* random, |
| 86 GrContext* context, |
| 87 const GrDrawTargetCaps&, |
| 88 GrTexture**) { |
| 89 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| 90 SkScalar radius1 = random->nextUScalar1(); |
| 91 SkPoint center2; |
| 92 SkScalar radius2; |
| 93 do { |
| 94 center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| 95 radius2 = random->nextUScalar1 (); |
| 96 // If the circles are identical the factory will give us an empty shader
. |
| 97 } while (radius1 == radius2 && center1 == center2); |
| 98 |
| 99 SkColor colors[kMaxRandomGradientColors]; |
| 100 SkScalar stopsArray[kMaxRandomGradientColors]; |
| 101 SkScalar* stops = stopsArray; |
| 102 SkShader::TileMode tm; |
| 103 int colorCount = RandomGradientParams(random, colors, &stops, &tm); |
| 104 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center
1, radius1, |
| 105 center
2, radius2, |
| 106 colors
, stops, colorCount, |
| 107 tm)); |
| 108 SkPaint paint; |
| 109 return shader->asNewEffect(context, paint); |
| 110 } |
| 111 |
| 112 |
| 113 ///////////////////////////////////////////////////////////////////// |
| 114 |
| 115 GrGL2PtConicalGradientEffect::GrGL2PtConicalGradientEffect(const GrBackendEffect
Factory& factory, |
| 116 const GrDrawEffect& drawEffect) |
| 117 : INHERITED(factory) |
| 118 , fVSVaryingName(NULL) |
| 119 , fFSVaryingName(NULL) |
| 120 , fCachedCenter(SK_ScalarMax) |
| 121 , fCachedRadius(-SK_ScalarMax) |
| 122 , fCachedDiffRadius(-SK_ScalarMax) { |
| 123 |
| 124 const Gr2PtConicalGradientEffect& data = drawEffect.castEffect<Gr2PtConicalG
radientEffect>(); |
| 125 fIsDegenerate = data.isDegenerate(); |
| 126 } |
| 127 |
| 128 void GrGL2PtConicalGradientEffect::emitCode(GrGLShaderBuilder* builder, |
| 129 const GrDrawEffect&, |
| 130 EffectKey key, |
| 131 const char* outputColor, |
| 132 const char* inputColor, |
| 133 const TransformedCoordsArray& coords, |
| 134 const TextureSamplerArray& samplers) { |
| 135 this->emitUniforms(builder, key); |
| 136 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility
, |
| 137 kFloat_GrSLType, "Conical2FSParams", 6)
; |
| 138 |
| 139 SkString cName("c"); |
| 140 SkString ac4Name("ac4"); |
| 141 SkString dName("d"); |
| 142 SkString qName("q"); |
| 143 SkString r0Name("r0"); |
| 144 SkString r1Name("r1"); |
| 145 SkString tName("t"); |
| 146 SkString p0; // 4a |
| 147 SkString p1; // 1/a |
| 148 SkString p2; // distance between centers |
| 149 SkString p3; // start radius |
| 150 SkString p4; // start radius squared |
| 151 SkString p5; // difference in radii (r1 - r0) |
| 152 |
| 153 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); |
| 154 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); |
| 155 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); |
| 156 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); |
| 157 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); |
| 158 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); |
| 159 |
| 160 // We interpolate the linear component in coords[1]. |
| 161 SkASSERT(coords[0].type() == coords[1].type()); |
| 162 const char* coords2D; |
| 163 SkString bVar; |
| 164 if (kVec3f_GrSLType == coords[0].type()) { |
| 165 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\
n", |
| 166 coords[0].c_str(), coords[1].c_str(), coords[0].c
_str()); |
| 167 coords2D = "interpolants.xy"; |
| 168 bVar = "interpolants.z"; |
| 169 } else { |
| 170 coords2D = coords[0].c_str(); |
| 171 bVar.printf("%s.x", coords[1].c_str()); |
| 172 } |
| 173 |
| 174 // output will default to transparent black (we simply won't write anything |
| 175 // else to it if invalid, instead of discarding or returning prematurely) |
| 176 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
| 177 |
| 178 // c = (x^2)+(y^2) - params[4] |
| 179 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", |
| 180 cName.c_str(), coords2D, coords2D, p4.c_str()); |
| 181 |
| 182 // Non-degenerate case (quadratic) |
| 183 if (!fIsDegenerate) { |
| 184 |
| 185 // ac4 = params[0] * c |
| 186 builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_
str(), |
| 187 cName.c_str()); |
| 188 |
| 189 // d = b^2 - ac4 |
| 190 builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(), |
| 191 bVar.c_str(), bVar.c_str(), ac4Name.c_str()); |
| 192 |
| 193 // only proceed if discriminant is >= 0 |
| 194 builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str()); |
| 195 |
| 196 // intermediate value we'll use to compute the roots |
| 197 // q = -0.5 * (b +/- sqrt(d)) |
| 198 builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1
.0)" |
| 199 " * sqrt(%s));\n", qName.c_str(), bVar.c_str(), |
| 200 bVar.c_str(), dName.c_str()); |
| 201 |
| 202 // compute both roots |
| 203 // r0 = q * params[1] |
| 204 builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(), |
| 205 qName.c_str(), p1.c_str()); |
| 206 // r1 = c / q |
| 207 builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(), |
| 208 cName.c_str(), qName.c_str()); |
| 209 |
| 210 // Note: If there are two roots that both generate radius(t) > 0, the |
| 211 // Canvas spec says to choose the larger t. |
| 212 |
| 213 // so we'll look at the larger one first: |
| 214 builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), |
| 215 r0Name.c_str(), r1Name.c_str()); |
| 216 |
| 217 // if r(t) > 0, then we're done; t will be our x coordinate |
| 218 builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), |
| 219 p5.c_str(), p3.c_str()); |
| 220 |
| 221 builder->fsCodeAppend("\t\t"); |
| 222 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); |
| 223 |
| 224 // otherwise, if r(t) for the larger root was <= 0, try the other root |
| 225 builder->fsCodeAppend("\t\t} else {\n"); |
| 226 builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), |
| 227 r0Name.c_str(), r1Name.c_str()); |
| 228 |
| 229 // if r(t) > 0 for the smaller root, then t will be our x coordinate |
| 230 builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", |
| 231 tName.c_str(), p5.c_str(), p3.c_str()); |
| 232 |
| 233 builder->fsCodeAppend("\t\t\t"); |
| 234 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); |
| 235 |
| 236 // end if (r(t) > 0) for smaller root |
| 237 builder->fsCodeAppend("\t\t\t}\n"); |
| 238 // end if (r(t) > 0), else, for larger root |
| 239 builder->fsCodeAppend("\t\t}\n"); |
| 240 // end if (discriminant >= 0) |
| 241 builder->fsCodeAppend("\t}\n"); |
| 242 } else { |
| 243 |
| 244 // linear case: t = -c/b |
| 245 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), |
| 246 cName.c_str(), bVar.c_str()); |
| 247 |
| 248 // if r(t) > 0, then t will be the x coordinate |
| 249 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), |
| 250 p5.c_str(), p3.c_str()); |
| 251 builder->fsCodeAppend("\t"); |
| 252 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, sa
mplers); |
| 253 builder->fsCodeAppend("\t}\n"); |
| 254 } |
| 255 } |
| 256 |
| 257 void GrGL2PtConicalGradientEffect::setData(const GrGLUniformManager& uman, |
| 258 const GrDrawEffect& drawEffect) { |
| 259 INHERITED::setData(uman, drawEffect); |
| 260 const Gr2PtConicalGradientEffect& data = drawEffect.castEffect<Gr2PtConicalG
radientEffect>(); |
| 261 SkASSERT(data.isDegenerate() == fIsDegenerate); |
| 262 SkScalar centerX1 = data.center(); |
| 263 SkScalar radius0 = data.radius(); |
| 264 SkScalar diffRadius = data.diffRadius(); |
| 265 |
| 266 if (fCachedCenter != centerX1 || |
| 267 fCachedRadius != radius0 || |
| 268 fCachedDiffRadius != diffRadius) { |
| 269 |
| 270 SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius; |
| 271 |
| 272 // When we're in the degenerate (linear) case, the second |
| 273 // value will be INF but the program doesn't read it. (We |
| 274 // use the same 6 uniforms even though we don't need them |
| 275 // all in the linear case just to keep the code complexity |
| 276 // down). |
| 277 float values[6] = { |
| 278 SkScalarToFloat(a * 4), |
| 279 1.f / (SkScalarToFloat(a)), |
| 280 SkScalarToFloat(centerX1), |
| 281 SkScalarToFloat(radius0), |
| 282 SkScalarToFloat(SkScalarMul(radius0, radius0)), |
| 283 SkScalarToFloat(diffRadius) |
| 284 }; |
| 285 |
| 286 uman.set1fv(fParamUni, 6, values); |
| 287 fCachedCenter = centerX1; |
| 288 fCachedRadius = radius0; |
| 289 fCachedDiffRadius = diffRadius; |
| 290 } |
| 291 } |
| 292 |
| 293 GrGLEffect::EffectKey GrGL2PtConicalGradientEffect::GenKey(const GrDrawEffect& d
rawEffect, |
| 294 const GrGLCaps&) { |
| 295 enum { |
| 296 kIsDegenerate = 1 << kBaseKeyBitCnt, |
| 297 }; |
| 298 |
| 299 EffectKey key = GenBaseGradientKey(drawEffect); |
| 300 if (drawEffect.castEffect<Gr2PtConicalGradientEffect>().isDegenerate()) { |
| 301 key |= kIsDegenerate; |
| 302 } |
| 303 return key; |
| 304 } |
| 305 #endif |
| 306 |
OLD | NEW |