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 11struct 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 23static 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 34static 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 74static float lerp(float x, float dx, float t) { 75 return x + t * dx; 76} 77 78static float sqr(float x) { return x * x; } 79 80void 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 97TwoPtRadialContext::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 107SkFixed 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 135typedef void (*TwoPointConicalProc)(TwoPtRadialContext* rec, SkPMColor* dstC, 136 const SkPMColor* cache, int toggle, int count); 137 138static 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 155static 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 172static 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///////////////////////////////////////////////////////////////////// 190 191SkTwoPointConicalGradient::SkTwoPointConicalGradient( 192 const SkPoint& start, SkScalar startRadius, 193 const SkPoint& end, SkScalar endRadius, 194 bool flippedGrad, const Descriptor& desc) 195 : SkGradientShaderBase(desc, SkMatrix::I()) 196 , fCenter1(start) 197 , fCenter2(end) 198 , fRadius1(startRadius) 199 , fRadius2(endRadius) 200 , fFlippedGrad(flippedGrad) 201{ 202 // this is degenerate, and should be caught by our caller 203 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); 204 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2, fFlippedGrad); 205} 206 207bool SkTwoPointConicalGradient::isOpaque() const { 208 // Because areas outside the cone are left untouched, we cannot treat the 209 // shader as opaque even if the gradient itself is opaque. 210 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 211 return false; 212} 213 214size_t SkTwoPointConicalGradient::contextSize() const { 215 return sizeof(TwoPointConicalGradientContext); 216} 217 218SkShader::Context* SkTwoPointConicalGradient::onCreateContext(const ContextRec& rec, 219 void* storage) const { 220 return SkNEW_PLACEMENT_ARGS(storage, TwoPointConicalGradientContext, (*this, rec)); 221} 222 223SkTwoPointConicalGradient::TwoPointConicalGradientContext::TwoPointConicalGradientContext( 224 const SkTwoPointConicalGradient& shader, const ContextRec& rec) 225 : INHERITED(shader, rec) 226{ 227 // we don't have a span16 proc 228 fFlags &= ~kHasSpan16_Flag; 229 230 // in general, we might discard based on computed-radius, so clear 231 // this flag (todo: sometimes we can detect that we never discard...) 232 fFlags &= ~kOpaqueAlpha_Flag; 233} 234 235void SkTwoPointConicalGradient::TwoPointConicalGradientContext::shadeSpan( 236 int x, int y, SkPMColor* dstCParam, int count) { 237 const SkTwoPointConicalGradient& twoPointConicalGradient = 238 static_cast<const SkTwoPointConicalGradient&>(fShader); 239 240 int toggle = init_dither_toggle(x, y); 241 242 SkASSERT(count > 0); 243 244 SkPMColor* SK_RESTRICT dstC = dstCParam; 245 246 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 247 248 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 249 250 TwoPointConicalProc shadeProc = twopoint_repeat; 251 if (SkShader::kClamp_TileMode == twoPointConicalGradient.fTileMode) { 252 shadeProc = twopoint_clamp; 253 } else if (SkShader::kMirror_TileMode == twoPointConicalGradient.fTileMode) { 254 shadeProc = twopoint_mirror; 255 } else { 256 SkASSERT(SkShader::kRepeat_TileMode == twoPointConicalGradient.fTileMode); 257 } 258 259 if (fDstToIndexClass != kPerspective_MatrixClass) { 260 SkPoint srcPt; 261 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 262 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 263 SkScalar dx, fx = srcPt.fX; 264 SkScalar dy, fy = srcPt.fY; 265 266 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 267 SkFixed fixedX, fixedY; 268 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY); 269 dx = SkFixedToScalar(fixedX); 270 dy = SkFixedToScalar(fixedY); 271 } else { 272 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 273 dx = fDstToIndex.getScaleX(); 274 dy = fDstToIndex.getSkewY(); 275 } 276 277 TwoPtRadialContext rec(twoPointConicalGradient.fRec, fx, fy, dx, dy); 278 (*shadeProc)(&rec, dstC, cache, toggle, count); 279 } else { // perspective case 280 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf; 281 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf; 282 for (; count > 0; --count) { 283 SkPoint srcPt; 284 dstProc(fDstToIndex, dstX, dstY, &srcPt); 285 TwoPtRadialContext rec(twoPointConicalGradient.fRec, srcPt.fX, srcPt.fY, 0, 0); 286 (*shadeProc)(&rec, dstC, cache, toggle, 1); 287 288 dstX += SK_Scalar1; 289 toggle = next_dither_toggle(toggle); 290 dstC += 1; 291 } 292 } 293} 294 295SkShader::BitmapType SkTwoPointConicalGradient::asABitmap( 296 SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const { 297 SkPoint diff = fCenter2 - fCenter1; 298 SkScalar diffLen = 0; 299 300 if (bitmap) { 301 this->getGradientTableBitmap(bitmap); 302 } 303 if (matrix) { 304 diffLen = diff.length(); 305 } 306 if (matrix) { 307 if (diffLen) { 308 SkScalar invDiffLen = SkScalarInvert(diffLen); 309 // rotate to align circle centers with the x-axis 310 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY), 311 SkScalarMul(invDiffLen, diff.fX)); 312 } else { 313 matrix->reset(); 314 } 315 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY); 316 } 317 if (xy) { 318 xy[0] = fTileMode; 319 xy[1] = kClamp_TileMode; 320 } 321 return kTwoPointConical_BitmapType; 322} 323 324// Returns the original non-sorted version of the gradient 325SkShader::GradientType SkTwoPointConicalGradient::asAGradient( 326 GradientInfo* info) const { 327 if (info) { 328 commonAsAGradient(info, fFlippedGrad); 329 info->fPoint[0] = fCenter1; 330 info->fPoint[1] = fCenter2; 331 info->fRadius[0] = fRadius1; 332 info->fRadius[1] = fRadius2; 333 if (fFlippedGrad) { 334 SkTSwap(info->fPoint[0], info->fPoint[1]); 335 SkTSwap(info->fRadius[0], info->fRadius[1]); 336 } 337 } 338 return kConical_GradientType; 339} 340 341SkFlattenable* SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) { 342 DescriptorScope desc; 343 if (!desc.unflatten(buffer)) { 344 return NULL; 345 } 346 SkPoint c1 = buffer.readPoint(); 347 SkPoint c2 = buffer.readPoint(); 348 SkScalar r1 = buffer.readScalar(); 349 SkScalar r2 = buffer.readScalar(); 350 351 if (buffer.readBool()) { // flipped 352 SkTSwap(c1, c2); 353 SkTSwap(r1, r2); 354 355 SkColor* colors = desc.mutableColors(); 356 SkScalar* pos = desc.mutablePos(); 357 const int last = desc.fCount - 1; 358 const int half = desc.fCount >> 1; 359 for (int i = 0; i < half; ++i) { 360 SkTSwap(colors[i], colors[last - i]); 361 if (pos) { 362 SkScalar tmp = pos[i]; 363 pos[i] = SK_Scalar1 - pos[last - i]; 364 pos[last - i] = SK_Scalar1 - tmp; 365 } 366 } 367 if (pos) { 368 if (desc.fCount & 1) { 369 pos[half] = SK_Scalar1 - pos[half]; 370 } 371 } 372 } 373 374 return SkGradientShader::CreateTwoPointConical(c1, r1, c2, r2, desc.fColors, desc.fPos, 375 desc.fCount, desc.fTileMode, desc.fGradFlags, 376 desc.fLocalMatrix); 377} 378 379void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { 380 this->INHERITED::flatten(buffer); 381 buffer.writePoint(fCenter1); 382 buffer.writePoint(fCenter2); 383 buffer.writeScalar(fRadius1); 384 buffer.writeScalar(fRadius2); 385 buffer.writeBool(fFlippedGrad); 386} 387 388#if SK_SUPPORT_GPU 389 390#include "SkGr.h" 391 392bool SkTwoPointConicalGradient::asFragmentProcessor(GrContext* context, 393 const SkPaint& paint, 394 const SkMatrix& viewM, 395 const SkMatrix* localMatrix, 396 GrColor* paintColor, 397 GrFragmentProcessor** fp) const { 398 SkASSERT(context); 399 SkASSERT(fPtsToUnit.isIdentity()); 400 401 *fp = Gr2PtConicalGradientEffect::Create(context, *this, fTileMode, localMatrix); 402 *paintColor = SkColor2GrColorJustAlpha(paint.getColor()); 403 return true; 404} 405 406#else 407 408bool SkTwoPointConicalGradient::asFragmentProcessor(GrContext*, const SkPaint&, 409 const SkMatrix&, const SkMatrix*, 410 GrColor*, GrFragmentProcessor**) const { 411 SkDEBUGFAIL("Should not call in GPU-less build"); 412 return false; 413} 414 415#endif 416 417#ifndef SK_IGNORE_TO_STRING 418void SkTwoPointConicalGradient::toString(SkString* str) const { 419 str->append("SkTwoPointConicalGradient: ("); 420 421 str->append("center1: ("); 422 str->appendScalar(fCenter1.fX); 423 str->append(", "); 424 str->appendScalar(fCenter1.fY); 425 str->append(") radius1: "); 426 str->appendScalar(fRadius1); 427 str->append(" "); 428 429 str->append("center2: ("); 430 str->appendScalar(fCenter2.fX); 431 str->append(", "); 432 str->appendScalar(fCenter2.fY); 433 str->append(") radius2: "); 434 str->appendScalar(fRadius2); 435 str->append(" "); 436 437 this->INHERITED::toString(str); 438 439 str->append(")"); 440} 441#endif 442