michael@0: # HG changeset patch michael@0: # User Matt Woodrow michael@0: # Date 1339988782 -43200 michael@0: # Node ID 1e9dae659ee6c992f719fd4136efbcc5410ded37 michael@0: # Parent 946750f6d95febd199fb7b748e9d2c48fd01c8a6 michael@0: [mq]: skia-windows-gradients michael@0: michael@0: diff --git a/gfx/skia/src/effects/SkGradientShader.cpp b/gfx/skia/src/effects/SkGradientShader.cpp michael@0: --- a/gfx/skia/src/effects/SkGradientShader.cpp michael@0: +++ b/gfx/skia/src/effects/SkGradientShader.cpp michael@0: @@ -847,16 +847,19 @@ bool Linear_Gradient::setContext(const S michael@0: fFlags |= SkShader::kConstInY32_Flag; michael@0: if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) { michael@0: // only claim this if we do have a 16bit mode (i.e. none of our michael@0: // colors have alpha), and if we are not dithering (which obviously michael@0: // is not const in Y). michael@0: fFlags |= SkShader::kConstInY16_Flag; michael@0: } michael@0: } michael@0: + if (fStart == fEnd) { michael@0: + fFlags &= ~kOpaqueAlpha_Flag; michael@0: + } michael@0: return true; michael@0: } michael@0: michael@0: #define NO_CHECK_ITER \ michael@0: do { \ michael@0: unsigned fi = fx >> Gradient_Shader::kCache32Shift; \ michael@0: SkASSERT(fi <= 0xFF); \ michael@0: fx += dx; \ michael@0: @@ -976,16 +979,21 @@ void Linear_Gradient::shadeSpan(int x, i michael@0: TileProc proc = fTileProc; michael@0: const SkPMColor* SK_RESTRICT cache = this->getCache32(); michael@0: #ifdef USE_DITHER_32BIT_GRADIENT michael@0: int toggle = ((x ^ y) & 1) * kDitherStride32; michael@0: #else michael@0: int toggle = 0; michael@0: #endif michael@0: michael@0: + if (fStart == fEnd) { michael@0: + sk_bzero(dstC, count * sizeof(*dstC)); michael@0: + return; michael@0: + } michael@0: + michael@0: if (fDstToIndexClass != kPerspective_MatrixClass) { michael@0: dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, michael@0: SkIntToScalar(y) + SK_ScalarHalf, &srcPt); michael@0: SkFixed dx, fx = SkScalarToFixed(srcPt.fX); michael@0: michael@0: if (fDstToIndexClass == kFixedStepInX_MatrixClass) { michael@0: SkFixed dxStorage[1]; michael@0: (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); michael@0: @@ -1169,16 +1177,21 @@ void Linear_Gradient::shadeSpan16(int x, michael@0: SkASSERT(count > 0); michael@0: michael@0: SkPoint srcPt; michael@0: SkMatrix::MapXYProc dstProc = fDstToIndexProc; michael@0: TileProc proc = fTileProc; michael@0: const uint16_t* SK_RESTRICT cache = this->getCache16(); michael@0: int toggle = ((x ^ y) & 1) * kDitherStride16; michael@0: michael@0: + if (fStart == fEnd) { michael@0: + sk_bzero(dstC, count * sizeof(*dstC)); michael@0: + return; michael@0: + } michael@0: + michael@0: if (fDstToIndexClass != kPerspective_MatrixClass) { michael@0: dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, michael@0: SkIntToScalar(y) + SK_ScalarHalf, &srcPt); michael@0: SkFixed dx, fx = SkScalarToFixed(srcPt.fX); michael@0: michael@0: if (fDstToIndexClass == kFixedStepInX_MatrixClass) { michael@0: SkFixed dxStorage[1]; michael@0: (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); michael@0: @@ -1739,21 +1752,25 @@ void Radial_Gradient::shadeSpan(int x, i 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: + inside of the tube) or the other (outside of the tube). We choose michael@0: + the one with the highest t value, as long as the radius that it michael@0: + corresponds to is >=0. In the case where neither root has a positive michael@0: + radius, we don't draw anything. michael@0: michael@0: + XXXmattwoodrow: I've removed this for now since it breaks michael@0: + down when Dr == 0. Is there something else we can do instead? 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: + we divide the entire quadratic by Dr, 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: @@ -1763,99 +1780,122 @@ void Radial_Gradient::shadeSpan(int x, i 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: +inline bool two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, michael@0: + SkScalar sr2d2, SkScalar foura, michael@0: + SkScalar oneOverTwoA, SkScalar diffRadius, michael@0: + SkScalar startRadius, SkFixed& t) { michael@0: SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; michael@0: if (0 == foura) { michael@0: - return SkScalarToFixed(SkScalarDiv(-c, b)); michael@0: + SkScalar result = SkScalarDiv(-c, b); michael@0: + if (result * diffRadius + startRadius >= 0) { michael@0: + t = SkScalarToFixed(result); michael@0: + return true; michael@0: + } michael@0: + return false; michael@0: } michael@0: michael@0: SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); michael@0: if (discrim < 0) { michael@0: - discrim = -discrim; michael@0: + return false; 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: + // Make sure the results corresponds to a positive radius. michael@0: + SkScalar result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); michael@0: + if (result * diffRadius + startRadius >= 0) { michael@0: + t = SkScalarToFixed(result); michael@0: + return true; michael@0: } michael@0: - return SkScalarToFixed(result); michael@0: + result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); michael@0: + if (result * diffRadius + startRadius >= 0) { michael@0: + t = SkScalarToFixed(result); michael@0: + return true; michael@0: + } michael@0: + michael@0: + return false; 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: + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, michael@0: + SkScalar fDiffRadius, SkScalar fRadius1, 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: + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, michael@0: + SkScalar fDiffRadius, SkScalar fRadius1, 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: - michael@0: - if (t < 0) { michael@0: + SkFixed t; michael@0: + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { michael@0: + *(dstC++) = 0; michael@0: + } else if (t < 0) { michael@0: *dstC++ = cache[-1]; michael@0: } else if (t > 0xFFFF) { michael@0: *dstC++ = cache[Gradient_Shader::kCache32Count * 2]; michael@0: } else { michael@0: SkASSERT(t <= 0xFFFF); michael@0: *dstC++ = cache[t >> Gradient_Shader::kCache32Shift]; michael@0: } michael@0: 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: + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, michael@0: + SkScalar fDiffRadius, SkScalar fRadius1, 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 >> Gradient_Shader::kCache32Shift]; michael@0: + SkFixed t; michael@0: + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { michael@0: + *(dstC++) = 0; michael@0: + } else { michael@0: + SkFixed index = mirror_tileproc(t); michael@0: + SkASSERT(index <= 0xFFFF); michael@0: + *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)]; michael@0: + } 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: + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, michael@0: + SkScalar fDiffRadius, SkScalar fRadius1, 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 >> Gradient_Shader::kCache32Shift]; michael@0: + SkFixed t; michael@0: + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { michael@0: + *(dstC++) = 0; michael@0: + } else { michael@0: + SkFixed index = repeat_tileproc(t); michael@0: + SkASSERT(index <= 0xFFFF); michael@0: + *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)]; michael@0: + } michael@0: fx += dx; michael@0: fy += dy; michael@0: b += db; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: @@ -1935,17 +1975,16 @@ public: 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: @@ -1954,60 +1993,69 @@ public: 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: + SkScalarMul(fDiff.fY, fy) - fStartRadius * fDiffRadius) * 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 (proc == clamp_tileproc) { michael@0: shadeProc = shadeSpan_twopoint_clamp; michael@0: } else if (proc == mirror_tileproc) { michael@0: shadeProc = shadeSpan_twopoint_mirror; michael@0: } else { michael@0: SkASSERT(proc == repeat_tileproc); michael@0: } michael@0: (*shadeProc)(fx, dx, fy, dy, b, db, michael@0: - fSr2D2, foura, fOneOverTwoA, posRoot, michael@0: + fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, 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 >> Gradient_Shader::kCache32Shift]; michael@0: + SkFixed t; michael@0: + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { michael@0: + *(dstC++) = 0; michael@0: + } else { michael@0: + SkFixed index = proc(t); michael@0: + SkASSERT(index <= 0xFFFF); michael@0: + *dstC++ = cache[index >> (16 - kCache32Bits)]; michael@0: + } michael@0: dstX += SK_Scalar1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: virtual bool setContext(const SkBitmap& device, michael@0: const SkPaint& paint, michael@0: const SkMatrix& matrix) SK_OVERRIDE { 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: + michael@0: + // If we might end up wanting to draw nothing as part of the gradient michael@0: + // then we should mark ourselves as not being opaque. michael@0: + if (fA >= 0 || (fDiffRadius == 0 && fCenter1 == fCenter2)) { michael@0: + fFlags &= ~kOpaqueAlpha_Flag; michael@0: + } michael@0: return true; michael@0: } michael@0: michael@0: SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient) michael@0: michael@0: protected: michael@0: Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) michael@0: : INHERITED(buffer), michael@0: @@ -2033,26 +2081,22 @@ private: michael@0: const SkScalar fRadius1; michael@0: const SkScalar fRadius2; michael@0: SkPoint fDiff; michael@0: SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA; michael@0: michael@0: void init() { michael@0: fDiff = fCenter1 - fCenter2; michael@0: fDiffRadius = fRadius2 - fRadius1; michael@0: - SkScalar inv = SkScalarInvert(fDiffRadius); michael@0: - fDiff.fX = SkScalarMul(fDiff.fX, inv); michael@0: - fDiff.fY = SkScalarMul(fDiff.fY, inv); michael@0: - fStartRadius = SkScalarMul(fRadius1, inv); michael@0: + fStartRadius = fRadius1; michael@0: fSr2D2 = SkScalarSquare(fStartRadius); michael@0: - fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; michael@0: + fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SkScalarSquare(fDiffRadius); 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: michael@0: class Sweep_Gradient : public Gradient_Shader { michael@0: public: michael@0: Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[], michael@0: @@ -2488,16 +2532,20 @@ SkShader* SkGradientShader::CreateTwoPoi michael@0: int colorCount, michael@0: SkShader::TileMode mode, michael@0: SkUnitMapper* mapper) { michael@0: if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) { michael@0: return NULL; michael@0: } michael@0: EXPAND_1_COLOR(colorCount); michael@0: michael@0: + if (start == end && startRadius == 0) { michael@0: + return CreateRadial(start, endRadius, colors, pos, colorCount, mode, mapper); michael@0: + } michael@0: + michael@0: return SkNEW_ARGS(Two_Point_Radial_Gradient, michael@0: (start, startRadius, end, endRadius, colors, pos, michael@0: colorCount, mode, mapper)); michael@0: } michael@0: michael@0: SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy, michael@0: const SkColor colors[], michael@0: const SkScalar pos[],