1 /* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkTwoPointConicalGradient.h" 9 #include "SkTwoPointConicalGradient_gpu.h" 10 11 struct TwoPtRadialContext { 12 const TwoPtRadial& fRec; 13 float fRelX, fRelY; 14 const float fIncX, fIncY; 15 float fB; 16 const float fDB; 17 18 TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, 19 SkScalar dfx, SkScalar dfy); 20 SkFixed nextT(); 21 }; 22 23 static int valid_divide(float numer, float denom, float* ratio) { 24 SkASSERT(ratio); 25 if (0 == denom) { 26 return 0; 27 } 28 *ratio = numer / denom; 29 return 1; 30 } 31 32 // Return the number of distinct real roots, and write them into roots[] in 33 // ascending order 34 static int find_quad_roots(float A, float B, float C, float roots[2], bool descendingOrder = false) { 35 SkASSERT(roots); 36 37 if (A == 0) { 38 return valid_divide(-C, B, roots); 39 } 40 41 float R = B*B - 4*A*C; 42 if (R < 0) { 43 return 0; 44 } 45 R = sk_float_sqrt(R); 46 47 #if 1 48 float Q = B; 49 if (Q < 0) { 50 Q -= R; 51 } else { 52 Q += R; 53 } 54 #else 55 // on 10.6 this was much slower than the above branch :( 56 float Q = B + copysignf(R, B); 57 #endif 58 Q *= -0.5f; 59 if (0 == Q) { 60 roots[0] = 0; 61 return 1; 62 } 63 64 float r0 = Q / A; 65 float r1 = C / Q; 66 roots[0] = r0 < r1 ? r0 : r1; 67 roots[1] = r0 > r1 ? r0 : r1; 68 if (descendingOrder) { 69 SkTSwap(roots[0], roots[1]); 70 } 71 return 2; 72 } 73 74 static float lerp(float x, float dx, float t) { 75 return x + t * dx; 76 } 77 78 static float sqr(float x) { return x * x; } 79 80 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0, 81 const SkPoint& center1, SkScalar rad1, 82 bool flipped) { 83 fCenterX = SkScalarToFloat(center0.fX); 84 fCenterY = SkScalarToFloat(center0.fY); 85 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX; 86 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY; 87 fRadius = SkScalarToFloat(rad0); 88 fDRadius = SkScalarToFloat(rad1) - fRadius; 89 90 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius); 91 fRadius2 = sqr(fRadius); 92 fRDR = fRadius * fDRadius; 93 94 fFlipped = flipped; 95 } 96 97 TwoPtRadialContext::TwoPtRadialContext(const TwoPtRadial& rec, SkScalar fx, SkScalar fy, 98 SkScalar dfx, SkScalar dfy) 99 : fRec(rec) 100 , fRelX(SkScalarToFloat(fx) - rec.fCenterX) 101 , fRelY(SkScalarToFloat(fy) - rec.fCenterY) 102 , fIncX(SkScalarToFloat(dfx)) 103 , fIncY(SkScalarToFloat(dfy)) 104 , fB(-2 * (rec.fDCenterX * fRelX + rec.fDCenterY * fRelY + rec.fRDR)) 105 , fDB(-2 * (rec.fDCenterX * fIncX + rec.fDCenterY * fIncY)) {} 106 107 SkFixed TwoPtRadialContext::nextT() { 108 float roots[2]; 109 110 float C = sqr(fRelX) + sqr(fRelY) - fRec.fRadius2; 111 int countRoots = find_quad_roots(fRec.fA, fB, C, roots, fRec.fFlipped); 112 113 fRelX += fIncX; 114 fRelY += fIncY; 115 fB += fDB; 116 117 if (0 == countRoots) { 118 return TwoPtRadial::kDontDrawT; 119 } 120 121 // Prefer the bigger t value if both give a radius(t) > 0 122 // find_quad_roots returns the values sorted, so we start with the last 123 float t = roots[countRoots - 1]; 124 float r = lerp(fRec.fRadius, fRec.fDRadius, t); 125 if (r <= 0) { 126 t = roots[0]; // might be the same as roots[countRoots-1] 127 r = lerp(fRec.fRadius, fRec.fDRadius, t); 128 if (r <= 0) { 129 return TwoPtRadial::kDontDrawT; 130 } 131 } 132 return SkFloatToFixed(t); 133 } 134 135 typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC, 136 const SkPMColor* cache, int toggle, int count); 137 138 static void twopoint_clamp(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, 139 const SkPMColor* SK_RESTRICT cache, int toggle, 140 int count) { 141 for (; count > 0; --count) { 142 SkFixed t = rec->nextT(); 143 if (TwoPtRadial::DontDrawT(t)) { 144 *dstC++ = 0; 145 } else { 146 SkFixed index = SkClampMax(t, 0xFFFF); 147 SkASSERT(index <= 0xFFFF); 148 *dstC++ = cache[toggle + 149 (index >> SkGradientShaderBase::kCache32Shift)]; 150 } 151 toggle = next_dither_toggle(toggle); 152 } 153 } 154 155 static void twopoint_repeat(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, 156 const SkPMColor* SK_RESTRICT cache, int toggle, 157 int count) { 158 for (; count > 0; --count) { 159 SkFixed t = rec->nextT(); 160 if (TwoPtRadial::DontDrawT(t)) { 161 *dstC++ = 0; 162 } else { 163 SkFixed index = repeat_tileproc(t); 164 SkASSERT(index <= 0xFFFF); 165 *dstC++ = cache[toggle + 166 (index >> SkGradientShaderBase::kCache32Shift)]; 167 } 168 toggle = next_dither_toggle(toggle); 169 } 170 } 171 172 static void twopoint_mirror(TwoPtRadialContext* rec, SkPMColor* SK_RESTRICT dstC, 173 const SkPMColor* SK_RESTRICT cache, int toggle, 174 int count) { 175 for (; count > 0; --count) { 176 SkFixed t = rec->nextT(); 177 if (TwoPtRadial::DontDrawT(t)) { 178 *dstC++ = 0; 179 } else { 180 SkFixed index = mirror_tileproc(t); 181 SkASSERT(index <= 0xFFFF); 182 *dstC++ = cache[toggle + 183 (index >> SkGradientShaderBase::kCache32Shift)]; 184 } 185 toggle = next_dither_toggle(toggle); 186 } 187 } 188 189 void SkTwoPointConicalGradient::init() { 190 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad); 191 fPtsToUnit.reset(); 192 } 193 194 ///////////////////////////////////////////////////////////////////// 195 196 SkTwoPointConicalGradient::SkTwoPointConicalGradient( 197 const SkPoint& start, SkScalar startRadius, 198 const SkPoint& end, SkScalar endRadius, 199 bool flippedGrad, const Descriptor& desc, 200 const SkMatrix* localMatrix) 201 : SkGradientShaderBase(desc, localMatrix), 202 fCenter1(start), 203 fCenter2(end), 204 fRadius1(startRadius), 205 fRadius2(endRadius), 206 fFlippedGrad(flippedGrad) { 207 // this is degenerate, and should be caught by our caller 208 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); 209 this->init(); 210 } 211 212 bool SkTwoPointConicalGradient::isOpaque() const { 213 // Because areas outside the cone are left untouched, we cannot treat the 214 // shader as opaque even if the gradient itself is opaque. 215 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 216 return false; 217 } 218 219 size_t SkTwoPointConicalGradient::contextSize() const { 220 return sizeof(TwoPointConicalGradientContext); 221 } 222 223 SkShader::Context* SkTwoPointConicalGradient::onCreateContext(const ContextRec& rec, 224 void* storage) const { 225 return SkNEW_PLACEMENT_ARGS(storage, TwoPointConicalGradientContext, (*this, rec)); 226 } 227 228 SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext( 229 const SkTwoPointConicalGradient& shader, const ContextRec& rec) 230 : INHERITED(shader, rec) 231 { 232 // we don't have a span16 proc 233 fFlags &= ~kHasSpan16_Flag; 234 235 // in general, we might discard based on computed-radius, so clear 236 // this flag (todo: sometimes we can detect that we never discard...) 237 fFlags &= ~kOpaqueAlpha_Flag; 238 } 239 240 void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan( 241 int x, int y, SkPMColor* dstCParam, int count) { 242 const SkTwoPointConicalGradient& twoPointConicalGradient = 243 static_cast<const SkTwoPointConicalGradient&>(fShader); 244 245 int toggle = init_dither_toggle(x, y); 246 247 SkASSERT(count > 0); 248 249 SkPMColor* SK_RESTRICT dstC = dstCParam; 250 251 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 252 253 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 254 255 TwoPointConicalProc shadeProc = twopoint_repeat; 256 if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) { 257 shadeProc = twopoint_clamp; 258 } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) { 259 shadeProc = twopoint_mirror; 260 } else { 261 SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode); 262 } 263 264 if (fDstToIndexClass != kPerspective_MatrixClass) { 265 SkPoint srcPt; 266 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 267 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 268 SkScalar dx, fx = srcPt.fX; 269 SkScalar dy, fy = srcPt.fY; 270 271 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 272 SkFixed fixedX, fixedY; 273 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 274 dx = SkFixedToScalar(fixedX); 275 dy = SkFixedToScalar(fixedY); 276 } else { 277 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 278 dx = fDstToIndex.getScaleX(); 279 dy = fDstToIndex.getSkewY(); 280 } 281 282 TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy); 283 (*shadeProc)(&rec, dstC, cache, toggle, count); 284 } else { // perspective case 285 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf; 286 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf; 287 for (; count > 0; --count) { 288 SkPoint srcPt; 289 dstProc(fDstToIndex, dstX, dstY, &srcPt); 290 TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0); 291 (*shadeProc)(&rec, dstC, cache, toggle, 1); 292 293 dstX += SK_Scalar1; 294 toggle = next_dither_toggle(toggle); 295 dstC += 1; 296 } 297 } 298 } 299 300 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap( 301 SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const { 302 SkPoint diff = fCenter2 - fCenter1; 303 SkScalar diffLen = 0; 304 305 if (bitmap) { 306 this->getGradientTableBitmap(bitmap); 307 } 308 if (matrix) { 309 diffLen = diff.length(); 310 } 311 if (matrix) { 312 if (diffLen) { 313 SkScalar invDiffLen = SkScalarInvert(diffLen); 314 // rotate to align circle centers with the x-axis 315 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY), 316 SkScalarMul(invDiffLen, diff.fX)); 317 } else { 318 matrix->reset(); 319 } 320 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY); 321 } 322 if (xy) { 323 xy[0] = fTileMode; 324 xy[1] = kClamp_TileMode; 325 } 326 return kTwoPointConical_BitmapType; 327 } 328 329 // Returns the original non-sorted version of the gradient 330 SkShader::GradientType SkTwoPointConicalGradient::asAGradient( 331 GradientInfo* info) const { 332 if (info) { 333 commonAsAGradient(info, fFlippedGrad); 334 info->fPoint[0] = fCenter1; 335 info->fPoint[1] = fCenter2; 336 info->fRadius[0] = fRadius1; 337 info->fRadius[1] = fRadius2; 338 if (fFlippedGrad) { 339 SkTSwap(info->fPoint[0], info->fPoint[1]); 340 SkTSwap(info->fRadius[0], info->fRadius[1]); 341 } 342 } 343 return kConical_GradientType; 344 } 345 346 SkTwoPointConicalGradient::SkTwoPointConicalGradient( 347 SkReadBuffer& buffer) 348 : INHERITED(buffer), 349 fCenter1(buffer.readPoint()), 350 fCenter2(buffer.readPoint()), 351 fRadius1(buffer.readScalar()), 352 fRadius2(buffer.readScalar()) { 353 if (buffer.isVersionLT(SkReadBuffer::kGradientFlippedFlag_Version)) { 354 // V23_COMPATIBILITY_CODE 355 // Sort gradient by radius size for old pictures 356 if (fRadius2 < fRadius1) { 357 SkTSwap(fCenter1, fCenter2); 358 SkTSwap(fRadius1, fRadius2); 359 this->flipGradientColors(); 360 fFlippedGrad = true; 361 } else { 362 fFlippedGrad = false; 363 } 364 } else { 365 fFlippedGrad = buffer.readBool(); 366 } 367 this->init(); 368 }; 369 370 void SkTwoPointConicalGradient::flatten( 371 SkWriteBuffer& buffer) const { 372 this->INHERITED::flatten(buffer); 373 buffer.writePoint(fCenter1); 374 buffer.writePoint(fCenter2); 375 buffer.writeScalar(fRadius1); 376 buffer.writeScalar(fRadius2); 377 buffer.writeBool(fFlippedGrad); 378 } 379 380 #if SK_SUPPORT_GPU 381 382 #include "SkGr.h" 383 384 bool SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint& paint, 385 const SkMatrix* localMatrix, GrColor* grColor, 386 GrEffectRef** grEffect) const { 387 SkASSERT(NULL != context); 388 SkASSERT(fPtsToUnit.isIdentity()); 389 390 *grEffect = Gr2PtConicalGradientEffect::Create(context, *this, fTileMode, localMatrix); 391 *grColor = SkColor2GrColorJustAlpha(paint.getColor()); 392 return true; 393 } 394 395 #else 396 397 bool SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint& paint, 398 const SkMatrix* localMatrix, GrColor* grColor, 399 GrEffectRef** grEffect) const { 400 SkDEBUGFAIL("Should not call in GPU-less build"); 401 return false; 402 } 403 404 #endif 405 406 #ifndef SK_IGNORE_TO_STRING 407 void SkTwoPointConicalGradient::toString(SkString* str) const { 408 str->append("SkTwoPointConicalGradient: ("); 409 410 str->append("center1: ("); 411 str->appendScalar(fCenter1.fX); 412 str->append(", "); 413 str->appendScalar(fCenter1.fY); 414 str->append(") radius1: "); 415 str->appendScalar(fRadius1); 416 str->append(" "); 417 418 str->append("center2: ("); 419 str->appendScalar(fCenter2.fX); 420 str->append(", "); 421 str->appendScalar(fCenter2.fY); 422 str->append(") radius2: "); 423 str->appendScalar(fRadius2); 424 str->append(" "); 425 426 this->INHERITED::toString(str); 427 428 str->append(")"); 429 } 430 #endif 431