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 : SkGradientShaderBase(desc) 201 , fCenter1(start) 202 , fCenter2(end) 203 , fRadius1(startRadius) 204 , fRadius2(endRadius) 205 , fFlippedGrad(flippedGrad) 206 { 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 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING 347 SkTwoPointConicalGradient::SkTwoPointConicalGradient( 348 SkReadBuffer& buffer) 349 : INHERITED(buffer), 350 fCenter1(buffer.readPoint()), 351 fCenter2(buffer.readPoint()), 352 fRadius1(buffer.readScalar()), 353 fRadius2(buffer.readScalar()) { 354 if (buffer.isVersionLT(SkReadBuffer::kGradientFlippedFlag_Version)) { 355 // V23_COMPATIBILITY_CODE 356 // Sort gradient by radius size for old pictures 357 if (fRadius2 < fRadius1) { 358 SkTSwap(fCenter1, fCenter2); 359 SkTSwap(fRadius1, fRadius2); 360 this->flipGradientColors(); 361 fFlippedGrad = true; 362 } else { 363 fFlippedGrad = false; 364 } 365 } else { 366 fFlippedGrad = buffer.readBool(); 367 } 368 this->init(); 369 }; 370 #endif 371 372 SkFlattenable* SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) { 373 DescriptorScope desc; 374 if (!desc.unflatten(buffer)) { 375 return NULL; 376 } 377 SkPoint c1 = buffer.readPoint(); 378 SkPoint c2 = buffer.readPoint(); 379 SkScalar r1 = buffer.readScalar(); 380 SkScalar r2 = buffer.readScalar(); 381 382 if (buffer.readBool()) { // flipped 383 SkTSwap(c1, c2); 384 SkTSwap(r1, r2); 385 386 SkColor* colors = desc.mutableColors(); 387 SkScalar* pos = desc.mutablePos(); 388 const int last = desc.fCount - 1; 389 const int half = desc.fCount >> 1; 390 for (int i = 0; i < half; ++i) { 391 SkTSwap(colors[i], colors[last - i]); 392 if (pos) { 393 SkScalar tmp = pos[i]; 394 pos[i] = SK_Scalar1 - pos[last - i]; 395 pos[last - i] = SK_Scalar1 - tmp; 396 } 397 } 398 if (pos) { 399 if (desc.fCount & 1) { 400 pos[half] = SK_Scalar1 - pos[half]; 401 } 402 } 403 } 404 405 return SkGradientShader::CreateTwoPointConical(c1, r1, c2, r2, desc.fColors, desc.fPos, 406 desc.fCount, desc.fTileMode, desc.fGradFlags, 407 desc.fLocalMatrix); 408 } 409 410 void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { 411 this->INHERITED::flatten(buffer); 412 buffer.writePoint(fCenter1); 413 buffer.writePoint(fCenter2); 414 buffer.writeScalar(fRadius1); 415 buffer.writeScalar(fRadius2); 416 buffer.writeBool(fFlippedGrad); 417 } 418 419 #if SK_SUPPORT_GPU 420 421 #include "SkGr.h" 422 423 bool SkTwoPointConicalGradient::asFragmentProcessor(GrContext* context, 424 const SkPaint& paint, 425 const SkMatrix* localMatrix, 426 GrColor* paintColor, 427 GrFragmentProcessor** fp) const { 428 SkASSERT(context); 429 SkASSERT(fPtsToUnit.isIdentity()); 430 431 *fp = Gr2PtConicalGradientEffect::Create(context, *this, fTileMode, localMatrix); 432 *paintColor = SkColor2GrColorJustAlpha(paint.getColor()); 433 return true; 434 } 435 436 #else 437 438 bool SkTwoPointConicalGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, 439 GrColor*, GrFragmentProcessor**) const { 440 SkDEBUGFAIL("Should not call in GPU-less build"); 441 return false; 442 } 443 444 #endif 445 446 #ifndef SK_IGNORE_TO_STRING 447 void SkTwoPointConicalGradient::toString(SkString* str) const { 448 str->append("SkTwoPointConicalGradient: ("); 449 450 str->append("center1: ("); 451 str->appendScalar(fCenter1.fX); 452 str->append(", "); 453 str->appendScalar(fCenter1.fY); 454 str->append(") radius1: "); 455 str->appendScalar(fRadius1); 456 str->append(" "); 457 458 str->append("center2: ("); 459 str->appendScalar(fCenter2.fX); 460 str->append(", "); 461 str->appendScalar(fCenter2.fY); 462 str->append(") radius2: "); 463 str->appendScalar(fRadius2); 464 str->append(" "); 465 466 this->INHERITED::toString(str); 467 468 str->append(")"); 469 } 470 #endif 471