1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/effects/gradients/SkTwoPointRadialGradient.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,691 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2012 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + #include "SkTwoPointRadialGradient.h" 1.13 + 1.14 +/* Two-point radial gradients are specified by two circles, each with a center 1.15 + point and radius. The gradient can be considered to be a series of 1.16 + concentric circles, with the color interpolated from the start circle 1.17 + (at t=0) to the end circle (at t=1). 1.18 + 1.19 + For each point (x, y) in the span, we want to find the 1.20 + interpolated circle that intersects that point. The center 1.21 + of the desired circle (Cx, Cy) falls at some distance t 1.22 + along the line segment between the start point (Sx, Sy) and 1.23 + end point (Ex, Ey): 1.24 + 1.25 + Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) 1.26 + Cy = (1 - t) * Sy + t * Ey 1.27 + 1.28 + The radius of the desired circle (r) is also a linear interpolation t 1.29 + between the start and end radii (Sr and Er): 1.30 + 1.31 + r = (1 - t) * Sr + t * Er 1.32 + 1.33 + But 1.34 + 1.35 + (x - Cx)^2 + (y - Cy)^2 = r^2 1.36 + 1.37 + so 1.38 + 1.39 + (x - ((1 - t) * Sx + t * Ex))^2 1.40 + + (y - ((1 - t) * Sy + t * Ey))^2 1.41 + = ((1 - t) * Sr + t * Er)^2 1.42 + 1.43 + Solving for t yields 1.44 + 1.45 + [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 1.46 + + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t 1.47 + + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 1.48 + 1.49 + To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy 1.50 + 1.51 + [Dx^2 + Dy^2 - Dr^2)] * t^2 1.52 + + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t 1.53 + + [dx^2 + dy^2 - Sr^2] = 0 1.54 + 1.55 + A quadratic in t. The two roots of the quadratic reflect the two 1.56 + possible circles on which the point may fall. Solving for t yields 1.57 + the gradient value to use. 1.58 + 1.59 + If a<0, the start circle is entirely contained in the 1.60 + end circle, and one of the roots will be <0 or >1 (off the line 1.61 + segment). If a>0, the start circle falls at least partially 1.62 + outside the end circle (or vice versa), and the gradient 1.63 + defines a "tube" where a point may be on one circle (on the 1.64 + inside of the tube) or the other (outside of the tube). We choose 1.65 + one arbitrarily. 1.66 + 1.67 + In order to keep the math to within the limits of fixed point, 1.68 + we divide the entire quadratic by Dr^2, and replace 1.69 + (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving 1.70 + 1.71 + [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 1.72 + + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t 1.73 + + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 1.74 + 1.75 + (x' and y' are computed by appending the subtract and scale to the 1.76 + fDstToIndex matrix in the constructor). 1.77 + 1.78 + Since the 'A' component of the quadratic is independent of x' and y', it 1.79 + is precomputed in the constructor. Since the 'B' component is linear in 1.80 + x' and y', if x and y are linear in the span, 'B' can be computed 1.81 + incrementally with a simple delta (db below). If it is not (e.g., 1.82 + a perspective projection), it must be computed in the loop. 1.83 + 1.84 +*/ 1.85 + 1.86 +namespace { 1.87 + 1.88 +inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, 1.89 + SkScalar sr2d2, SkScalar foura, 1.90 + SkScalar oneOverTwoA, bool posRoot) { 1.91 + SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; 1.92 + if (0 == foura) { 1.93 + return SkScalarToFixed(SkScalarDiv(-c, b)); 1.94 + } 1.95 + 1.96 + SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); 1.97 + if (discrim < 0) { 1.98 + discrim = -discrim; 1.99 + } 1.100 + SkScalar rootDiscrim = SkScalarSqrt(discrim); 1.101 + SkScalar result; 1.102 + if (posRoot) { 1.103 + result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); 1.104 + } else { 1.105 + result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); 1.106 + } 1.107 + return SkScalarToFixed(result); 1.108 +} 1.109 + 1.110 +typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, 1.111 + SkScalar fy, SkScalar dy, 1.112 + SkScalar b, SkScalar db, 1.113 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 1.114 + SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 1.115 + int count); 1.116 + 1.117 +void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, 1.118 + SkScalar fy, SkScalar dy, 1.119 + SkScalar b, SkScalar db, 1.120 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 1.121 + SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 1.122 + int count) { 1.123 + for (; count > 0; --count) { 1.124 + SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 1.125 + fOneOverTwoA, posRoot); 1.126 + SkFixed index = SkClampMax(t, 0xFFFF); 1.127 + SkASSERT(index <= 0xFFFF); 1.128 + *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 1.129 + fx += dx; 1.130 + fy += dy; 1.131 + b += db; 1.132 + } 1.133 +} 1.134 +void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, 1.135 + SkScalar fy, SkScalar dy, 1.136 + SkScalar b, SkScalar db, 1.137 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 1.138 + SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 1.139 + int count) { 1.140 + for (; count > 0; --count) { 1.141 + SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 1.142 + fOneOverTwoA, posRoot); 1.143 + SkFixed index = mirror_tileproc(t); 1.144 + SkASSERT(index <= 0xFFFF); 1.145 + *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 1.146 + fx += dx; 1.147 + fy += dy; 1.148 + b += db; 1.149 + } 1.150 +} 1.151 + 1.152 +void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, 1.153 + SkScalar fy, SkScalar dy, 1.154 + SkScalar b, SkScalar db, 1.155 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 1.156 + SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 1.157 + int count) { 1.158 + for (; count > 0; --count) { 1.159 + SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 1.160 + fOneOverTwoA, posRoot); 1.161 + SkFixed index = repeat_tileproc(t); 1.162 + SkASSERT(index <= 0xFFFF); 1.163 + *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 1.164 + fx += dx; 1.165 + fy += dy; 1.166 + b += db; 1.167 + } 1.168 +} 1.169 +} 1.170 + 1.171 +///////////////////////////////////////////////////////////////////// 1.172 + 1.173 +SkTwoPointRadialGradient::SkTwoPointRadialGradient( 1.174 + const SkPoint& start, SkScalar startRadius, 1.175 + const SkPoint& end, SkScalar endRadius, 1.176 + const Descriptor& desc) 1.177 + : SkGradientShaderBase(desc), 1.178 + fCenter1(start), 1.179 + fCenter2(end), 1.180 + fRadius1(startRadius), 1.181 + fRadius2(endRadius) { 1.182 + init(); 1.183 +} 1.184 + 1.185 +SkShader::BitmapType SkTwoPointRadialGradient::asABitmap( 1.186 + SkBitmap* bitmap, 1.187 + SkMatrix* matrix, 1.188 + SkShader::TileMode* xy) const { 1.189 + if (bitmap) { 1.190 + this->getGradientTableBitmap(bitmap); 1.191 + } 1.192 + SkScalar diffL = 0; // just to avoid gcc warning 1.193 + if (matrix) { 1.194 + diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + 1.195 + SkScalarSquare(fDiff.fY)); 1.196 + } 1.197 + if (matrix) { 1.198 + if (diffL) { 1.199 + SkScalar invDiffL = SkScalarInvert(diffL); 1.200 + matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), 1.201 + SkScalarMul(invDiffL, fDiff.fX)); 1.202 + } else { 1.203 + matrix->reset(); 1.204 + } 1.205 + matrix->preConcat(fPtsToUnit); 1.206 + } 1.207 + if (xy) { 1.208 + xy[0] = fTileMode; 1.209 + xy[1] = kClamp_TileMode; 1.210 + } 1.211 + return kTwoPointRadial_BitmapType; 1.212 +} 1.213 + 1.214 +SkShader::GradientType SkTwoPointRadialGradient::asAGradient( 1.215 + SkShader::GradientInfo* info) const { 1.216 + if (info) { 1.217 + commonAsAGradient(info); 1.218 + info->fPoint[0] = fCenter1; 1.219 + info->fPoint[1] = fCenter2; 1.220 + info->fRadius[0] = fRadius1; 1.221 + info->fRadius[1] = fRadius2; 1.222 + } 1.223 + return kRadial2_GradientType; 1.224 +} 1.225 + 1.226 +void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, 1.227 + int count) { 1.228 + SkASSERT(count > 0); 1.229 + 1.230 + SkPMColor* SK_RESTRICT dstC = dstCParam; 1.231 + 1.232 + // Zero difference between radii: fill with transparent black. 1.233 + if (fDiffRadius == 0) { 1.234 + sk_bzero(dstC, count * sizeof(*dstC)); 1.235 + return; 1.236 + } 1.237 + SkMatrix::MapXYProc dstProc = fDstToIndexProc; 1.238 + TileProc proc = fTileProc; 1.239 + const SkPMColor* SK_RESTRICT cache = this->getCache32(); 1.240 + 1.241 + SkScalar foura = fA * 4; 1.242 + bool posRoot = fDiffRadius < 0; 1.243 + if (fDstToIndexClass != kPerspective_MatrixClass) { 1.244 + SkPoint srcPt; 1.245 + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 1.246 + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 1.247 + SkScalar dx, fx = srcPt.fX; 1.248 + SkScalar dy, fy = srcPt.fY; 1.249 + 1.250 + if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 1.251 + SkFixed fixedX, fixedY; 1.252 + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 1.253 + dx = SkFixedToScalar(fixedX); 1.254 + dy = SkFixedToScalar(fixedY); 1.255 + } else { 1.256 + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 1.257 + dx = fDstToIndex.getScaleX(); 1.258 + dy = fDstToIndex.getSkewY(); 1.259 + } 1.260 + SkScalar b = (SkScalarMul(fDiff.fX, fx) + 1.261 + SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 1.262 + SkScalar db = (SkScalarMul(fDiff.fX, dx) + 1.263 + SkScalarMul(fDiff.fY, dy)) * 2; 1.264 + 1.265 + TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; 1.266 + if (SkShader::kClamp_TileMode == fTileMode) { 1.267 + shadeProc = shadeSpan_twopoint_clamp; 1.268 + } else if (SkShader::kMirror_TileMode == fTileMode) { 1.269 + shadeProc = shadeSpan_twopoint_mirror; 1.270 + } else { 1.271 + SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 1.272 + } 1.273 + (*shadeProc)(fx, dx, fy, dy, b, db, 1.274 + fSr2D2, foura, fOneOverTwoA, posRoot, 1.275 + dstC, cache, count); 1.276 + } else { // perspective case 1.277 + SkScalar dstX = SkIntToScalar(x); 1.278 + SkScalar dstY = SkIntToScalar(y); 1.279 + for (; count > 0; --count) { 1.280 + SkPoint srcPt; 1.281 + dstProc(fDstToIndex, dstX, dstY, &srcPt); 1.282 + SkScalar fx = srcPt.fX; 1.283 + SkScalar fy = srcPt.fY; 1.284 + SkScalar b = (SkScalarMul(fDiff.fX, fx) + 1.285 + SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 1.286 + SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 1.287 + fOneOverTwoA, posRoot); 1.288 + SkFixed index = proc(t); 1.289 + SkASSERT(index <= 0xFFFF); 1.290 + *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; 1.291 + dstX += SK_Scalar1; 1.292 + } 1.293 + } 1.294 +} 1.295 + 1.296 +bool SkTwoPointRadialGradient::setContext( const SkBitmap& device, 1.297 + const SkPaint& paint, 1.298 + const SkMatrix& matrix){ 1.299 + // For now, we might have divided by zero, so detect that 1.300 + if (0 == fDiffRadius) { 1.301 + return false; 1.302 + } 1.303 + 1.304 + if (!this->INHERITED::setContext(device, paint, matrix)) { 1.305 + return false; 1.306 + } 1.307 + 1.308 + // we don't have a span16 proc 1.309 + fFlags &= ~kHasSpan16_Flag; 1.310 + return true; 1.311 +} 1.312 + 1.313 +#ifndef SK_IGNORE_TO_STRING 1.314 +void SkTwoPointRadialGradient::toString(SkString* str) const { 1.315 + str->append("SkTwoPointRadialGradient: ("); 1.316 + 1.317 + str->append("center1: ("); 1.318 + str->appendScalar(fCenter1.fX); 1.319 + str->append(", "); 1.320 + str->appendScalar(fCenter1.fY); 1.321 + str->append(") radius1: "); 1.322 + str->appendScalar(fRadius1); 1.323 + str->append(" "); 1.324 + 1.325 + str->append("center2: ("); 1.326 + str->appendScalar(fCenter2.fX); 1.327 + str->append(", "); 1.328 + str->appendScalar(fCenter2.fY); 1.329 + str->append(") radius2: "); 1.330 + str->appendScalar(fRadius2); 1.331 + str->append(" "); 1.332 + 1.333 + this->INHERITED::toString(str); 1.334 + 1.335 + str->append(")"); 1.336 +} 1.337 +#endif 1.338 + 1.339 +SkTwoPointRadialGradient::SkTwoPointRadialGradient( 1.340 + SkReadBuffer& buffer) 1.341 + : INHERITED(buffer), 1.342 + fCenter1(buffer.readPoint()), 1.343 + fCenter2(buffer.readPoint()), 1.344 + fRadius1(buffer.readScalar()), 1.345 + fRadius2(buffer.readScalar()) { 1.346 + init(); 1.347 +}; 1.348 + 1.349 +void SkTwoPointRadialGradient::flatten( 1.350 + SkWriteBuffer& buffer) const { 1.351 + this->INHERITED::flatten(buffer); 1.352 + buffer.writePoint(fCenter1); 1.353 + buffer.writePoint(fCenter2); 1.354 + buffer.writeScalar(fRadius1); 1.355 + buffer.writeScalar(fRadius2); 1.356 +} 1.357 + 1.358 +void SkTwoPointRadialGradient::init() { 1.359 + fDiff = fCenter1 - fCenter2; 1.360 + fDiffRadius = fRadius2 - fRadius1; 1.361 + // hack to avoid zero-divide for now 1.362 + SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; 1.363 + fDiff.fX = SkScalarMul(fDiff.fX, inv); 1.364 + fDiff.fY = SkScalarMul(fDiff.fY, inv); 1.365 + fStartRadius = SkScalarMul(fRadius1, inv); 1.366 + fSr2D2 = SkScalarSquare(fStartRadius); 1.367 + fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; 1.368 + fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; 1.369 + 1.370 + fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); 1.371 + fPtsToUnit.postScale(inv, inv); 1.372 +} 1.373 + 1.374 +///////////////////////////////////////////////////////////////////// 1.375 + 1.376 +#if SK_SUPPORT_GPU 1.377 + 1.378 +#include "GrTBackendEffectFactory.h" 1.379 + 1.380 +// For brevity 1.381 +typedef GrGLUniformManager::UniformHandle UniformHandle; 1.382 + 1.383 +class GrGLRadial2Gradient : public GrGLGradientEffect { 1.384 + 1.385 +public: 1.386 + 1.387 + GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); 1.388 + virtual ~GrGLRadial2Gradient() { } 1.389 + 1.390 + virtual void emitCode(GrGLShaderBuilder*, 1.391 + const GrDrawEffect&, 1.392 + EffectKey, 1.393 + const char* outputColor, 1.394 + const char* inputColor, 1.395 + const TransformedCoordsArray&, 1.396 + const TextureSamplerArray&) SK_OVERRIDE; 1.397 + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 1.398 + 1.399 + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 1.400 + 1.401 +protected: 1.402 + 1.403 + UniformHandle fParamUni; 1.404 + 1.405 + const char* fVSVaryingName; 1.406 + const char* fFSVaryingName; 1.407 + 1.408 + bool fIsDegenerate; 1.409 + 1.410 + // @{ 1.411 + /// Values last uploaded as uniforms 1.412 + 1.413 + SkScalar fCachedCenter; 1.414 + SkScalar fCachedRadius; 1.415 + bool fCachedPosRoot; 1.416 + 1.417 + // @} 1.418 + 1.419 +private: 1.420 + 1.421 + typedef GrGLGradientEffect INHERITED; 1.422 + 1.423 +}; 1.424 + 1.425 +///////////////////////////////////////////////////////////////////// 1.426 + 1.427 +class GrRadial2Gradient : public GrGradientEffect { 1.428 +public: 1.429 + static GrEffectRef* Create(GrContext* ctx, 1.430 + const SkTwoPointRadialGradient& shader, 1.431 + const SkMatrix& matrix, 1.432 + SkShader::TileMode tm) { 1.433 + AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm))); 1.434 + return CreateEffectRef(effect); 1.435 + } 1.436 + 1.437 + virtual ~GrRadial2Gradient() { } 1.438 + 1.439 + static const char* Name() { return "Two-Point Radial Gradient"; } 1.440 + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 1.441 + return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance(); 1.442 + } 1.443 + 1.444 + // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 1.445 + bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } 1.446 + SkScalar center() const { return fCenterX1; } 1.447 + SkScalar radius() const { return fRadius0; } 1.448 + bool isPosRoot() const { return SkToBool(fPosRoot); } 1.449 + 1.450 + typedef GrGLRadial2Gradient GLEffect; 1.451 + 1.452 +private: 1.453 + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 1.454 + const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase); 1.455 + return (INHERITED::onIsEqual(sBase) && 1.456 + this->fCenterX1 == s.fCenterX1 && 1.457 + this->fRadius0 == s.fRadius0 && 1.458 + this->fPosRoot == s.fPosRoot); 1.459 + } 1.460 + 1.461 + GrRadial2Gradient(GrContext* ctx, 1.462 + const SkTwoPointRadialGradient& shader, 1.463 + const SkMatrix& matrix, 1.464 + SkShader::TileMode tm) 1.465 + : INHERITED(ctx, shader, matrix, tm) 1.466 + , fCenterX1(shader.getCenterX1()) 1.467 + , fRadius0(shader.getStartRadius()) 1.468 + , fPosRoot(shader.getDiffRadius() < 0) { 1.469 + // We pass the linear part of the quadratic as a varying. 1.470 + // float b = 2.0 * (fCenterX1 * x - fRadius0 * z) 1.471 + fBTransform = this->getCoordTransform(); 1.472 + SkMatrix& bMatrix = *fBTransform.accessMatrix(); 1.473 + bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) - 1.474 + SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0])); 1.475 + bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) - 1.476 + SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1])); 1.477 + bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) - 1.478 + SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2])); 1.479 + this->addCoordTransform(&fBTransform); 1.480 + } 1.481 + 1.482 + GR_DECLARE_EFFECT_TEST; 1.483 + 1.484 + // @{ 1.485 + // Cache of values - these can change arbitrarily, EXCEPT 1.486 + // we shouldn't change between degenerate and non-degenerate?! 1.487 + 1.488 + GrCoordTransform fBTransform; 1.489 + SkScalar fCenterX1; 1.490 + SkScalar fRadius0; 1.491 + SkBool8 fPosRoot; 1.492 + 1.493 + // @} 1.494 + 1.495 + typedef GrGradientEffect INHERITED; 1.496 +}; 1.497 + 1.498 +///////////////////////////////////////////////////////////////////// 1.499 + 1.500 +GR_DEFINE_EFFECT_TEST(GrRadial2Gradient); 1.501 + 1.502 +GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random, 1.503 + GrContext* context, 1.504 + const GrDrawTargetCaps&, 1.505 + GrTexture**) { 1.506 + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 1.507 + SkScalar radius1 = random->nextUScalar1(); 1.508 + SkPoint center2; 1.509 + SkScalar radius2; 1.510 + do { 1.511 + center2.set(random->nextUScalar1(), random->nextUScalar1()); 1.512 + radius2 = random->nextUScalar1 (); 1.513 + // There is a bug in two point radial gradients with identical radii 1.514 + } while (radius1 == radius2); 1.515 + 1.516 + SkColor colors[kMaxRandomGradientColors]; 1.517 + SkScalar stopsArray[kMaxRandomGradientColors]; 1.518 + SkScalar* stops = stopsArray; 1.519 + SkShader::TileMode tm; 1.520 + int colorCount = RandomGradientParams(random, colors, &stops, &tm); 1.521 + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1, 1.522 + center2, radius2, 1.523 + colors, stops, colorCount, 1.524 + tm)); 1.525 + SkPaint paint; 1.526 + return shader->asNewEffect(context, paint); 1.527 +} 1.528 + 1.529 +///////////////////////////////////////////////////////////////////// 1.530 + 1.531 +GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory, 1.532 + const GrDrawEffect& drawEffect) 1.533 + : INHERITED(factory) 1.534 + , fVSVaryingName(NULL) 1.535 + , fFSVaryingName(NULL) 1.536 + , fCachedCenter(SK_ScalarMax) 1.537 + , fCachedRadius(-SK_ScalarMax) 1.538 + , fCachedPosRoot(0) { 1.539 + 1.540 + const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 1.541 + fIsDegenerate = data.isDegenerate(); 1.542 +} 1.543 + 1.544 +void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder, 1.545 + const GrDrawEffect& drawEffect, 1.546 + EffectKey key, 1.547 + const char* outputColor, 1.548 + const char* inputColor, 1.549 + const TransformedCoordsArray& coords, 1.550 + const TextureSamplerArray& samplers) { 1.551 + 1.552 + this->emitUniforms(builder, key); 1.553 + fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, 1.554 + kFloat_GrSLType, "Radial2FSParams", 6); 1.555 + 1.556 + SkString cName("c"); 1.557 + SkString ac4Name("ac4"); 1.558 + SkString rootName("root"); 1.559 + SkString t; 1.560 + SkString p0; 1.561 + SkString p1; 1.562 + SkString p2; 1.563 + SkString p3; 1.564 + SkString p4; 1.565 + SkString p5; 1.566 + builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 1.567 + builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 1.568 + builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); 1.569 + builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); 1.570 + builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); 1.571 + builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); 1.572 + 1.573 + // We interpolate the linear component in coords[1]. 1.574 + SkASSERT(coords[0].type() == coords[1].type()); 1.575 + const char* coords2D; 1.576 + SkString bVar; 1.577 + if (kVec3f_GrSLType == coords[0].type()) { 1.578 + builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", 1.579 + coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); 1.580 + coords2D = "interpolants.xy"; 1.581 + bVar = "interpolants.z"; 1.582 + } else { 1.583 + coords2D = coords[0].c_str(); 1.584 + bVar.printf("%s.x", coords[1].c_str()); 1.585 + } 1.586 + 1.587 + // c = (x^2)+(y^2) - params[4] 1.588 + builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 1.589 + cName.c_str(), coords2D, coords2D, p4.c_str()); 1.590 + 1.591 + // If we aren't degenerate, emit some extra code, and accept a slightly 1.592 + // more complex coord. 1.593 + if (!fIsDegenerate) { 1.594 + 1.595 + // ac4 = 4.0 * params[0] * c 1.596 + builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n", 1.597 + ac4Name.c_str(), p0.c_str(), 1.598 + cName.c_str()); 1.599 + 1.600 + // root = sqrt(b^2-4ac) 1.601 + // (abs to avoid exception due to fp precision) 1.602 + builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", 1.603 + rootName.c_str(), bVar.c_str(), bVar.c_str(), 1.604 + ac4Name.c_str()); 1.605 + 1.606 + // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] 1.607 + t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), 1.608 + rootName.c_str(), p1.c_str()); 1.609 + } else { 1.610 + // t is: -c/b 1.611 + t.printf("-%s / %s", cName.c_str(), bVar.c_str()); 1.612 + } 1.613 + 1.614 + this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); 1.615 +} 1.616 + 1.617 +void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, 1.618 + const GrDrawEffect& drawEffect) { 1.619 + INHERITED::setData(uman, drawEffect); 1.620 + const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>(); 1.621 + SkASSERT(data.isDegenerate() == fIsDegenerate); 1.622 + SkScalar centerX1 = data.center(); 1.623 + SkScalar radius0 = data.radius(); 1.624 + if (fCachedCenter != centerX1 || 1.625 + fCachedRadius != radius0 || 1.626 + fCachedPosRoot != data.isPosRoot()) { 1.627 + 1.628 + SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; 1.629 + 1.630 + // When we're in the degenerate (linear) case, the second 1.631 + // value will be INF but the program doesn't read it. (We 1.632 + // use the same 6 uniforms even though we don't need them 1.633 + // all in the linear case just to keep the code complexity 1.634 + // down). 1.635 + float values[6] = { 1.636 + SkScalarToFloat(a), 1.637 + 1 / (2.f * SkScalarToFloat(a)), 1.638 + SkScalarToFloat(centerX1), 1.639 + SkScalarToFloat(radius0), 1.640 + SkScalarToFloat(SkScalarMul(radius0, radius0)), 1.641 + data.isPosRoot() ? 1.f : -1.f 1.642 + }; 1.643 + 1.644 + uman.set1fv(fParamUni, 6, values); 1.645 + fCachedCenter = centerX1; 1.646 + fCachedRadius = radius0; 1.647 + fCachedPosRoot = data.isPosRoot(); 1.648 + } 1.649 +} 1.650 + 1.651 +GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect, 1.652 + const GrGLCaps&) { 1.653 + enum { 1.654 + kIsDegenerate = 1 << kBaseKeyBitCnt, 1.655 + }; 1.656 + 1.657 + EffectKey key = GenBaseGradientKey(drawEffect); 1.658 + if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) { 1.659 + key |= kIsDegenerate; 1.660 + } 1.661 + return key; 1.662 +} 1.663 + 1.664 +///////////////////////////////////////////////////////////////////// 1.665 + 1.666 +GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const { 1.667 + SkASSERT(NULL != context); 1.668 + // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis. 1.669 + SkMatrix matrix; 1.670 + if (!this->getLocalMatrix().invert(&matrix)) { 1.671 + return NULL; 1.672 + } 1.673 + matrix.postConcat(fPtsToUnit); 1.674 + 1.675 + SkScalar diffLen = fDiff.length(); 1.676 + if (0 != diffLen) { 1.677 + SkScalar invDiffLen = SkScalarInvert(diffLen); 1.678 + SkMatrix rot; 1.679 + rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), 1.680 + SkScalarMul(invDiffLen, fDiff.fX)); 1.681 + matrix.postConcat(rot); 1.682 + } 1.683 + 1.684 + return GrRadial2Gradient::Create(context, *this, matrix, fTileMode); 1.685 +} 1.686 + 1.687 +#else 1.688 + 1.689 +GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const { 1.690 + SkDEBUGFAIL("Should not call in GPU-less build"); 1.691 + return NULL; 1.692 +} 1.693 + 1.694 +#endif