0016-Bug-718849-Radial-gradients.patch (16295B)
1 # HG changeset patch 2 # User Matt Woodrow <mwoodrow@mozilla.com> 3 # Date 1339988782 -43200 4 # Node ID 1e9dae659ee6c992f719fd4136efbcc5410ded37 5 # Parent 946750f6d95febd199fb7b748e9d2c48fd01c8a6 6 [mq]: skia-windows-gradients 7 8 diff --git a/gfx/skia/src/effects/SkGradientShader.cpp b/gfx/skia/src/effects/SkGradientShader.cpp 9 --- a/gfx/skia/src/effects/SkGradientShader.cpp 10 +++ b/gfx/skia/src/effects/SkGradientShader.cpp 11 @@ -847,16 +847,19 @@ bool Linear_Gradient::setContext(const S 12 fFlags |= SkShader::kConstInY32_Flag; 13 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) { 14 // only claim this if we do have a 16bit mode (i.e. none of our 15 // colors have alpha), and if we are not dithering (which obviously 16 // is not const in Y). 17 fFlags |= SkShader::kConstInY16_Flag; 18 } 19 } 20 + if (fStart == fEnd) { 21 + fFlags &= ~kOpaqueAlpha_Flag; 22 + } 23 return true; 24 } 25 26 #define NO_CHECK_ITER \ 27 do { \ 28 unsigned fi = fx >> Gradient_Shader::kCache32Shift; \ 29 SkASSERT(fi <= 0xFF); \ 30 fx += dx; \ 31 @@ -976,16 +979,21 @@ void Linear_Gradient::shadeSpan(int x, i 32 TileProc proc = fTileProc; 33 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 34 #ifdef USE_DITHER_32BIT_GRADIENT 35 int toggle = ((x ^ y) & 1) * kDitherStride32; 36 #else 37 int toggle = 0; 38 #endif 39 40 + if (fStart == fEnd) { 41 + sk_bzero(dstC, count * sizeof(*dstC)); 42 + return; 43 + } 44 + 45 if (fDstToIndexClass != kPerspective_MatrixClass) { 46 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 47 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 48 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 49 50 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 51 SkFixed dxStorage[1]; 52 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 53 @@ -1169,16 +1177,21 @@ void Linear_Gradient::shadeSpan16(int x, 54 SkASSERT(count > 0); 55 56 SkPoint srcPt; 57 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 58 TileProc proc = fTileProc; 59 const uint16_t* SK_RESTRICT cache = this->getCache16(); 60 int toggle = ((x ^ y) & 1) * kDitherStride16; 61 62 + if (fStart == fEnd) { 63 + sk_bzero(dstC, count * sizeof(*dstC)); 64 + return; 65 + } 66 + 67 if (fDstToIndexClass != kPerspective_MatrixClass) { 68 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 69 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 70 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 71 72 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 73 SkFixed dxStorage[1]; 74 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 75 @@ -1739,21 +1752,25 @@ void Radial_Gradient::shadeSpan(int x, i 76 possible circles on which the point may fall. Solving for t yields 77 the gradient value to use. 78 79 If a<0, the start circle is entirely contained in the 80 end circle, and one of the roots will be <0 or >1 (off the line 81 segment). If a>0, the start circle falls at least partially 82 outside the end circle (or vice versa), and the gradient 83 defines a "tube" where a point may be on one circle (on the 84 - inside of the tube) or the other (outside of the tube). We choose 85 - one arbitrarily. 86 + inside of the tube) or the other (outside of the tube). We choose 87 + the one with the highest t value, as long as the radius that it 88 + corresponds to is >=0. In the case where neither root has a positive 89 + radius, we don't draw anything. 90 91 + XXXmattwoodrow: I've removed this for now since it breaks 92 + down when Dr == 0. Is there something else we can do instead? 93 In order to keep the math to within the limits of fixed point, 94 - we divide the entire quadratic by Dr^2, and replace 95 + we divide the entire quadratic by Dr, and replace 96 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving 97 98 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2 99 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t 100 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0 101 102 (x' and y' are computed by appending the subtract and scale to the 103 fDstToIndex matrix in the constructor). 104 @@ -1763,99 +1780,122 @@ void Radial_Gradient::shadeSpan(int x, i 105 x' and y', if x and y are linear in the span, 'B' can be computed 106 incrementally with a simple delta (db below). If it is not (e.g., 107 a perspective projection), it must be computed in the loop. 108 109 */ 110 111 namespace { 112 113 -inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, 114 - SkScalar sr2d2, SkScalar foura, 115 - SkScalar oneOverTwoA, bool posRoot) { 116 +inline bool two_point_radial(SkScalar b, SkScalar fx, SkScalar fy, 117 + SkScalar sr2d2, SkScalar foura, 118 + SkScalar oneOverTwoA, SkScalar diffRadius, 119 + SkScalar startRadius, SkFixed& t) { 120 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2; 121 if (0 == foura) { 122 - return SkScalarToFixed(SkScalarDiv(-c, b)); 123 + SkScalar result = SkScalarDiv(-c, b); 124 + if (result * diffRadius + startRadius >= 0) { 125 + t = SkScalarToFixed(result); 126 + return true; 127 + } 128 + return false; 129 } 130 131 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c); 132 if (discrim < 0) { 133 - discrim = -discrim; 134 + return false; 135 } 136 SkScalar rootDiscrim = SkScalarSqrt(discrim); 137 - SkScalar result; 138 - if (posRoot) { 139 - result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); 140 - } else { 141 - result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); 142 + 143 + // Make sure the results corresponds to a positive radius. 144 + SkScalar result = SkScalarMul(-b + rootDiscrim, oneOverTwoA); 145 + if (result * diffRadius + startRadius >= 0) { 146 + t = SkScalarToFixed(result); 147 + return true; 148 } 149 - return SkScalarToFixed(result); 150 + result = SkScalarMul(-b - rootDiscrim, oneOverTwoA); 151 + if (result * diffRadius + startRadius >= 0) { 152 + t = SkScalarToFixed(result); 153 + return true; 154 + } 155 + 156 + return false; 157 } 158 159 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx, 160 SkScalar fy, SkScalar dy, 161 SkScalar b, SkScalar db, 162 - SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 163 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, 164 + SkScalar fDiffRadius, SkScalar fRadius1, 165 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 166 int count); 167 168 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx, 169 SkScalar fy, SkScalar dy, 170 SkScalar b, SkScalar db, 171 - SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 172 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, 173 + SkScalar fDiffRadius, SkScalar fRadius1, 174 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 175 int count) { 176 for (; count > 0; --count) { 177 - SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 178 - fOneOverTwoA, posRoot); 179 - 180 - if (t < 0) { 181 + SkFixed t; 182 + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { 183 + *(dstC++) = 0; 184 + } else if (t < 0) { 185 *dstC++ = cache[-1]; 186 } else if (t > 0xFFFF) { 187 *dstC++ = cache[Gradient_Shader::kCache32Count * 2]; 188 } else { 189 SkASSERT(t <= 0xFFFF); 190 *dstC++ = cache[t >> Gradient_Shader::kCache32Shift]; 191 } 192 193 fx += dx; 194 fy += dy; 195 b += db; 196 } 197 } 198 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx, 199 SkScalar fy, SkScalar dy, 200 SkScalar b, SkScalar db, 201 - SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 202 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, 203 + SkScalar fDiffRadius, SkScalar fRadius1, 204 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 205 int count) { 206 for (; count > 0; --count) { 207 - SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 208 - fOneOverTwoA, posRoot); 209 - SkFixed index = mirror_tileproc(t); 210 - SkASSERT(index <= 0xFFFF); 211 - *dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; 212 + SkFixed t; 213 + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { 214 + *(dstC++) = 0; 215 + } else { 216 + SkFixed index = mirror_tileproc(t); 217 + SkASSERT(index <= 0xFFFF); 218 + *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)]; 219 + } 220 fx += dx; 221 fy += dy; 222 b += db; 223 } 224 } 225 226 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx, 227 SkScalar fy, SkScalar dy, 228 SkScalar b, SkScalar db, 229 - SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot, 230 + SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, 231 + SkScalar fDiffRadius, SkScalar fRadius1, 232 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 233 int count) { 234 for (; count > 0; --count) { 235 - SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 236 - fOneOverTwoA, posRoot); 237 - SkFixed index = repeat_tileproc(t); 238 - SkASSERT(index <= 0xFFFF); 239 - *dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; 240 + SkFixed t; 241 + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { 242 + *(dstC++) = 0; 243 + } else { 244 + SkFixed index = repeat_tileproc(t); 245 + SkASSERT(index <= 0xFFFF); 246 + *dstC++ = cache[index >> (16 - Gradient_Shader::kCache32Shift)]; 247 + } 248 fx += dx; 249 fy += dy; 250 b += db; 251 } 252 } 253 254 255 256 @@ -1935,17 +1975,16 @@ public: 257 sk_bzero(dstC, count * sizeof(*dstC)); 258 return; 259 } 260 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 261 TileProc proc = fTileProc; 262 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 263 264 SkScalar foura = fA * 4; 265 - bool posRoot = fDiffRadius < 0; 266 if (fDstToIndexClass != kPerspective_MatrixClass) { 267 SkPoint srcPt; 268 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 269 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 270 SkScalar dx, fx = srcPt.fX; 271 SkScalar dy, fy = srcPt.fY; 272 273 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 274 @@ -1954,60 +1993,69 @@ public: 275 dx = SkFixedToScalar(fixedX); 276 dy = SkFixedToScalar(fixedY); 277 } else { 278 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 279 dx = fDstToIndex.getScaleX(); 280 dy = fDstToIndex.getSkewY(); 281 } 282 SkScalar b = (SkScalarMul(fDiff.fX, fx) + 283 - SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 284 + SkScalarMul(fDiff.fY, fy) - fStartRadius * fDiffRadius) * 2; 285 SkScalar db = (SkScalarMul(fDiff.fX, dx) + 286 SkScalarMul(fDiff.fY, dy)) * 2; 287 288 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat; 289 if (proc == clamp_tileproc) { 290 shadeProc = shadeSpan_twopoint_clamp; 291 } else if (proc == mirror_tileproc) { 292 shadeProc = shadeSpan_twopoint_mirror; 293 } else { 294 SkASSERT(proc == repeat_tileproc); 295 } 296 (*shadeProc)(fx, dx, fy, dy, b, db, 297 - fSr2D2, foura, fOneOverTwoA, posRoot, 298 + fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, 299 dstC, cache, count); 300 } else { // perspective case 301 SkScalar dstX = SkIntToScalar(x); 302 SkScalar dstY = SkIntToScalar(y); 303 for (; count > 0; --count) { 304 SkPoint srcPt; 305 dstProc(fDstToIndex, dstX, dstY, &srcPt); 306 SkScalar fx = srcPt.fX; 307 SkScalar fy = srcPt.fY; 308 SkScalar b = (SkScalarMul(fDiff.fX, fx) + 309 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2; 310 - SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura, 311 - fOneOverTwoA, posRoot); 312 - SkFixed index = proc(t); 313 - SkASSERT(index <= 0xFFFF); 314 - *dstC++ = cache[index >> Gradient_Shader::kCache32Shift]; 315 + SkFixed t; 316 + if (!two_point_radial(b, fx, fy, fSr2D2, foura, fOneOverTwoA, fDiffRadius, fRadius1, t)) { 317 + *(dstC++) = 0; 318 + } else { 319 + SkFixed index = proc(t); 320 + SkASSERT(index <= 0xFFFF); 321 + *dstC++ = cache[index >> (16 - kCache32Bits)]; 322 + } 323 dstX += SK_Scalar1; 324 } 325 } 326 } 327 328 virtual bool setContext(const SkBitmap& device, 329 const SkPaint& paint, 330 const SkMatrix& matrix) SK_OVERRIDE { 331 if (!this->INHERITED::setContext(device, paint, matrix)) { 332 return false; 333 } 334 335 // we don't have a span16 proc 336 fFlags &= ~kHasSpan16_Flag; 337 + 338 + // If we might end up wanting to draw nothing as part of the gradient 339 + // then we should mark ourselves as not being opaque. 340 + if (fA >= 0 || (fDiffRadius == 0 && fCenter1 == fCenter2)) { 341 + fFlags &= ~kOpaqueAlpha_Flag; 342 + } 343 return true; 344 } 345 346 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Two_Point_Radial_Gradient) 347 348 protected: 349 Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer) 350 : INHERITED(buffer), 351 @@ -2033,26 +2081,22 @@ private: 352 const SkScalar fRadius1; 353 const SkScalar fRadius2; 354 SkPoint fDiff; 355 SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA; 356 357 void init() { 358 fDiff = fCenter1 - fCenter2; 359 fDiffRadius = fRadius2 - fRadius1; 360 - SkScalar inv = SkScalarInvert(fDiffRadius); 361 - fDiff.fX = SkScalarMul(fDiff.fX, inv); 362 - fDiff.fY = SkScalarMul(fDiff.fY, inv); 363 - fStartRadius = SkScalarMul(fRadius1, inv); 364 + fStartRadius = fRadius1; 365 fSr2D2 = SkScalarSquare(fStartRadius); 366 - fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1; 367 + fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SkScalarSquare(fDiffRadius); 368 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0; 369 370 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY); 371 - fPtsToUnit.postScale(inv, inv); 372 } 373 }; 374 375 /////////////////////////////////////////////////////////////////////////////// 376 377 class Sweep_Gradient : public Gradient_Shader { 378 public: 379 Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[], 380 @@ -2488,16 +2532,20 @@ SkShader* SkGradientShader::CreateTwoPoi 381 int colorCount, 382 SkShader::TileMode mode, 383 SkUnitMapper* mapper) { 384 if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) { 385 return NULL; 386 } 387 EXPAND_1_COLOR(colorCount); 388 389 + if (start == end && startRadius == 0) { 390 + return CreateRadial(start, endRadius, colors, pos, colorCount, mode, mapper); 391 + } 392 + 393 return SkNEW_ARGS(Two_Point_Radial_Gradient, 394 (start, startRadius, end, endRadius, colors, pos, 395 colorCount, mode, mapper)); 396 } 397 398 SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy, 399 const SkColor colors[], 400 const SkScalar pos[],