rileya@google.com | 589708b | 2012-07-26 20:04:23 +0000 | [diff] [blame] | 1 | |
| 2 | /* |
| 3 | * Copyright 2012 Google Inc. |
| 4 | * |
| 5 | * Use of this source code is governed by a BSD-style license that can be |
| 6 | * found in the LICENSE file. |
| 7 | */ |
| 8 | |
| 9 | #include "SkTwoPointRadialGradient.h" |
| 10 | |
| 11 | /* Two-point radial gradients are specified by two circles, each with a center |
| 12 | point and radius. The gradient can be considered to be a series of |
| 13 | concentric circles, with the color interpolated from the start circle |
| 14 | (at t=0) to the end circle (at t=1). |
| 15 | |
| 16 | For each point (x, y) in the span, we want to find the |
| 17 | interpolated circle that intersects that point. The center |
| 18 | of the desired circle (Cx, Cy) falls at some distance t |
| 19 | along the line segment between the start point (Sx, Sy) and |
| 20 | end point (Ex, Ey): |
| 21 | |
| 22 | Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) |
| 23 | Cy = (1 - t) * Sy + t * Ey |
| 24 | |
| 25 | The radius of the desired circle (r) is also a linear interpolation t |
| 26 | between the start and end radii (Sr and Er): |
| 27 | |
| 28 | r = (1 - t) * Sr + t * Er |
| 29 | |
| 30 | But |
| 31 | |
| 32 | (x - Cx)^2 + (y - Cy)^2 = r^2 |
| 33 | |
| 34 | so |
| 35 | |
| 36 | (x - ((1 - t) * Sx + t * Ex))^2 |
| 37 | + (y - ((1 - t) * Sy + t * Ey))^2 |
| 38 | = ((1 - t) * Sr + t * Er)^2 |
| 39 | |
| 40 | Solving for t yields |
| 41 | |
| 42 | [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 |
| 43 | + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t |
| 44 | + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 |
| 45 | |
| 46 | To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy |
| 47 | |
| 48 | [Dx^2 + Dy^2 - Dr^2)] * t^2 |
| 49 | + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t |
| 50 | + [dx^2 + dy^2 - Sr^2] = 0 |
| 51 | |
| 52 | A quadratic in t. The two roots of the quadratic reflect the two |
| 53 | possible circles on which the point may fall. Solving for t yields |
| 54 | the gradient value to use. |
| 55 | |
| 56 | If a<0, the start circle is entirely contained in the |
| 57 | end circle, and one of the roots will be <0 or >1 (off the line |
| 58 | segment). If a>0, the start circle falls at least partially |
| 59 | outside the end circle (or vice versa), and the gradient |
| 60 | defines a "tube" where a point may be on one circle (on the |
| 61 | inside of the tube) or the other (outside of the tube). We choose |
| 62 | one arbitrarily. |
| 63 | |
| 64 | In order to keep the math to within the limits of fixed point, |
| 65 | we divide the entire quadratic by Dr^2, and replace |
| 66 | (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving |
| 67 | |
| 68 | [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 |
| 69 | + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t |
| 70 | + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 |
| 71 | |
| 72 | (x' and y' are computed by appending the subtract and scale to the |
| 73 | fDstToIndex matrix in the constructor). |
| 74 | |
| 75 | Since the 'A' component of the quadratic is independent of x' and y', it |
| 76 | is precomputed in the constructor. Since the 'B' component is linear in |
| 77 | x' and y', if x and y are linear in the span, 'B' can be computed |
| 78 | incrementally with a simple delta (db below). If it is not (e.g., |
| 79 | a perspective projection), it must be computed in the loop. |
| 80 | |
| 81 | */ |
| 82 | |
| 83 | namespace { |
| 84 | |
| 85 | inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, |
| 86 | SkScalar sr2d2, SkScalar foura, |
| 87 | SkScalar oneOverTwoA, bool posRoot) { |
| 88 | SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; |
| 89 | if (0 == foura) { |
| 90 | return SkScalarToFixed(SkScalarDiv(-c, b)); |
| 91 | } |
| 92 | |
| 93 | SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); |
| 94 | if (discrim < 0) { |
| 95 | discrim = -discrim; |
| 96 | } |
| 97 | SkScalar rootDiscrim = SkScalarSqrt(discrim); |
| 98 | SkScalar result; |
| 99 | if (posRoot) { |
| 100 | result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); |
| 101 | } else { |
| 102 | result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); |
| 103 | } |
| 104 | return SkScalarToFixed(result); |
| 105 | } |
| 106 | |
| 107 | typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, |
| 108 | SkScalar fy, SkScalar dy, |
| 109 | SkScalar b, SkScalar db, |
| 110 | SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, |
| 111 | SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, |
| 112 | int count); |
| 113 | |
| 114 | void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, |
| 115 | SkScalar fy, SkScalar dy, |
| 116 | SkScalar b, SkScalar db, |
| 117 | SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, |
| 118 | SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, |
| 119 | int count) { |
| 120 | for (; count > 0; --count) { |
| 121 | SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, |
| 122 | fOneOverTwoA, posRoot); |
| 123 | SkFixed index = SkClampMax(t, 0xFFFF); |
| 124 | SkASSERT(index <= 0xFFFF); |
| 125 | *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; |
| 126 | fx += dx; |
| 127 | fy += dy; |
| 128 | b += db; |
| 129 | } |
| 130 | } |
| 131 | void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, |
| 132 | SkScalar fy, SkScalar dy, |
| 133 | SkScalar b, SkScalar db, |
| 134 | SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, |
| 135 | SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, |
| 136 | int count) { |
| 137 | for (; count > 0; --count) { |
| 138 | SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, |
| 139 | fOneOverTwoA, posRoot); |
| 140 | SkFixed index = mirror_tileproc(t); |
| 141 | SkASSERT(index <= 0xFFFF); |
| 142 | *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; |
| 143 | fx += dx; |
| 144 | fy += dy; |
| 145 | b += db; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, |
| 150 | SkScalar fy, SkScalar dy, |
| 151 | SkScalar b, SkScalar db, |
| 152 | SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, |
| 153 | SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, |
| 154 | int count) { |
| 155 | for (; count > 0; --count) { |
| 156 | SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, |
| 157 | fOneOverTwoA, posRoot); |
| 158 | SkFixed index = repeat_tileproc(t); |
| 159 | SkASSERT(index <= 0xFFFF); |
| 160 | *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; |
| 161 | fx += dx; |
| 162 | fy += dy; |
| 163 | b += db; |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | SkTwoPointRadialGradient::SkTwoPointRadialGradient( |
| 169 | const SkPoint& start, SkScalar startRadius, |
| 170 | const SkPoint& end, SkScalar endRadius, |
| 171 | const SkColor colors[], const SkScalar pos[], |
| 172 | int colorCount, SkShader::TileMode mode, |
| 173 | SkUnitMapper* mapper) |
| 174 | : SkGradientShaderBase(colors, pos, colorCount, mode, mapper), |
| 175 | fCenter1(start), |
| 176 | fCenter2(end), |
| 177 | fRadius1(startRadius), |
| 178 | fRadius2(endRadius) { |
| 179 | init(); |
| 180 | } |
| 181 | |
| 182 | SkShader::BitmapType SkTwoPointRadialGradient::asABitmap( |
| 183 | SkBitmap* bitmap, |
| 184 | SkMatrix* matrix, |
| 185 | SkShader::TileMode* xy) const { |
| 186 | if (bitmap) { |
rileya@google.com | 1c6d64b | 2012-07-27 15:49:05 +0000 | [diff] [blame^] | 187 | this->getGradientTableBitmap(bitmap); |
rileya@google.com | 589708b | 2012-07-26 20:04:23 +0000 | [diff] [blame] | 188 | } |
| 189 | SkScalar diffL = 0; // just to avoid gcc warning |
| 190 | if (matrix) { |
| 191 | diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + |
| 192 | SkScalarSquare(fDiff.fY)); |
| 193 | } |
| 194 | if (matrix) { |
| 195 | if (diffL) { |
| 196 | SkScalar invDiffL = SkScalarInvert(diffL); |
| 197 | matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), |
| 198 | SkScalarMul(invDiffL, fDiff.fX)); |
| 199 | } else { |
| 200 | matrix->reset(); |
| 201 | } |
| 202 | matrix->preConcat(fPtsToUnit); |
| 203 | } |
| 204 | if (xy) { |
| 205 | xy[0] = fTileMode; |
| 206 | xy[1] = kClamp_TileMode; |
| 207 | } |
| 208 | return kTwoPointRadial_BitmapType; |
| 209 | } |
| 210 | |
| 211 | SkShader::GradientType SkTwoPointRadialGradient::asAGradient( |
| 212 | SkShader::GradientInfo* info) const { |
| 213 | if (info) { |
| 214 | commonAsAGradient(info); |
| 215 | info->fPoint[0] = fCenter1; |
| 216 | info->fPoint[1] = fCenter2; |
| 217 | info->fRadius[0] = fRadius1; |
| 218 | info->fRadius[1] = fRadius2; |
| 219 | } |
| 220 | return kRadial2_GradientType; |
| 221 | } |
| 222 | |
| 223 | GrCustomStage* SkTwoPointRadialGradient::asNewCustomStage( |
| 224 | GrContext* context, GrSamplerState* sampler) const { |
| 225 | SkASSERT(NULL != context && NULL != sampler); |
| 226 | SkScalar diffLen = fDiff.length(); |
| 227 | if (0 != diffLen) { |
| 228 | SkScalar invDiffLen = SkScalarInvert(diffLen); |
| 229 | sampler->matrix()->setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), |
| 230 | SkScalarMul(invDiffLen, fDiff.fX)); |
| 231 | } else { |
| 232 | sampler->matrix()->reset(); |
| 233 | } |
| 234 | sampler->matrix()->preConcat(fPtsToUnit); |
| 235 | sampler->textureParams()->setTileModeX(fTileMode); |
| 236 | sampler->textureParams()->setTileModeY(kClamp_TileMode); |
| 237 | sampler->textureParams()->setBilerp(true); |
rileya@google.com | 1c6d64b | 2012-07-27 15:49:05 +0000 | [diff] [blame^] | 238 | return SkNEW_ARGS(GrRadial2Gradient, (context, *this, sampler)); |
rileya@google.com | 589708b | 2012-07-26 20:04:23 +0000 | [diff] [blame] | 239 | } |
| 240 | |
| 241 | void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, |
| 242 | int count) { |
| 243 | SkASSERT(count > 0); |
| 244 | |
| 245 | SkPMColor* SK_RESTRICT dstC = dstCParam; |
| 246 | |
| 247 | // Zero difference between radii: fill with transparent black. |
| 248 | if (fDiffRadius == 0) { |
| 249 | sk_bzero(dstC, count * sizeof(*dstC)); |
| 250 | return; |
| 251 | } |
| 252 | SkMatrix::MapXYProc dstProc = fDstToIndexProc; |
| 253 | TileProc proc = fTileProc; |
| 254 | const SkPMColor* SK_RESTRICT cache = this->getCache32(); |
| 255 | |
| 256 | SkScalar foura = fA * 4; |
| 257 | bool posRoot = fDiffRadius < 0; |
| 258 | if (fDstToIndexClass != kPerspective_MatrixClass) { |
| 259 | SkPoint srcPt; |
| 260 | dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, |
| 261 | SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
| 262 | SkScalar dx, fx = srcPt.fX; |
| 263 | SkScalar dy, fy = srcPt.fY; |
| 264 | |
| 265 | if (fDstToIndexClass == kFixedStepInX_MatrixClass) { |
| 266 | SkFixed fixedX, fixedY; |
| 267 | (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); |
| 268 | dx = SkFixedToScalar(fixedX); |
| 269 | dy = SkFixedToScalar(fixedY); |
| 270 | } else { |
| 271 | SkASSERT(fDstToIndexClass == kLinear_MatrixClass); |
| 272 | dx = fDstToIndex.getScaleX(); |
| 273 | dy = fDstToIndex.getSkewY(); |
| 274 | } |
| 275 | SkScalar b = (SkScalarMul(fDiff.fX, fx) + |
| 276 | SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; |
| 277 | SkScalar db = (SkScalarMul(fDiff.fX, dx) + |
| 278 | SkScalarMul(fDiff.fY, dy)) * 2; |
| 279 | |
| 280 | TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; |
| 281 | if (SkShader::kClamp_TileMode == fTileMode) { |
| 282 | shadeProc = shadeSpan_twopoint_clamp; |
| 283 | } else if (SkShader::kMirror_TileMode == fTileMode) { |
| 284 | shadeProc = shadeSpan_twopoint_mirror; |
| 285 | } else { |
| 286 | SkASSERT(SkShader::kRepeat_TileMode == fTileMode); |
| 287 | } |
| 288 | (*shadeProc)(fx, dx, fy, dy, b, db, |
| 289 | fSr2D2, foura, fOneOverTwoA, posRoot, |
| 290 | dstC, cache, count); |
| 291 | } else { // perspective case |
| 292 | SkScalar dstX = SkIntToScalar(x); |
| 293 | SkScalar dstY = SkIntToScalar(y); |
| 294 | for (; count > 0; --count) { |
| 295 | SkPoint srcPt; |
| 296 | dstProc(fDstToIndex, dstX, dstY, &srcPt); |
| 297 | SkScalar fx = srcPt.fX; |
| 298 | SkScalar fy = srcPt.fY; |
| 299 | SkScalar b = (SkScalarMul(fDiff.fX, fx) + |
| 300 | SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; |
| 301 | SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, |
| 302 | fOneOverTwoA, posRoot); |
| 303 | SkFixed index = proc(t); |
| 304 | SkASSERT(index <= 0xFFFF); |
| 305 | *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; |
| 306 | dstX += SK_Scalar1; |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | bool SkTwoPointRadialGradient::setContext( |
| 312 | const SkBitmap& device, |
| 313 | const SkPaint& paint, |
| 314 | const SkMatrix& matrix){ |
| 315 | if (!this->INHERITED::setContext(device, paint, matrix)) { |
| 316 | return false; |
| 317 | } |
| 318 | |
| 319 | // For now, we might have divided by zero, so detect that |
| 320 | if (0 == fDiffRadius) { |
| 321 | return false; |
| 322 | } |
| 323 | |
| 324 | // we don't have a span16 proc |
| 325 | fFlags &= ~kHasSpan16_Flag; |
| 326 | return true; |
| 327 | } |
| 328 | |
| 329 | SkTwoPointRadialGradient::SkTwoPointRadialGradient( |
| 330 | SkFlattenableReadBuffer& buffer) |
| 331 | : INHERITED(buffer), |
| 332 | fCenter1(buffer.readPoint()), |
| 333 | fCenter2(buffer.readPoint()), |
| 334 | fRadius1(buffer.readScalar()), |
| 335 | fRadius2(buffer.readScalar()) { |
| 336 | init(); |
| 337 | }; |
| 338 | |
| 339 | void SkTwoPointRadialGradient::flatten( |
| 340 | SkFlattenableWriteBuffer& buffer) const { |
| 341 | this->INHERITED::flatten(buffer); |
| 342 | buffer.writePoint(fCenter1); |
| 343 | buffer.writePoint(fCenter2); |
| 344 | buffer.writeScalar(fRadius1); |
| 345 | buffer.writeScalar(fRadius2); |
| 346 | } |
| 347 | |
| 348 | void SkTwoPointRadialGradient::init() { |
| 349 | fDiff = fCenter1 - fCenter2; |
| 350 | fDiffRadius = fRadius2 - fRadius1; |
| 351 | // hack to avoid zero-divide for now |
| 352 | SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; |
| 353 | fDiff.fX = SkScalarMul(fDiff.fX, inv); |
| 354 | fDiff.fY = SkScalarMul(fDiff.fY, inv); |
| 355 | fStartRadius = SkScalarMul(fRadius1, inv); |
| 356 | fSr2D2 = SkScalarSquare(fStartRadius); |
| 357 | fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; |
| 358 | fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; |
| 359 | |
| 360 | fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); |
| 361 | fPtsToUnit.postScale(inv, inv); |
| 362 | } |
| 363 | |
rileya@google.com | d7cc651 | 2012-07-27 14:00:39 +0000 | [diff] [blame] | 364 | ///////////////////////////////////////////////////////////////////// |
| 365 | |
| 366 | // For brevity |
| 367 | typedef GrGLUniformManager::UniformHandle UniformHandle; |
| 368 | static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; |
| 369 | |
| 370 | class GrGLRadial2Gradient : public GrGLGradientStage { |
| 371 | |
| 372 | public: |
| 373 | |
| 374 | GrGLRadial2Gradient(const GrProgramStageFactory& factory, |
| 375 | const GrCustomStage&); |
| 376 | virtual ~GrGLRadial2Gradient() { } |
| 377 | |
| 378 | virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE; |
| 379 | virtual void emitVS(GrGLShaderBuilder* builder, |
| 380 | const char* vertexCoords) SK_OVERRIDE; |
| 381 | virtual void emitFS(GrGLShaderBuilder* builder, |
| 382 | const char* outputColor, |
| 383 | const char* inputColor, |
| 384 | const char* samplerName) SK_OVERRIDE; |
| 385 | virtual void setData(const GrGLUniformManager&, |
| 386 | const GrCustomStage&, |
| 387 | const GrRenderTarget*, |
| 388 | int stageNum) SK_OVERRIDE; |
| 389 | |
| 390 | static StageKey GenKey(const GrCustomStage& s) { |
| 391 | return (static_cast<const GrRadial2Gradient&>(s).isDegenerate()); |
| 392 | } |
| 393 | |
| 394 | protected: |
| 395 | |
| 396 | UniformHandle fVSParamUni; |
| 397 | UniformHandle fFSParamUni; |
| 398 | |
| 399 | const char* fVSVaryingName; |
| 400 | const char* fFSVaryingName; |
| 401 | |
| 402 | bool fIsDegenerate; |
| 403 | |
| 404 | // @{ |
| 405 | /// Values last uploaded as uniforms |
| 406 | |
| 407 | GrScalar fCachedCenter; |
| 408 | GrScalar fCachedRadius; |
| 409 | bool fCachedPosRoot; |
| 410 | |
| 411 | // @} |
| 412 | |
| 413 | private: |
| 414 | |
| 415 | typedef GrGLGradientStage INHERITED; |
| 416 | |
| 417 | }; |
| 418 | |
| 419 | GrGLRadial2Gradient::GrGLRadial2Gradient( |
| 420 | const GrProgramStageFactory& factory, |
| 421 | const GrCustomStage& baseData) |
| 422 | : INHERITED(factory) |
| 423 | , fVSParamUni(kInvalidUniformHandle) |
| 424 | , fFSParamUni(kInvalidUniformHandle) |
| 425 | , fVSVaryingName(NULL) |
| 426 | , fFSVaryingName(NULL) |
| 427 | , fCachedCenter(GR_ScalarMax) |
| 428 | , fCachedRadius(-GR_ScalarMax) |
| 429 | , fCachedPosRoot(0) { |
| 430 | |
| 431 | const GrRadial2Gradient& data = |
| 432 | static_cast<const GrRadial2Gradient&>(baseData); |
| 433 | fIsDegenerate = data.isDegenerate(); |
| 434 | } |
| 435 | |
| 436 | void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) { |
| 437 | // 2 copies of uniform array, 1 for each of vertex & fragment shader, |
| 438 | // to work around Xoom bug. Doesn't seem to cause performance decrease |
| 439 | // in test apps, but need to keep an eye on it. |
| 440 | fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType, |
| 441 | kFloat_GrSLType, "Radial2VSParams", 6); |
| 442 | fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, |
| 443 | kFloat_GrSLType, "Radial2FSParams", 6); |
| 444 | |
| 445 | // For radial gradients without perspective we can pass the linear |
| 446 | // part of the quadratic as a varying. |
| 447 | if (builder->fVaryingDims == builder->fCoordDims) { |
| 448 | builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", |
| 449 | &fVSVaryingName, &fFSVaryingName); |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* builder, |
| 454 | const char* vertexCoords) { |
| 455 | SkString* code = &builder->fVSCode; |
| 456 | SkString p2; |
| 457 | SkString p3; |
| 458 | builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2); |
| 459 | builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3); |
| 460 | |
| 461 | // For radial gradients without perspective we can pass the linear |
| 462 | // part of the quadratic as a varying. |
| 463 | if (builder->fVaryingDims == builder->fCoordDims) { |
| 464 | // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3]) |
| 465 | code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n", |
| 466 | fVSVaryingName, p2.c_str(), |
| 467 | vertexCoords, p3.c_str()); |
| 468 | } |
| 469 | } |
| 470 | |
| 471 | void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* builder, |
| 472 | const char* outputColor, |
| 473 | const char* inputColor, |
| 474 | const char* samplerName) { |
| 475 | SkString* code = &builder->fFSCode; |
| 476 | SkString cName("c"); |
| 477 | SkString ac4Name("ac4"); |
| 478 | SkString rootName("root"); |
| 479 | SkString t; |
| 480 | SkString p0; |
| 481 | SkString p1; |
| 482 | SkString p2; |
| 483 | SkString p3; |
| 484 | SkString p4; |
| 485 | SkString p5; |
| 486 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0); |
| 487 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1); |
| 488 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2); |
| 489 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3); |
| 490 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4); |
| 491 | builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5); |
| 492 | |
| 493 | // If we we're able to interpolate the linear component, |
| 494 | // bVar is the varying; otherwise compute it |
| 495 | SkString bVar; |
| 496 | if (builder->fCoordDims == builder->fVaryingDims) { |
| 497 | bVar = fFSVaryingName; |
| 498 | GrAssert(2 == builder->fVaryingDims); |
| 499 | } else { |
| 500 | GrAssert(3 == builder->fVaryingDims); |
| 501 | bVar = "b"; |
| 502 | //bVar.appendS32(stageNum); |
| 503 | code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n", |
| 504 | bVar.c_str(), p2.c_str(), |
| 505 | builder->fSampleCoords.c_str(), p3.c_str()); |
| 506 | } |
| 507 | |
| 508 | // c = (x^2)+(y^2) - params[4] |
| 509 | code->appendf("\tfloat %s = dot(%s, %s) - %s;\n", |
| 510 | cName.c_str(), builder->fSampleCoords.c_str(), |
| 511 | builder->fSampleCoords.c_str(), |
| 512 | p4.c_str()); |
| 513 | |
| 514 | // If we aren't degenerate, emit some extra code, and accept a slightly |
| 515 | // more complex coord. |
| 516 | if (!fIsDegenerate) { |
| 517 | |
| 518 | // ac4 = 4.0 * params[0] * c |
| 519 | code->appendf("\tfloat %s = %s * 4.0 * %s;\n", |
| 520 | ac4Name.c_str(), p0.c_str(), |
| 521 | cName.c_str()); |
| 522 | |
| 523 | // root = sqrt(b^2-4ac) |
| 524 | // (abs to avoid exception due to fp precision) |
| 525 | code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", |
| 526 | rootName.c_str(), bVar.c_str(), bVar.c_str(), |
| 527 | ac4Name.c_str()); |
| 528 | |
| 529 | // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] |
| 530 | t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), |
| 531 | rootName.c_str(), p1.c_str()); |
| 532 | } else { |
| 533 | // t is: -c/b |
| 534 | t.printf("-%s / %s", cName.c_str(), bVar.c_str()); |
| 535 | } |
| 536 | |
| 537 | this->emitColorLookup(builder, t.c_str(), outputColor, samplerName); |
| 538 | } |
| 539 | |
| 540 | void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, |
| 541 | const GrCustomStage& baseData, |
| 542 | const GrRenderTarget*, |
| 543 | int stageNum) { |
| 544 | const GrRadial2Gradient& data = |
| 545 | static_cast<const GrRadial2Gradient&>(baseData); |
| 546 | GrAssert(data.isDegenerate() == fIsDegenerate); |
| 547 | GrScalar centerX1 = data.center(); |
| 548 | GrScalar radius0 = data.radius(); |
| 549 | if (fCachedCenter != centerX1 || |
| 550 | fCachedRadius != radius0 || |
| 551 | fCachedPosRoot != data.isPosRoot()) { |
| 552 | |
| 553 | GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1; |
| 554 | |
| 555 | // When we're in the degenerate (linear) case, the second |
| 556 | // value will be INF but the program doesn't read it. (We |
| 557 | // use the same 6 uniforms even though we don't need them |
| 558 | // all in the linear case just to keep the code complexity |
| 559 | // down). |
| 560 | float values[6] = { |
| 561 | GrScalarToFloat(a), |
| 562 | 1 / (2.f * GrScalarToFloat(a)), |
| 563 | GrScalarToFloat(centerX1), |
| 564 | GrScalarToFloat(radius0), |
| 565 | GrScalarToFloat(GrMul(radius0, radius0)), |
| 566 | data.isPosRoot() ? 1.f : -1.f |
| 567 | }; |
| 568 | |
| 569 | uman.set1fv(fVSParamUni, 0, 6, values); |
| 570 | uman.set1fv(fFSParamUni, 0, 6, values); |
| 571 | fCachedCenter = centerX1; |
| 572 | fCachedRadius = radius0; |
| 573 | fCachedPosRoot = data.isPosRoot(); |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | |
| 578 | ///////////////////////////////////////////////////////////////////// |
| 579 | |
| 580 | GrRadial2Gradient::GrRadial2Gradient(GrTexture* texture, |
| 581 | GrScalar center, |
| 582 | GrScalar radius, |
| 583 | bool posRoot) |
| 584 | : INHERITED(texture) |
| 585 | , fCenterX1 (center) |
| 586 | , fRadius0 (radius) |
| 587 | , fPosRoot (posRoot) { |
| 588 | |
| 589 | } |
| 590 | |
| 591 | GrRadial2Gradient::GrRadial2Gradient(GrContext* ctx, |
rileya@google.com | 1c6d64b | 2012-07-27 15:49:05 +0000 | [diff] [blame^] | 592 | const SkTwoPointRadialGradient& shader, |
| 593 | GrSamplerState* sampler) |
| 594 | : INHERITED(ctx, shader, sampler) |
| 595 | , fCenterX1(shader.getCenterX1()) |
| 596 | , fRadius0(shader.getStartRadius()) |
| 597 | , fPosRoot(shader.getDiffRadius() < 0) { |
rileya@google.com | d7cc651 | 2012-07-27 14:00:39 +0000 | [diff] [blame] | 598 | } |
| 599 | |
rileya@google.com | 1c6d64b | 2012-07-27 15:49:05 +0000 | [diff] [blame^] | 600 | |
rileya@google.com | d7cc651 | 2012-07-27 14:00:39 +0000 | [diff] [blame] | 601 | GrRadial2Gradient::~GrRadial2Gradient() { |
| 602 | |
| 603 | } |
| 604 | |
| 605 | |
| 606 | const GrProgramStageFactory& GrRadial2Gradient::getFactory() const { |
| 607 | return GrTProgramStageFactory<GrRadial2Gradient>::getInstance(); |
| 608 | } |
| 609 | |
| 610 | bool GrRadial2Gradient::isEqual(const GrCustomStage& sBase) const { |
| 611 | const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase); |
| 612 | return (INHERITED::isEqual(sBase) && |
| 613 | this->fCenterX1 == s.fCenterX1 && |
| 614 | this->fRadius0 == s.fRadius0 && |
| 615 | this->fPosRoot == s.fPosRoot); |
| 616 | } |
| 617 | |