1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/effects/gradients/SkTwoPointConicalGradient.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,737 @@ 1.4 +/* 1.5 + * Copyright 2012 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkTwoPointConicalGradient.h" 1.12 + 1.13 +static int valid_divide(float numer, float denom, float* ratio) { 1.14 + SkASSERT(ratio); 1.15 + if (0 == denom) { 1.16 + return 0; 1.17 + } 1.18 + *ratio = numer / denom; 1.19 + return 1; 1.20 +} 1.21 + 1.22 +// Return the number of distinct real roots, and write them into roots[] in 1.23 +// ascending order 1.24 +static int find_quad_roots(float A, float B, float C, float roots[2]) { 1.25 + SkASSERT(roots); 1.26 + 1.27 + if (A == 0) { 1.28 + return valid_divide(-C, B, roots); 1.29 + } 1.30 + 1.31 + float R = B*B - 4*A*C; 1.32 + if (R < 0) { 1.33 + return 0; 1.34 + } 1.35 + R = sk_float_sqrt(R); 1.36 + 1.37 +#if 1 1.38 + float Q = B; 1.39 + if (Q < 0) { 1.40 + Q -= R; 1.41 + } else { 1.42 + Q += R; 1.43 + } 1.44 +#else 1.45 + // on 10.6 this was much slower than the above branch :( 1.46 + float Q = B + copysignf(R, B); 1.47 +#endif 1.48 + Q *= -0.5f; 1.49 + if (0 == Q) { 1.50 + roots[0] = 0; 1.51 + return 1; 1.52 + } 1.53 + 1.54 + float r0 = Q / A; 1.55 + float r1 = C / Q; 1.56 + roots[0] = r0 < r1 ? r0 : r1; 1.57 + roots[1] = r0 > r1 ? r0 : r1; 1.58 + return 2; 1.59 +} 1.60 + 1.61 +static float lerp(float x, float dx, float t) { 1.62 + return x + t * dx; 1.63 +} 1.64 + 1.65 +static float sqr(float x) { return x * x; } 1.66 + 1.67 +void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, 1.68 + const SkPoint& center1, SkScalar rad1) { 1.69 + fCenterX = SkScalarToFloat(center0.fX); 1.70 + fCenterY = SkScalarToFloat(center0.fY); 1.71 + fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; 1.72 + fDCenterY = SkScalarToFloat(center1.fY) - fCenterY; 1.73 + fRadius = SkScalarToFloat(rad0); 1.74 + fDRadius = SkScalarToFloat(rad1) - fRadius; 1.75 + 1.76 + fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); 1.77 + fRadius2 = sqr(fRadius); 1.78 + fRDR = fRadius * fDRadius; 1.79 +} 1.80 + 1.81 +void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) { 1.82 + fRelX = SkScalarToFloat(fx) - fCenterX; 1.83 + fRelY = SkScalarToFloat(fy) - fCenterY; 1.84 + fIncX = SkScalarToFloat(dfx); 1.85 + fIncY = SkScalarToFloat(dfy); 1.86 + fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR); 1.87 + fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY); 1.88 +} 1.89 + 1.90 +SkFixed TwoPtRadial::nextT() { 1.91 + float roots[2]; 1.92 + 1.93 + float C = sqr(fRelX) + sqr(fRelY) - fRadius2; 1.94 + int countRoots = find_quad_roots(fA, fB, C, roots); 1.95 + 1.96 + fRelX += fIncX; 1.97 + fRelY += fIncY; 1.98 + fB += fDB; 1.99 + 1.100 + if (0 == countRoots) { 1.101 + return kDontDrawT; 1.102 + } 1.103 + 1.104 + // Prefer the bigger t value if both give a radius(t) > 0 1.105 + // find_quad_roots returns the values sorted, so we start with the last 1.106 + float t = roots[countRoots - 1]; 1.107 + float r = lerp(fRadius, fDRadius, t); 1.108 + if (r <= 0) { 1.109 + t = roots[0]; // might be the same as roots[countRoots-1] 1.110 + r = lerp(fRadius, fDRadius, t); 1.111 + if (r <= 0) { 1.112 + return kDontDrawT; 1.113 + } 1.114 + } 1.115 + return SkFloatToFixed(t); 1.116 +} 1.117 + 1.118 +typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC, 1.119 + const SkPMColor* cache, int toggle, int count); 1.120 + 1.121 +static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 1.122 + const SkPMColor* SK_RESTRICT cache, int toggle, 1.123 + int count) { 1.124 + for (; count > 0; --count) { 1.125 + SkFixed t = rec->nextT(); 1.126 + if (TwoPtRadial::DontDrawT(t)) { 1.127 + *dstC++ = 0; 1.128 + } else { 1.129 + SkFixed index = SkClampMax(t, 0xFFFF); 1.130 + SkASSERT(index <= 0xFFFF); 1.131 + *dstC++ = cache[toggle + 1.132 + (index >> SkGradientShaderBase::kCache32Shift)]; 1.133 + } 1.134 + toggle = next_dither_toggle(toggle); 1.135 + } 1.136 +} 1.137 + 1.138 +static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 1.139 + const SkPMColor* SK_RESTRICT cache, int toggle, 1.140 + int count) { 1.141 + for (; count > 0; --count) { 1.142 + SkFixed t = rec->nextT(); 1.143 + if (TwoPtRadial::DontDrawT(t)) { 1.144 + *dstC++ = 0; 1.145 + } else { 1.146 + SkFixed index = repeat_tileproc(t); 1.147 + SkASSERT(index <= 0xFFFF); 1.148 + *dstC++ = cache[toggle + 1.149 + (index >> SkGradientShaderBase::kCache32Shift)]; 1.150 + } 1.151 + toggle = next_dither_toggle(toggle); 1.152 + } 1.153 +} 1.154 + 1.155 +static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC, 1.156 + const SkPMColor* SK_RESTRICT cache, int toggle, 1.157 + int count) { 1.158 + for (; count > 0; --count) { 1.159 + SkFixed t = rec->nextT(); 1.160 + if (TwoPtRadial::DontDrawT(t)) { 1.161 + *dstC++ = 0; 1.162 + } else { 1.163 + SkFixed index = mirror_tileproc(t); 1.164 + SkASSERT(index <= 0xFFFF); 1.165 + *dstC++ = cache[toggle + 1.166 + (index >> SkGradientShaderBase::kCache32Shift)]; 1.167 + } 1.168 + toggle = next_dither_toggle(toggle); 1.169 + } 1.170 +} 1.171 + 1.172 +void SkTwoPointConicalGradient::init() { 1.173 + fRec.init(fCenter1, fRadius1, fCenter2, fRadius2); 1.174 + fPtsToUnit.reset(); 1.175 +} 1.176 + 1.177 +///////////////////////////////////////////////////////////////////// 1.178 + 1.179 +SkTwoPointConicalGradient::SkTwoPointConicalGradient( 1.180 + const SkPoint& start, SkScalar startRadius, 1.181 + const SkPoint& end, SkScalar endRadius, 1.182 + const Descriptor& desc) 1.183 + : SkGradientShaderBase(desc), 1.184 + fCenter1(start), 1.185 + fCenter2(end), 1.186 + fRadius1(startRadius), 1.187 + fRadius2(endRadius) { 1.188 + // this is degenerate, and should be caught by our caller 1.189 + SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); 1.190 + this->init(); 1.191 +} 1.192 + 1.193 +bool SkTwoPointConicalGradient::isOpaque() const { 1.194 + // Because areas outside the cone are left untouched, we cannot treat the 1.195 + // shader as opaque even if the gradient itself is opaque. 1.196 + // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 1.197 + return false; 1.198 +} 1.199 + 1.200 +void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, 1.201 + int count) { 1.202 + int toggle = init_dither_toggle(x, y); 1.203 + 1.204 + SkASSERT(count > 0); 1.205 + 1.206 + SkPMColor* SK_RESTRICT dstC = dstCParam; 1.207 + 1.208 + SkMatrix::MapXYProc dstProc = fDstToIndexProc; 1.209 + 1.210 + const SkPMColor* SK_RESTRICT cache = this->getCache32(); 1.211 + 1.212 + TwoPointConicalProc shadeProc = twopoint_repeat; 1.213 + if (SkShader::kClamp_TileMode == fTileMode) { 1.214 + shadeProc = twopoint_clamp; 1.215 + } else if (SkShader::kMirror_TileMode == fTileMode) { 1.216 + shadeProc = twopoint_mirror; 1.217 + } else { 1.218 + SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 1.219 + } 1.220 + 1.221 + if (fDstToIndexClass != kPerspective_MatrixClass) { 1.222 + SkPoint srcPt; 1.223 + dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 1.224 + SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 1.225 + SkScalar dx, fx = srcPt.fX; 1.226 + SkScalar dy, fy = srcPt.fY; 1.227 + 1.228 + if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 1.229 + SkFixed fixedX, fixedY; 1.230 + (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 1.231 + dx = SkFixedToScalar(fixedX); 1.232 + dy = SkFixedToScalar(fixedY); 1.233 + } else { 1.234 + SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 1.235 + dx = fDstToIndex.getScaleX(); 1.236 + dy = fDstToIndex.getSkewY(); 1.237 + } 1.238 + 1.239 + fRec.setup(fx, fy, dx, dy); 1.240 + (*shadeProc)(&fRec, dstC, cache, toggle, count); 1.241 + } else { // perspective case 1.242 + SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf; 1.243 + SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf; 1.244 + for (; count > 0; --count) { 1.245 + SkPoint srcPt; 1.246 + dstProc(fDstToIndex, dstX, dstY, &srcPt); 1.247 + fRec.setup(srcPt.fX, srcPt.fY, 0, 0); 1.248 + (*shadeProc)(&fRec, dstC, cache, toggle, 1); 1.249 + 1.250 + dstX += SK_Scalar1; 1.251 + toggle = next_dither_toggle(toggle); 1.252 + dstC += 1; 1.253 + } 1.254 + } 1.255 +} 1.256 + 1.257 +bool SkTwoPointConicalGradient::setContext(const SkBitmap& device, 1.258 + const SkPaint& paint, 1.259 + const SkMatrix& matrix) { 1.260 + if (!this->INHERITED::setContext(device, paint, matrix)) { 1.261 + return false; 1.262 + } 1.263 + 1.264 + // we don't have a span16 proc 1.265 + fFlags &= ~kHasSpan16_Flag; 1.266 + 1.267 + // in general, we might discard based on computed-radius, so clear 1.268 + // this flag (todo: sometimes we can detect that we never discard...) 1.269 + fFlags &= ~kOpaqueAlpha_Flag; 1.270 + 1.271 + return true; 1.272 +} 1.273 + 1.274 +SkShader::BitmapType SkTwoPointConicalGradient::asABitmap( 1.275 + SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const { 1.276 + SkPoint diff = fCenter2 - fCenter1; 1.277 + SkScalar diffLen = 0; 1.278 + 1.279 + if (bitmap) { 1.280 + this->getGradientTableBitmap(bitmap); 1.281 + } 1.282 + if (matrix) { 1.283 + diffLen = diff.length(); 1.284 + } 1.285 + if (matrix) { 1.286 + if (diffLen) { 1.287 + SkScalar invDiffLen = SkScalarInvert(diffLen); 1.288 + // rotate to align circle centers with the x-axis 1.289 + matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY), 1.290 + SkScalarMul(invDiffLen, diff.fX)); 1.291 + } else { 1.292 + matrix->reset(); 1.293 + } 1.294 + matrix->preTranslate(-fCenter1.fX, -fCenter1.fY); 1.295 + } 1.296 + if (xy) { 1.297 + xy[0] = fTileMode; 1.298 + xy[1] = kClamp_TileMode; 1.299 + } 1.300 + return kTwoPointConical_BitmapType; 1.301 +} 1.302 + 1.303 +SkShader::GradientType SkTwoPointConicalGradient::asAGradient( 1.304 + GradientInfo* info) const { 1.305 + if (info) { 1.306 + commonAsAGradient(info); 1.307 + info->fPoint[0] = fCenter1; 1.308 + info->fPoint[1] = fCenter2; 1.309 + info->fRadius[0] = fRadius1; 1.310 + info->fRadius[1] = fRadius2; 1.311 + } 1.312 + return kConical_GradientType; 1.313 +} 1.314 + 1.315 +SkTwoPointConicalGradient::SkTwoPointConicalGradient( 1.316 + SkReadBuffer& buffer) 1.317 + : INHERITED(buffer), 1.318 + fCenter1(buffer.readPoint()), 1.319 + fCenter2(buffer.readPoint()), 1.320 + fRadius1(buffer.readScalar()), 1.321 + fRadius2(buffer.readScalar()) { 1.322 + this->init(); 1.323 +}; 1.324 + 1.325 +void SkTwoPointConicalGradient::flatten( 1.326 + SkWriteBuffer& buffer) const { 1.327 + this->INHERITED::flatten(buffer); 1.328 + buffer.writePoint(fCenter1); 1.329 + buffer.writePoint(fCenter2); 1.330 + buffer.writeScalar(fRadius1); 1.331 + buffer.writeScalar(fRadius2); 1.332 +} 1.333 + 1.334 +///////////////////////////////////////////////////////////////////// 1.335 + 1.336 +#if SK_SUPPORT_GPU 1.337 + 1.338 +#include "GrTBackendEffectFactory.h" 1.339 + 1.340 +// For brevity 1.341 +typedef GrGLUniformManager::UniformHandle UniformHandle; 1.342 + 1.343 +class GrGLConical2Gradient : public GrGLGradientEffect { 1.344 +public: 1.345 + 1.346 + GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); 1.347 + virtual ~GrGLConical2Gradient() { } 1.348 + 1.349 + virtual void emitCode(GrGLShaderBuilder*, 1.350 + const GrDrawEffect&, 1.351 + EffectKey, 1.352 + const char* outputColor, 1.353 + const char* inputColor, 1.354 + const TransformedCoordsArray&, 1.355 + const TextureSamplerArray&) SK_OVERRIDE; 1.356 + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 1.357 + 1.358 + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); 1.359 + 1.360 +protected: 1.361 + 1.362 + UniformHandle fParamUni; 1.363 + 1.364 + const char* fVSVaryingName; 1.365 + const char* fFSVaryingName; 1.366 + 1.367 + bool fIsDegenerate; 1.368 + 1.369 + // @{ 1.370 + /// Values last uploaded as uniforms 1.371 + 1.372 + SkScalar fCachedCenter; 1.373 + SkScalar fCachedRadius; 1.374 + SkScalar fCachedDiffRadius; 1.375 + 1.376 + // @} 1.377 + 1.378 +private: 1.379 + 1.380 + typedef GrGLGradientEffect INHERITED; 1.381 + 1.382 +}; 1.383 + 1.384 +///////////////////////////////////////////////////////////////////// 1.385 + 1.386 +class GrConical2Gradient : public GrGradientEffect { 1.387 +public: 1.388 + 1.389 + static GrEffectRef* Create(GrContext* ctx, 1.390 + const SkTwoPointConicalGradient& shader, 1.391 + const SkMatrix& matrix, 1.392 + SkShader::TileMode tm) { 1.393 + AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm))); 1.394 + return CreateEffectRef(effect); 1.395 + } 1.396 + 1.397 + virtual ~GrConical2Gradient() { } 1.398 + 1.399 + static const char* Name() { return "Two-Point Conical Gradient"; } 1.400 + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 1.401 + return GrTBackendEffectFactory<GrConical2Gradient>::getInstance(); 1.402 + } 1.403 + 1.404 + // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. 1.405 + bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); } 1.406 + SkScalar center() const { return fCenterX1; } 1.407 + SkScalar diffRadius() const { return fDiffRadius; } 1.408 + SkScalar radius() const { return fRadius0; } 1.409 + 1.410 + typedef GrGLConical2Gradient GLEffect; 1.411 + 1.412 +private: 1.413 + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { 1.414 + const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase); 1.415 + return (INHERITED::onIsEqual(sBase) && 1.416 + this->fCenterX1 == s.fCenterX1 && 1.417 + this->fRadius0 == s.fRadius0 && 1.418 + this->fDiffRadius == s.fDiffRadius); 1.419 + } 1.420 + 1.421 + GrConical2Gradient(GrContext* ctx, 1.422 + const SkTwoPointConicalGradient& shader, 1.423 + const SkMatrix& matrix, 1.424 + SkShader::TileMode tm) 1.425 + : INHERITED(ctx, shader, matrix, tm) 1.426 + , fCenterX1(shader.getCenterX1()) 1.427 + , fRadius0(shader.getStartRadius()) 1.428 + , fDiffRadius(shader.getDiffRadius()) { 1.429 + // We pass the linear part of the quadratic as a varying. 1.430 + // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) 1.431 + fBTransform = this->getCoordTransform(); 1.432 + SkMatrix& bMatrix = *fBTransform.accessMatrix(); 1.433 + SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); 1.434 + bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) + 1.435 + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0])); 1.436 + bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) + 1.437 + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1])); 1.438 + bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) + 1.439 + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2])); 1.440 + this->addCoordTransform(&fBTransform); 1.441 + } 1.442 + 1.443 + GR_DECLARE_EFFECT_TEST; 1.444 + 1.445 + // @{ 1.446 + // Cache of values - these can change arbitrarily, EXCEPT 1.447 + // we shouldn't change between degenerate and non-degenerate?! 1.448 + 1.449 + GrCoordTransform fBTransform; 1.450 + SkScalar fCenterX1; 1.451 + SkScalar fRadius0; 1.452 + SkScalar fDiffRadius; 1.453 + 1.454 + // @} 1.455 + 1.456 + typedef GrGradientEffect INHERITED; 1.457 +}; 1.458 + 1.459 +GR_DEFINE_EFFECT_TEST(GrConical2Gradient); 1.460 + 1.461 +GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random, 1.462 + GrContext* context, 1.463 + const GrDrawTargetCaps&, 1.464 + GrTexture**) { 1.465 + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; 1.466 + SkScalar radius1 = random->nextUScalar1(); 1.467 + SkPoint center2; 1.468 + SkScalar radius2; 1.469 + do { 1.470 + center2.set(random->nextUScalar1(), random->nextUScalar1()); 1.471 + radius2 = random->nextUScalar1 (); 1.472 + // If the circles are identical the factory will give us an empty shader. 1.473 + } while (radius1 == radius2 && center1 == center2); 1.474 + 1.475 + SkColor colors[kMaxRandomGradientColors]; 1.476 + SkScalar stopsArray[kMaxRandomGradientColors]; 1.477 + SkScalar* stops = stopsArray; 1.478 + SkShader::TileMode tm; 1.479 + int colorCount = RandomGradientParams(random, colors, &stops, &tm); 1.480 + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, 1.481 + center2, radius2, 1.482 + colors, stops, colorCount, 1.483 + tm)); 1.484 + SkPaint paint; 1.485 + return shader->asNewEffect(context, paint); 1.486 +} 1.487 + 1.488 + 1.489 +///////////////////////////////////////////////////////////////////// 1.490 + 1.491 +GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory, 1.492 + const GrDrawEffect& drawEffect) 1.493 + : INHERITED(factory) 1.494 + , fVSVaryingName(NULL) 1.495 + , fFSVaryingName(NULL) 1.496 + , fCachedCenter(SK_ScalarMax) 1.497 + , fCachedRadius(-SK_ScalarMax) 1.498 + , fCachedDiffRadius(-SK_ScalarMax) { 1.499 + 1.500 + const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>(); 1.501 + fIsDegenerate = data.isDegenerate(); 1.502 +} 1.503 + 1.504 +void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder, 1.505 + const GrDrawEffect&, 1.506 + EffectKey key, 1.507 + const char* outputColor, 1.508 + const char* inputColor, 1.509 + const TransformedCoordsArray& coords, 1.510 + const TextureSamplerArray& samplers) { 1.511 + this->emitUniforms(builder, key); 1.512 + fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, 1.513 + kFloat_GrSLType, "Conical2FSParams", 6); 1.514 + 1.515 + SkString cName("c"); 1.516 + SkString ac4Name("ac4"); 1.517 + SkString dName("d"); 1.518 + SkString qName("q"); 1.519 + SkString r0Name("r0"); 1.520 + SkString r1Name("r1"); 1.521 + SkString tName("t"); 1.522 + SkString p0; // 4a 1.523 + SkString p1; // 1/a 1.524 + SkString p2; // distance between centers 1.525 + SkString p3; // start radius 1.526 + SkString p4; // start radius squared 1.527 + SkString p5; // difference in radii (r1 - r0) 1.528 + 1.529 + builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); 1.530 + builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); 1.531 + builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); 1.532 + builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); 1.533 + builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); 1.534 + builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); 1.535 + 1.536 + // We interpolate the linear component in coords[1]. 1.537 + SkASSERT(coords[0].type() == coords[1].type()); 1.538 + const char* coords2D; 1.539 + SkString bVar; 1.540 + if (kVec3f_GrSLType == coords[0].type()) { 1.541 + builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", 1.542 + coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); 1.543 + coords2D = "interpolants.xy"; 1.544 + bVar = "interpolants.z"; 1.545 + } else { 1.546 + coords2D = coords[0].c_str(); 1.547 + bVar.printf("%s.x", coords[1].c_str()); 1.548 + } 1.549 + 1.550 + // output will default to transparent black (we simply won't write anything 1.551 + // else to it if invalid, instead of discarding or returning prematurely) 1.552 + builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); 1.553 + 1.554 + // c = (x^2)+(y^2) - params[4] 1.555 + builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", 1.556 + cName.c_str(), coords2D, coords2D, p4.c_str()); 1.557 + 1.558 + // Non-degenerate case (quadratic) 1.559 + if (!fIsDegenerate) { 1.560 + 1.561 + // ac4 = params[0] * c 1.562 + builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(), 1.563 + cName.c_str()); 1.564 + 1.565 + // d = b^2 - ac4 1.566 + builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(), 1.567 + bVar.c_str(), bVar.c_str(), ac4Name.c_str()); 1.568 + 1.569 + // only proceed if discriminant is >= 0 1.570 + builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str()); 1.571 + 1.572 + // intermediate value we'll use to compute the roots 1.573 + // q = -0.5 * (b +/- sqrt(d)) 1.574 + builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)" 1.575 + " * sqrt(%s));\n", qName.c_str(), bVar.c_str(), 1.576 + bVar.c_str(), dName.c_str()); 1.577 + 1.578 + // compute both roots 1.579 + // r0 = q * params[1] 1.580 + builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(), 1.581 + qName.c_str(), p1.c_str()); 1.582 + // r1 = c / q 1.583 + builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(), 1.584 + cName.c_str(), qName.c_str()); 1.585 + 1.586 + // Note: If there are two roots that both generate radius(t) > 0, the 1.587 + // Canvas spec says to choose the larger t. 1.588 + 1.589 + // so we'll look at the larger one first: 1.590 + builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), 1.591 + r0Name.c_str(), r1Name.c_str()); 1.592 + 1.593 + // if r(t) > 0, then we're done; t will be our x coordinate 1.594 + builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), 1.595 + p5.c_str(), p3.c_str()); 1.596 + 1.597 + builder->fsCodeAppend("\t\t"); 1.598 + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 1.599 + 1.600 + // otherwise, if r(t) for the larger root was <= 0, try the other root 1.601 + builder->fsCodeAppend("\t\t} else {\n"); 1.602 + builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), 1.603 + r0Name.c_str(), r1Name.c_str()); 1.604 + 1.605 + // if r(t) > 0 for the smaller root, then t will be our x coordinate 1.606 + builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", 1.607 + tName.c_str(), p5.c_str(), p3.c_str()); 1.608 + 1.609 + builder->fsCodeAppend("\t\t\t"); 1.610 + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 1.611 + 1.612 + // end if (r(t) > 0) for smaller root 1.613 + builder->fsCodeAppend("\t\t\t}\n"); 1.614 + // end if (r(t) > 0), else, for larger root 1.615 + builder->fsCodeAppend("\t\t}\n"); 1.616 + // end if (discriminant >= 0) 1.617 + builder->fsCodeAppend("\t}\n"); 1.618 + } else { 1.619 + 1.620 + // linear case: t = -c/b 1.621 + builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), 1.622 + cName.c_str(), bVar.c_str()); 1.623 + 1.624 + // if r(t) > 0, then t will be the x coordinate 1.625 + builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), 1.626 + p5.c_str(), p3.c_str()); 1.627 + builder->fsCodeAppend("\t"); 1.628 + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); 1.629 + builder->fsCodeAppend("\t}\n"); 1.630 + } 1.631 +} 1.632 + 1.633 +void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, 1.634 + const GrDrawEffect& drawEffect) { 1.635 + INHERITED::setData(uman, drawEffect); 1.636 + const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>(); 1.637 + SkASSERT(data.isDegenerate() == fIsDegenerate); 1.638 + SkScalar centerX1 = data.center(); 1.639 + SkScalar radius0 = data.radius(); 1.640 + SkScalar diffRadius = data.diffRadius(); 1.641 + 1.642 + if (fCachedCenter != centerX1 || 1.643 + fCachedRadius != radius0 || 1.644 + fCachedDiffRadius != diffRadius) { 1.645 + 1.646 + SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius; 1.647 + 1.648 + // When we're in the degenerate (linear) case, the second 1.649 + // value will be INF but the program doesn't read it. (We 1.650 + // use the same 6 uniforms even though we don't need them 1.651 + // all in the linear case just to keep the code complexity 1.652 + // down). 1.653 + float values[6] = { 1.654 + SkScalarToFloat(a * 4), 1.655 + 1.f / (SkScalarToFloat(a)), 1.656 + SkScalarToFloat(centerX1), 1.657 + SkScalarToFloat(radius0), 1.658 + SkScalarToFloat(SkScalarMul(radius0, radius0)), 1.659 + SkScalarToFloat(diffRadius) 1.660 + }; 1.661 + 1.662 + uman.set1fv(fParamUni, 6, values); 1.663 + fCachedCenter = centerX1; 1.664 + fCachedRadius = radius0; 1.665 + fCachedDiffRadius = diffRadius; 1.666 + } 1.667 +} 1.668 + 1.669 +GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect, 1.670 + const GrGLCaps&) { 1.671 + enum { 1.672 + kIsDegenerate = 1 << kBaseKeyBitCnt, 1.673 + }; 1.674 + 1.675 + EffectKey key = GenBaseGradientKey(drawEffect); 1.676 + if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) { 1.677 + key |= kIsDegenerate; 1.678 + } 1.679 + return key; 1.680 +} 1.681 + 1.682 +///////////////////////////////////////////////////////////////////// 1.683 + 1.684 +GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const { 1.685 + SkASSERT(NULL != context); 1.686 + SkASSERT(fPtsToUnit.isIdentity()); 1.687 + // invert the localM, translate to center1, rotate so center2 is on x axis. 1.688 + SkMatrix matrix; 1.689 + if (!this->getLocalMatrix().invert(&matrix)) { 1.690 + return NULL; 1.691 + } 1.692 + matrix.postTranslate(-fCenter1.fX, -fCenter1.fY); 1.693 + 1.694 + SkPoint diff = fCenter2 - fCenter1; 1.695 + SkScalar diffLen = diff.length(); 1.696 + if (0 != diffLen) { 1.697 + SkScalar invDiffLen = SkScalarInvert(diffLen); 1.698 + SkMatrix rot; 1.699 + rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY), 1.700 + SkScalarMul(invDiffLen, diff.fX)); 1.701 + matrix.postConcat(rot); 1.702 + } 1.703 + 1.704 + return GrConical2Gradient::Create(context, *this, matrix, fTileMode); 1.705 +} 1.706 + 1.707 +#else 1.708 + 1.709 +GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const { 1.710 + SkDEBUGFAIL("Should not call in GPU-less build"); 1.711 + return NULL; 1.712 +} 1.713 + 1.714 +#endif 1.715 + 1.716 +#ifndef SK_IGNORE_TO_STRING 1.717 +void SkTwoPointConicalGradient::toString(SkString* str) const { 1.718 + str->append("SkTwoPointConicalGradient: ("); 1.719 + 1.720 + str->append("center1: ("); 1.721 + str->appendScalar(fCenter1.fX); 1.722 + str->append(", "); 1.723 + str->appendScalar(fCenter1.fY); 1.724 + str->append(") radius1: "); 1.725 + str->appendScalar(fRadius1); 1.726 + str->append(" "); 1.727 + 1.728 + str->append("center2: ("); 1.729 + str->appendScalar(fCenter2.fX); 1.730 + str->append(", "); 1.731 + str->appendScalar(fCenter2.fY); 1.732 + str->append(") radius2: "); 1.733 + str->appendScalar(fRadius2); 1.734 + str->append(" "); 1.735 + 1.736 + this->INHERITED::toString(str); 1.737 + 1.738 + str->append(")"); 1.739 +} 1.740 +#endif