michael@0: michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkTwoPointRadialGradient.h" michael@0: michael@0: /* Two-point radial gradients are specified by two circles, each with a center michael@0: point and radius. The gradient can be considered to be a series of michael@0: concentric circles, with the color interpolated from the start circle michael@0: (at t=0) to the end circle (at t=1). michael@0: michael@0: For each point (x, y) in the span, we want to find the michael@0: interpolated circle that intersects that point. The center michael@0: of the desired circle (Cx, Cy) falls at some distance t michael@0: along the line segment between the start point (Sx, Sy) and michael@0: end point (Ex, Ey): michael@0: michael@0: Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1) michael@0: Cy = (1 - t) * Sy + t * Ey michael@0: michael@0: The radius of the desired circle (r) is also a linear interpolation t michael@0: between the start and end radii (Sr and Er): michael@0: michael@0: r = (1 - t) * Sr + t * Er michael@0: michael@0: But michael@0: michael@0: (x - Cx)^2 + (y - Cy)^2 = r^2 michael@0: michael@0: so michael@0: michael@0: (x - ((1 - t) * Sx + t * Ex))^2 michael@0: + (y - ((1 - t) * Sy + t * Ey))^2 michael@0: = ((1 - t) * Sr + t * Er)^2 michael@0: michael@0: Solving for t yields michael@0: michael@0: [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2 michael@0: + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t michael@0: + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0 michael@0: michael@0: To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy michael@0: michael@0: [Dx^2 + Dy^2 - Dr^2)] * t^2 michael@0: + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t michael@0: + [dx^2 + dy^2 - Sr^2] = 0 michael@0: michael@0: A quadratic in t. The two roots of the quadratic reflect the two michael@0: possible circles on which the point may fall. Solving for t yields michael@0: the gradient value to use. michael@0: michael@0: If a<0, the start circle is entirely contained in the michael@0: end circle, and one of the roots will be <0 or >1 (off the line michael@0: segment). If a>0, the start circle falls at least partially michael@0: outside the end circle (or vice versa), and the gradient michael@0: defines a "tube" where a point may be on one circle (on the michael@0: inside of the tube) or the other (outside of the tube). We choose michael@0: one arbitrarily. michael@0: michael@0: In order to keep the math to within the limits of fixed point, michael@0: we divide the entire quadratic by Dr^2, and replace michael@0: (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving michael@0: michael@0: [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 michael@0: + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t michael@0: + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 michael@0: michael@0: (x' and y' are computed by appending the subtract and scale to the michael@0: fDstToIndex matrix in the constructor). michael@0: michael@0: Since the 'A' component of the quadratic is independent of x' and y', it michael@0: is precomputed in the constructor. Since the 'B' component is linear in michael@0: x' and y', if x and y are linear in the span, 'B' can be computed michael@0: incrementally with a simple delta (db below). If it is not (e.g., michael@0: a perspective projection), it must be computed in the loop. michael@0: michael@0: */ michael@0: michael@0: namespace { michael@0: michael@0: inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, michael@0: SkScalar sr2d2, SkScalar foura, michael@0: SkScalar oneOverTwoA, bool posRoot) { michael@0: SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; michael@0: if (0 == foura) { michael@0: return SkScalarToFixed(SkScalarDiv(-c, b)); michael@0: } michael@0: michael@0: SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); michael@0: if (discrim < 0) { michael@0: discrim = -discrim; michael@0: } michael@0: SkScalar rootDiscrim = SkScalarSqrt(discrim); michael@0: SkScalar result; michael@0: if (posRoot) { michael@0: result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); michael@0: } else { michael@0: result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); michael@0: } michael@0: return SkScalarToFixed(result); michael@0: } michael@0: michael@0: typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, michael@0: SkScalar fy, SkScalar dy, michael@0: SkScalar b, SkScalar db, michael@0: SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, michael@0: SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, michael@0: int count); michael@0: michael@0: void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, michael@0: SkScalar fy, SkScalar dy, michael@0: SkScalar b, SkScalar db, michael@0: SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, michael@0: SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, michael@0: int count) { michael@0: for (; count > 0; --count) { michael@0: SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, michael@0: fOneOverTwoA, posRoot); michael@0: SkFixed index = SkClampMax(t, 0xFFFF); michael@0: SkASSERT(index <= 0xFFFF); michael@0: *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; michael@0: fx += dx; michael@0: fy += dy; michael@0: b += db; michael@0: } michael@0: } michael@0: void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, michael@0: SkScalar fy, SkScalar dy, michael@0: SkScalar b, SkScalar db, michael@0: SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, michael@0: SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, michael@0: int count) { michael@0: for (; count > 0; --count) { michael@0: SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, michael@0: fOneOverTwoA, posRoot); michael@0: SkFixed index = mirror_tileproc(t); michael@0: SkASSERT(index <= 0xFFFF); michael@0: *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; michael@0: fx += dx; michael@0: fy += dy; michael@0: b += db; michael@0: } michael@0: } michael@0: michael@0: void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, michael@0: SkScalar fy, SkScalar dy, michael@0: SkScalar b, SkScalar db, michael@0: SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, michael@0: SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, michael@0: int count) { michael@0: for (; count > 0; --count) { michael@0: SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, michael@0: fOneOverTwoA, posRoot); michael@0: SkFixed index = repeat_tileproc(t); michael@0: SkASSERT(index <= 0xFFFF); michael@0: *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; michael@0: fx += dx; michael@0: fy += dy; michael@0: b += db; michael@0: } michael@0: } michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: SkTwoPointRadialGradient::SkTwoPointRadialGradient( michael@0: const SkPoint& start, SkScalar startRadius, michael@0: const SkPoint& end, SkScalar endRadius, michael@0: const Descriptor& desc) michael@0: : SkGradientShaderBase(desc), michael@0: fCenter1(start), michael@0: fCenter2(end), michael@0: fRadius1(startRadius), michael@0: fRadius2(endRadius) { michael@0: init(); michael@0: } michael@0: michael@0: SkShader::BitmapType SkTwoPointRadialGradient::asABitmap( michael@0: SkBitmap* bitmap, michael@0: SkMatrix* matrix, michael@0: SkShader::TileMode* xy) const { michael@0: if (bitmap) { michael@0: this->getGradientTableBitmap(bitmap); michael@0: } michael@0: SkScalar diffL = 0; // just to avoid gcc warning michael@0: if (matrix) { michael@0: diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) + michael@0: SkScalarSquare(fDiff.fY)); michael@0: } michael@0: if (matrix) { michael@0: if (diffL) { michael@0: SkScalar invDiffL = SkScalarInvert(diffL); michael@0: matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY), michael@0: SkScalarMul(invDiffL, fDiff.fX)); michael@0: } else { michael@0: matrix->reset(); michael@0: } michael@0: matrix->preConcat(fPtsToUnit); michael@0: } michael@0: if (xy) { michael@0: xy[0] = fTileMode; michael@0: xy[1] = kClamp_TileMode; michael@0: } michael@0: return kTwoPointRadial_BitmapType; michael@0: } michael@0: michael@0: SkShader::GradientType SkTwoPointRadialGradient::asAGradient( michael@0: SkShader::GradientInfo* info) const { michael@0: if (info) { michael@0: commonAsAGradient(info); michael@0: info->fPoint[0] = fCenter1; michael@0: info->fPoint[1] = fCenter2; michael@0: info->fRadius[0] = fRadius1; michael@0: info->fRadius[1] = fRadius2; michael@0: } michael@0: return kRadial2_GradientType; michael@0: } michael@0: michael@0: void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam, michael@0: int count) { michael@0: SkASSERT(count > 0); michael@0: michael@0: SkPMColor* SK_RESTRICT dstC = dstCParam; michael@0: michael@0: // Zero difference between radii: fill with transparent black. michael@0: if (fDiffRadius == 0) { michael@0: sk_bzero(dstC, count * sizeof(*dstC)); michael@0: return; michael@0: } michael@0: SkMatrix::MapXYProc dstProc = fDstToIndexProc; michael@0: TileProc proc = fTileProc; michael@0: const SkPMColor* SK_RESTRICT cache = this->getCache32(); michael@0: michael@0: SkScalar foura = fA * 4; michael@0: bool posRoot = fDiffRadius < 0; michael@0: if (fDstToIndexClass != kPerspective_MatrixClass) { michael@0: SkPoint srcPt; michael@0: dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, michael@0: SkIntToScalar(y) + SK_ScalarHalf, &srcPt); michael@0: SkScalar dx, fx = srcPt.fX; michael@0: SkScalar dy, fy = srcPt.fY; michael@0: michael@0: if (fDstToIndexClass == kFixedStepInX_MatrixClass) { michael@0: SkFixed fixedX, fixedY; michael@0: (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); michael@0: dx = SkFixedToScalar(fixedX); michael@0: dy = SkFixedToScalar(fixedY); michael@0: } else { michael@0: SkASSERT(fDstToIndexClass == kLinear_MatrixClass); michael@0: dx = fDstToIndex.getScaleX(); michael@0: dy = fDstToIndex.getSkewY(); michael@0: } michael@0: SkScalar b = (SkScalarMul(fDiff.fX, fx) + michael@0: SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; michael@0: SkScalar db = (SkScalarMul(fDiff.fX, dx) + michael@0: SkScalarMul(fDiff.fY, dy)) * 2; michael@0: michael@0: TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; michael@0: if (SkShader::kClamp_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan_twopoint_clamp; michael@0: } else if (SkShader::kMirror_TileMode == fTileMode) { michael@0: shadeProc = shadeSpan_twopoint_mirror; michael@0: } else { michael@0: SkASSERT(SkShader::kRepeat_TileMode == fTileMode); michael@0: } michael@0: (*shadeProc)(fx, dx, fy, dy, b, db, michael@0: fSr2D2, foura, fOneOverTwoA, posRoot, michael@0: dstC, cache, count); michael@0: } else { // perspective case michael@0: SkScalar dstX = SkIntToScalar(x); michael@0: SkScalar dstY = SkIntToScalar(y); michael@0: for (; count > 0; --count) { michael@0: SkPoint srcPt; michael@0: dstProc(fDstToIndex, dstX, dstY, &srcPt); michael@0: SkScalar fx = srcPt.fX; michael@0: SkScalar fy = srcPt.fY; michael@0: SkScalar b = (SkScalarMul(fDiff.fX, fx) + michael@0: SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; michael@0: SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, michael@0: fOneOverTwoA, posRoot); michael@0: SkFixed index = proc(t); michael@0: SkASSERT(index <= 0xFFFF); michael@0: *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift]; michael@0: dstX += SK_Scalar1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool SkTwoPointRadialGradient::setContext( const SkBitmap& device, michael@0: const SkPaint& paint, michael@0: const SkMatrix& matrix){ michael@0: // For now, we might have divided by zero, so detect that michael@0: if (0 == fDiffRadius) { michael@0: return false; michael@0: } michael@0: michael@0: if (!this->INHERITED::setContext(device, paint, matrix)) { michael@0: return false; michael@0: } michael@0: michael@0: // we don't have a span16 proc michael@0: fFlags &= ~kHasSpan16_Flag; michael@0: return true; michael@0: } michael@0: michael@0: #ifndef SK_IGNORE_TO_STRING michael@0: void SkTwoPointRadialGradient::toString(SkString* str) const { michael@0: str->append("SkTwoPointRadialGradient: ("); michael@0: michael@0: str->append("center1: ("); michael@0: str->appendScalar(fCenter1.fX); michael@0: str->append(", "); michael@0: str->appendScalar(fCenter1.fY); michael@0: str->append(") radius1: "); michael@0: str->appendScalar(fRadius1); michael@0: str->append(" "); michael@0: michael@0: str->append("center2: ("); michael@0: str->appendScalar(fCenter2.fX); michael@0: str->append(", "); michael@0: str->appendScalar(fCenter2.fY); michael@0: str->append(") radius2: "); michael@0: str->appendScalar(fRadius2); michael@0: str->append(" "); michael@0: michael@0: this->INHERITED::toString(str); michael@0: michael@0: str->append(")"); michael@0: } michael@0: #endif michael@0: michael@0: SkTwoPointRadialGradient::SkTwoPointRadialGradient( michael@0: SkReadBuffer& buffer) michael@0: : INHERITED(buffer), michael@0: fCenter1(buffer.readPoint()), michael@0: fCenter2(buffer.readPoint()), michael@0: fRadius1(buffer.readScalar()), michael@0: fRadius2(buffer.readScalar()) { michael@0: init(); michael@0: }; michael@0: michael@0: void SkTwoPointRadialGradient::flatten( michael@0: SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writePoint(fCenter1); michael@0: buffer.writePoint(fCenter2); michael@0: buffer.writeScalar(fRadius1); michael@0: buffer.writeScalar(fRadius2); michael@0: } michael@0: michael@0: void SkTwoPointRadialGradient::init() { michael@0: fDiff = fCenter1 - fCenter2; michael@0: fDiffRadius = fRadius2 - fRadius1; michael@0: // hack to avoid zero-divide for now michael@0: SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0; michael@0: fDiff.fX = SkScalarMul(fDiff.fX, inv); michael@0: fDiff.fY = SkScalarMul(fDiff.fY, inv); michael@0: fStartRadius = SkScalarMul(fRadius1, inv); michael@0: fSr2D2 = SkScalarSquare(fStartRadius); michael@0: fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; michael@0: fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; michael@0: michael@0: fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); michael@0: fPtsToUnit.postScale(inv, inv); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: #if SK_SUPPORT_GPU michael@0: michael@0: #include "GrTBackendEffectFactory.h" michael@0: michael@0: // For brevity michael@0: typedef GrGLUniformManager::UniformHandle UniformHandle; michael@0: michael@0: class GrGLRadial2Gradient : public GrGLGradientEffect { michael@0: michael@0: public: michael@0: michael@0: GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); michael@0: virtual ~GrGLRadial2Gradient() { } michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder*, michael@0: const GrDrawEffect&, michael@0: EffectKey, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; michael@0: michael@0: static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); michael@0: michael@0: protected: michael@0: michael@0: UniformHandle fParamUni; michael@0: michael@0: const char* fVSVaryingName; michael@0: const char* fFSVaryingName; michael@0: michael@0: bool fIsDegenerate; michael@0: michael@0: // @{ michael@0: /// Values last uploaded as uniforms michael@0: michael@0: SkScalar fCachedCenter; michael@0: SkScalar fCachedRadius; michael@0: bool fCachedPosRoot; michael@0: michael@0: // @} michael@0: michael@0: private: michael@0: michael@0: typedef GrGLGradientEffect INHERITED; michael@0: michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrRadial2Gradient : public GrGradientEffect { michael@0: public: michael@0: static GrEffectRef* Create(GrContext* ctx, michael@0: const SkTwoPointRadialGradient& shader, michael@0: const SkMatrix& matrix, michael@0: SkShader::TileMode tm) { michael@0: AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm))); michael@0: return CreateEffectRef(effect); michael@0: } michael@0: michael@0: virtual ~GrRadial2Gradient() { } michael@0: michael@0: static const char* Name() { return "Two-Point Radial Gradient"; } michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. michael@0: bool isDegenerate() const { return SK_Scalar1 == fCenterX1; } michael@0: SkScalar center() const { return fCenterX1; } michael@0: SkScalar radius() const { return fRadius0; } michael@0: bool isPosRoot() const { return SkToBool(fPosRoot); } michael@0: michael@0: typedef GrGLRadial2Gradient GLEffect; michael@0: michael@0: private: michael@0: virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { michael@0: const GrRadial2Gradient& s = CastEffect(sBase); michael@0: return (INHERITED::onIsEqual(sBase) && michael@0: this->fCenterX1 == s.fCenterX1 && michael@0: this->fRadius0 == s.fRadius0 && michael@0: this->fPosRoot == s.fPosRoot); michael@0: } michael@0: michael@0: GrRadial2Gradient(GrContext* ctx, michael@0: const SkTwoPointRadialGradient& shader, michael@0: const SkMatrix& matrix, michael@0: SkShader::TileMode tm) michael@0: : INHERITED(ctx, shader, matrix, tm) michael@0: , fCenterX1(shader.getCenterX1()) michael@0: , fRadius0(shader.getStartRadius()) michael@0: , fPosRoot(shader.getDiffRadius() < 0) { michael@0: // We pass the linear part of the quadratic as a varying. michael@0: // float b = 2.0 * (fCenterX1 * x - fRadius0 * z) michael@0: fBTransform = this->getCoordTransform(); michael@0: SkMatrix& bMatrix = *fBTransform.accessMatrix(); michael@0: bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) - michael@0: SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0])); michael@0: bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) - michael@0: SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1])); michael@0: bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) - michael@0: SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2])); michael@0: this->addCoordTransform(&fBTransform); michael@0: } michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: // @{ michael@0: // Cache of values - these can change arbitrarily, EXCEPT michael@0: // we shouldn't change between degenerate and non-degenerate?! michael@0: michael@0: GrCoordTransform fBTransform; michael@0: SkScalar fCenterX1; michael@0: SkScalar fRadius0; michael@0: SkBool8 fPosRoot; michael@0: michael@0: // @} michael@0: michael@0: typedef GrGradientEffect INHERITED; michael@0: }; michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrRadial2Gradient); michael@0: michael@0: GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random, michael@0: GrContext* context, michael@0: const GrDrawTargetCaps&, michael@0: GrTexture**) { michael@0: SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; michael@0: SkScalar radius1 = random->nextUScalar1(); michael@0: SkPoint center2; michael@0: SkScalar radius2; michael@0: do { michael@0: center2.set(random->nextUScalar1(), random->nextUScalar1()); michael@0: radius2 = random->nextUScalar1 (); michael@0: // There is a bug in two point radial gradients with identical radii michael@0: } while (radius1 == radius2); michael@0: michael@0: SkColor colors[kMaxRandomGradientColors]; michael@0: SkScalar stopsArray[kMaxRandomGradientColors]; michael@0: SkScalar* stops = stopsArray; michael@0: SkShader::TileMode tm; michael@0: int colorCount = RandomGradientParams(random, colors, &stops, &tm); michael@0: SkAutoTUnref shader(SkGradientShader::CreateTwoPointRadial(center1, radius1, michael@0: center2, radius2, michael@0: colors, stops, colorCount, michael@0: tm)); michael@0: SkPaint paint; michael@0: return shader->asNewEffect(context, paint); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED(factory) michael@0: , fVSVaryingName(NULL) michael@0: , fFSVaryingName(NULL) michael@0: , fCachedCenter(SK_ScalarMax) michael@0: , fCachedRadius(-SK_ScalarMax) michael@0: , fCachedPosRoot(0) { michael@0: michael@0: const GrRadial2Gradient& data = drawEffect.castEffect(); michael@0: fIsDegenerate = data.isDegenerate(); michael@0: } michael@0: michael@0: void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray& coords, michael@0: const TextureSamplerArray& samplers) { michael@0: michael@0: this->emitUniforms(builder, key); michael@0: fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, michael@0: kFloat_GrSLType, "Radial2FSParams", 6); michael@0: michael@0: SkString cName("c"); michael@0: SkString ac4Name("ac4"); michael@0: SkString rootName("root"); michael@0: SkString t; michael@0: SkString p0; michael@0: SkString p1; michael@0: SkString p2; michael@0: SkString p3; michael@0: SkString p4; michael@0: SkString p5; michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); michael@0: builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); michael@0: michael@0: // We interpolate the linear component in coords[1]. michael@0: SkASSERT(coords[0].type() == coords[1].type()); michael@0: const char* coords2D; michael@0: SkString bVar; michael@0: if (kVec3f_GrSLType == coords[0].type()) { michael@0: builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", michael@0: coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); michael@0: coords2D = "interpolants.xy"; michael@0: bVar = "interpolants.z"; michael@0: } else { michael@0: coords2D = coords[0].c_str(); michael@0: bVar.printf("%s.x", coords[1].c_str()); michael@0: } michael@0: michael@0: // c = (x^2)+(y^2) - params[4] michael@0: builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", michael@0: cName.c_str(), coords2D, coords2D, p4.c_str()); michael@0: michael@0: // If we aren't degenerate, emit some extra code, and accept a slightly michael@0: // more complex coord. michael@0: if (!fIsDegenerate) { michael@0: michael@0: // ac4 = 4.0 * params[0] * c michael@0: builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n", michael@0: ac4Name.c_str(), p0.c_str(), michael@0: cName.c_str()); michael@0: michael@0: // root = sqrt(b^2-4ac) michael@0: // (abs to avoid exception due to fp precision) michael@0: builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", michael@0: rootName.c_str(), bVar.c_str(), bVar.c_str(), michael@0: ac4Name.c_str()); michael@0: michael@0: // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] michael@0: t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), michael@0: rootName.c_str(), p1.c_str()); michael@0: } else { michael@0: // t is: -c/b michael@0: t.printf("-%s / %s", cName.c_str(), bVar.c_str()); michael@0: } michael@0: michael@0: this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); michael@0: } michael@0: michael@0: void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, michael@0: const GrDrawEffect& drawEffect) { michael@0: INHERITED::setData(uman, drawEffect); michael@0: const GrRadial2Gradient& data = drawEffect.castEffect(); michael@0: SkASSERT(data.isDegenerate() == fIsDegenerate); michael@0: SkScalar centerX1 = data.center(); michael@0: SkScalar radius0 = data.radius(); michael@0: if (fCachedCenter != centerX1 || michael@0: fCachedRadius != radius0 || michael@0: fCachedPosRoot != data.isPosRoot()) { michael@0: michael@0: SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1; michael@0: michael@0: // When we're in the degenerate (linear) case, the second michael@0: // value will be INF but the program doesn't read it. (We michael@0: // use the same 6 uniforms even though we don't need them michael@0: // all in the linear case just to keep the code complexity michael@0: // down). michael@0: float values[6] = { michael@0: SkScalarToFloat(a), michael@0: 1 / (2.f * SkScalarToFloat(a)), michael@0: SkScalarToFloat(centerX1), michael@0: SkScalarToFloat(radius0), michael@0: SkScalarToFloat(SkScalarMul(radius0, radius0)), michael@0: data.isPosRoot() ? 1.f : -1.f michael@0: }; michael@0: michael@0: uman.set1fv(fParamUni, 6, values); michael@0: fCachedCenter = centerX1; michael@0: fCachedRadius = radius0; michael@0: fCachedPosRoot = data.isPosRoot(); michael@0: } michael@0: } michael@0: michael@0: GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: enum { michael@0: kIsDegenerate = 1 << kBaseKeyBitCnt, michael@0: }; michael@0: michael@0: EffectKey key = GenBaseGradientKey(drawEffect); michael@0: if (drawEffect.castEffect().isDegenerate()) { michael@0: key |= kIsDegenerate; michael@0: } michael@0: return key; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////// michael@0: michael@0: GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const { michael@0: SkASSERT(NULL != context); michael@0: // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis. michael@0: SkMatrix matrix; michael@0: if (!this->getLocalMatrix().invert(&matrix)) { michael@0: return NULL; michael@0: } michael@0: matrix.postConcat(fPtsToUnit); michael@0: michael@0: SkScalar diffLen = fDiff.length(); michael@0: if (0 != diffLen) { michael@0: SkScalar invDiffLen = SkScalarInvert(diffLen); michael@0: SkMatrix rot; michael@0: rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY), michael@0: SkScalarMul(invDiffLen, fDiff.fX)); michael@0: matrix.postConcat(rot); michael@0: } michael@0: michael@0: return GrRadial2Gradient::Create(context, *this, matrix, fTileMode); michael@0: } michael@0: michael@0: #else michael@0: michael@0: GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const { michael@0: SkDEBUGFAIL("Should not call in GPU-less build"); michael@0: return NULL; michael@0: } michael@0: michael@0: #endif