Bug 718849; match radial gradients to spec. r=bas

This commit is contained in:
Matt Woodrow 2012-06-18 15:06:22 +12:00
parent 27a424af1c
commit 52bee18527

View File

@ -852,6 +852,9 @@ bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
fFlags |= SkShader::kConstInY16_Flag; fFlags |= SkShader::kConstInY16_Flag;
} }
} }
if (fStart == fEnd) {
fFlags &= ~kOpaqueAlpha_Flag;
}
return true; return true;
} }
@ -981,6 +984,11 @@ void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
int toggle = 0; int toggle = 0;
#endif #endif
if (fStart == fEnd) {
sk_bzero(dstC, count * sizeof(*dstC));
return;
}
if (fDstToIndexClass != kPerspective_MatrixClass) { if (fDstToIndexClass != kPerspective_MatrixClass) {
dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
@ -1174,6 +1182,11 @@ void Linear_Gradient::shadeSpan16(int x, int y,
const uint16_t* SK_RESTRICT cache = this->getCache16(); const uint16_t* SK_RESTRICT cache = this->getCache16();
int toggle = ((x ^ y) & 1) * kDitherStride16; int toggle = ((x ^ y) & 1) * kDitherStride16;
if (fStart == fEnd) {
sk_bzero(dstC, count * sizeof(*dstC));
return;
}
if (fDstToIndexClass != kPerspective_MatrixClass) { if (fDstToIndexClass != kPerspective_MatrixClass) {
dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
SkIntToScalar(y) + SK_ScalarHalf, &srcPt); SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
@ -1744,11 +1757,15 @@ void Radial_Gradient::shadeSpan(int x, int y,
segment). If a>0, the start circle falls at least partially segment). If a>0, the start circle falls at least partially
outside the end circle (or vice versa), and the gradient outside the end circle (or vice versa), and the gradient
defines a "tube" where a point may be on one circle (on the defines a "tube" where a point may be on one circle (on the
inside of the tube) or the other (outside of the tube). We choose inside of the tube) or the other (outside of the tube). We choose
one arbitrarily. the one with the highest t value, as long as the radius that it
corresponds to is >=0. In the case where neither root has a positive
radius, we don't draw anything.
XXXmattwoodrow: I've removed this for now since it breaks
down when Dr == 0. Is there something else we can do instead?
In order to keep the math to within the limits of fixed point, In order to keep the math to within the limits of fixed point,
we divide the entire quadratic by Dr^2, and replace we divide the entire quadratic by Dr, and replace
(x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
[Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
@ -1768,46 +1785,61 @@ void Radial_Gradient::shadeSpan(int x, int y,
namespace { namespace {
inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, inline bool two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
SkScalar sr2d2, SkScalar foura, SkScalar sr2d2, SkScalar foura,
SkScalar oneOverTwoA, bool posRoot) { SkScalar oneOverTwoA, SkScalar diffRadius,
SkScalar startRadius, SkFixed& t) {
SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
if (0 == foura) { if (0 == foura) {
return SkScalarToFixed(SkScalarDiv(-c, b)); SkScalar result = SkScalarDiv(-c, b);
if (result * diffRadius + startRadius >= 0) {
t = SkScalarToFixed(result);
return true;
}
return false;
} }
SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
if (discrim < 0) { if (discrim < 0) {
discrim = -discrim; return false;
} }
SkScalar rootDiscrim = SkScalarSqrt(discrim); SkScalar rootDiscrim = SkScalarSqrt(discrim);
SkScalar result;
if (posRoot) { // Make sure the results corresponds to a positive radius.
result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); SkScalar result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
} else { if (result * diffRadius + startRadius >= 0) {
result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); t = SkScalarToFixed(result);
return true;
} }
return SkScalarToFixed(result); result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
if (result * diffRadius + startRadius >= 0) {
t = SkScalarToFixed(result);
return true;
}
return false;
} }
typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
SkScalar fy, SkScalar dy, SkScalar fy, SkScalar dy,
SkScalar b, SkScalar db, SkScalar b, SkScalar db,
SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
SkScalar fDiffRadius, SkScalar fRadius1,
SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
int count); int count);
void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
SkScalar fy, SkScalar dy, SkScalar fy, SkScalar dy,
SkScalar b, SkScalar db, SkScalar b, SkScalar db,
SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
SkScalar fDiffRadius, SkScalar fRadius1,
SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
int count) { int count) {
for (; count > 0; --count) { for (; count > 0; --count) {
SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, SkFixed t;
fOneOverTwoA, posRoot); if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
*(dstC++) = 0;
if (t < 0) { } else if (t < 0) {
*dstC++ = cache[-1]; *dstC++ = cache[-1];
} else if (t > 0xFFFF) { } else if (t > 0xFFFF) {
*dstC++ = cache[Gradient_Shader::kCache32Count * 2]; *dstC++ = cache[Gradient_Shader::kCache32Count * 2];
@ -1824,15 +1856,19 @@ void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
SkScalar fy, SkScalar dy, SkScalar fy, SkScalar dy,
SkScalar b, SkScalar db, SkScalar b, SkScalar db,
SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
SkScalar fDiffRadius, SkScalar fRadius1,
SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
int count) { int count) {
for (; count > 0; --count) { for (; count > 0; --count) {
SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, SkFixed t;
fOneOverTwoA, posRoot); if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
SkFixed index = mirror_tileproc(t); *(dstC++) = 0;
SkASSERT(index <= 0xFFFF); } else {
*dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; SkFixed index = mirror_tileproc(t);
SkASSERT(index <= 0xFFFF);
*dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)];
}
fx += dx; fx += dx;
fy += dy; fy += dy;
b += db; b += db;
@ -1842,15 +1878,19 @@ void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
SkScalar fy, SkScalar dy, SkScalar fy, SkScalar dy,
SkScalar b, SkScalar db, SkScalar b, SkScalar db,
SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA,
SkScalar fDiffRadius, SkScalar fRadius1,
SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
int count) { int count) {
for (; count > 0; --count) { for (; count > 0; --count) {
SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, SkFixed t;
fOneOverTwoA, posRoot); if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
SkFixed index = repeat_tileproc(t); *(dstC++) = 0;
SkASSERT(index <= 0xFFFF); } else {
*dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; SkFixed index = repeat_tileproc(t);
SkASSERT(index <= 0xFFFF);
*dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)];
}
fx += dx; fx += dx;
fy += dy; fy += dy;
b += db; b += db;
@ -1940,7 +1980,6 @@ public:
const SkPMColor* SK_RESTRICT cache = this->getCache32(); const SkPMColor* SK_RESTRICT cache = this->getCache32();
SkScalar foura = fA * 4; SkScalar foura = fA * 4;
bool posRoot = fDiffRadius < 0;
if (fDstToIndexClass != kPerspective_MatrixClass) { if (fDstToIndexClass != kPerspective_MatrixClass) {
SkPoint srcPt; SkPoint srcPt;
dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
@ -1959,7 +1998,7 @@ public:
dy = fDstToIndex.getSkewY(); dy = fDstToIndex.getSkewY();
} }
SkScalar b = (SkScalarMul(fDiff.fX, fx) + SkScalar b = (SkScalarMul(fDiff.fX, fx) +
SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; SkScalarMul(fDiff.fY, fy) - fStartRadius * fDiffRadius) * 2;
SkScalar db = (SkScalarMul(fDiff.fX, dx) + SkScalar db = (SkScalarMul(fDiff.fX, dx) +
SkScalarMul(fDiff.fY, dy)) * 2; SkScalarMul(fDiff.fY, dy)) * 2;
@ -1972,7 +2011,7 @@ public:
SkASSERT(proc == repeat_tileproc); SkASSERT(proc == repeat_tileproc);
} }
(*shadeProc)(fx, dx, fy, dy, b, db, (*shadeProc)(fx, dx, fy, dy, b, db,
fSr2D2, foura, fOneOverTwoA, posRoot, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1,
dstC, cache, count); dstC, cache, count);
} else { // perspective case } else { // perspective case
SkScalar dstX = SkIntToScalar(x); SkScalar dstX = SkIntToScalar(x);
@ -1984,11 +2023,14 @@ public:
SkScalar fy = srcPt.fY; SkScalar fy = srcPt.fY;
SkScalar b = (SkScalarMul(fDiff.fX, fx) + SkScalar b = (SkScalarMul(fDiff.fX, fx) +
SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, SkFixed t;
fOneOverTwoA, posRoot); if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) {
SkFixed index = proc(t); *(dstC++) = 0;
SkASSERT(index <= 0xFFFF); } else {
*dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; SkFixed index = proc(t);
SkASSERT(index <= 0xFFFF);
*dstC++ = cache[index >> (16 - kCache32Bits)];
}
dstX += SK_Scalar1; dstX += SK_Scalar1;
} }
} }
@ -2003,6 +2045,12 @@ public:
// we don't have a span16 proc // we don't have a span16 proc
fFlags &= ~kHasSpan16_Flag; fFlags &= ~kHasSpan16_Flag;
// If we might end up wanting to draw nothing as part of the gradient
// then we should mark ourselves as not being opaque.
if (fA >= 0 || (fDiffRadius == 0 && fCenter1 == fCenter2)) {
fFlags &= ~kOpaqueAlpha_Flag;
}
return true; return true;
} }
@ -2038,16 +2086,12 @@ private:
void init() { void init() {
fDiff = fCenter1 - fCenter2; fDiff = fCenter1 - fCenter2;
fDiffRadius = fRadius2 - fRadius1; fDiffRadius = fRadius2 - fRadius1;
SkScalar inv = SkScalarInvert(fDiffRadius); fStartRadius = fRadius1;
fDiff.fX = SkScalarMul(fDiff.fX, inv);
fDiff.fY = SkScalarMul(fDiff.fY, inv);
fStartRadius = SkScalarMul(fRadius1, inv);
fSr2D2 = SkScalarSquare(fStartRadius); fSr2D2 = SkScalarSquare(fStartRadius);
fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SkScalarSquare(fDiffRadius);
fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
fPtsToUnit.postScale(inv, inv);
} }
}; };
@ -2493,6 +2537,10 @@ SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
} }
EXPAND_1_COLOR(colorCount); EXPAND_1_COLOR(colorCount);
if (start == end && startRadius == 0) {
return CreateRadial(start, endRadius, colors, pos, colorCount, mode, mapper);
}
return SkNEW_ARGS(Two_Point_Radial_Gradient, return SkNEW_ARGS(Two_Point_Radial_Gradient,
(start, startRadius, end, endRadius, colors, pos, (start, startRadius, end, endRadius, colors, pos,
colorCount, mode, mapper)); colorCount, mode, mapper));