3 * Copyright 2012 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
9 #include "SkTwoPointRadialGradient.h"
11 /* Two-point radial gradients are specified by two circles, each with a center
12 point and radius. The gradient can be considered to be a series of
13 concentric circles, with the color interpolated from the start circle
14 (at t=0) to the end circle (at t=1).
16 For each point (x, y) in the span, we want to find the
17 interpolated circle that intersects that point. The center
18 of the desired circle (Cx, Cy) falls at some distance t
19 along the line segment between the start point (Sx, Sy) and
22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
23 Cy = (1 - t) * Sy + t * Ey
25 The radius of the desired circle (r) is also a linear interpolation t
26 between the start and end radii (Sr and Er):
28 r = (1 - t) * Sr + t * Er
32 (x - Cx)^2 + (y - Cy)^2 = r^2
36 (x - ((1 - t) * Sx + t * Ex))^2
37 + (y - ((1 - t) * Sy + t * Ey))^2
38 = ((1 - t) * Sr + t * Er)^2
42 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
43 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
44 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
48 [Dx^2 + Dy^2 - Dr^2)] * t^2
49 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
50 + [dx^2 + dy^2 - Sr^2] = 0
52 A quadratic in t. The two roots of the quadratic reflect the two
53 possible circles on which the point may fall. Solving for t yields
54 the gradient value to use.
56 If a<0, the start circle is entirely contained in the
57 end circle, and one of the roots will be <0 or >1 (off the line
58 segment). If a>0, the start circle falls at least partially
59 outside the end circle (or vice versa), and the gradient
60 defines a "tube" where a point may be on one circle (on the
61 inside of the tube) or the other (outside of the tube). We choose
64 In order to keep the math to within the limits of fixed point,
65 we divide the entire quadratic by Dr^2, and replace
66 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
68 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
69 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
70 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
72 (x' and y' are computed by appending the subtract and scale to the
73 fDstToIndex matrix in the constructor).
75 Since the 'A' component of the quadratic is independent of x' and y', it
76 is precomputed in the constructor. Since the 'B' component is linear in
77 x' and y', if x and y are linear in the span, 'B' can be computed
78 incrementally with a simple delta (db below). If it is not (e.g.,
79 a perspective projection), it must be computed in the loop.
85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
86 SkScalar sr2d2, SkScalar foura,
87 SkScalar oneOverTwoA, bool posRoot) {
88 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
90 return SkScalarToFixed(SkScalarDiv(-c, b));
93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
97 SkScalar rootDiscrim = SkScalarSqrt(discrim);
100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
104 return SkScalarToFixed(result);
107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
108 SkScalar fy, SkScalar dy,
109 SkScalar b, SkScalar db,
110 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
111 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
115 SkScalar fy, SkScalar dy,
116 SkScalar b, SkScalar db,
117 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
118 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
120 for (; count > 0; --count) {
121 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
122 fOneOverTwoA, posRoot);
123 SkFixed index = SkClampMax(t, 0xFFFF);
124 SkASSERT(index <= 0xFFFF);
125 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
132 SkScalar fy, SkScalar dy,
133 SkScalar b, SkScalar db,
134 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
135 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
137 for (; count > 0; --count) {
138 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
139 fOneOverTwoA, posRoot);
140 SkFixed index = mirror_tileproc(t);
141 SkASSERT(index <= 0xFFFF);
142 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
150 SkScalar fy, SkScalar dy,
151 SkScalar b, SkScalar db,
152 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
153 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
155 for (; count > 0; --count) {
156 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
157 fOneOverTwoA, posRoot);
158 SkFixed index = repeat_tileproc(t);
159 SkASSERT(index <= 0xFFFF);
160 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
168 /////////////////////////////////////////////////////////////////////
170 static SkMatrix pts_to_unit(const SkPoint& start, SkScalar diffRadius) {
171 SkScalar inv = diffRadius ? SkScalarInvert(diffRadius) : 0;
173 matrix.setTranslate(-start.fX, -start.fY);
174 matrix.postScale(inv, inv);
178 SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
179 const SkPoint& end, SkScalar endRadius,
180 const Descriptor& desc)
181 : SkGradientShaderBase(desc, pts_to_unit(start, endRadius - startRadius))
184 , fRadius1(startRadius)
185 , fRadius2(endRadius)
187 fDiff = fCenter1 - fCenter2;
188 fDiffRadius = fRadius2 - fRadius1;
189 // hack to avoid zero-divide for now
190 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
191 fDiff.fX = SkScalarMul(fDiff.fX, inv);
192 fDiff.fY = SkScalarMul(fDiff.fY, inv);
193 fStartRadius = SkScalarMul(fRadius1, inv);
194 fSr2D2 = SkScalarSquare(fStartRadius);
195 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
196 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
199 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
202 SkShader::TileMode* xy) const {
204 this->getGradientTableBitmap(bitmap);
206 SkScalar diffL = 0; // just to avoid gcc warning
208 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
209 SkScalarSquare(fDiff.fY));
213 SkScalar invDiffL = SkScalarInvert(diffL);
214 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
215 SkScalarMul(invDiffL, fDiff.fX));
219 matrix->preConcat(fPtsToUnit);
223 xy[1] = kClamp_TileMode;
225 return kTwoPointRadial_BitmapType;
228 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
229 SkShader::GradientInfo* info) const {
231 commonAsAGradient(info);
232 info->fPoint[0] = fCenter1;
233 info->fPoint[1] = fCenter2;
234 info->fRadius[0] = fRadius1;
235 info->fRadius[1] = fRadius2;
237 return kRadial2_GradientType;
240 size_t SkTwoPointRadialGradient::contextSize() const {
241 return sizeof(TwoPointRadialGradientContext);
244 SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
245 void* storage) const {
246 // For now, we might have divided by zero, so detect that.
247 if (0 == fDiffRadius) {
250 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
253 SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
254 const SkTwoPointRadialGradient& shader, const ContextRec& rec)
255 : INHERITED(shader, rec)
257 // we don't have a span16 proc
258 fFlags &= ~kHasSpan16_Flag;
261 void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
262 int x, int y, SkPMColor* dstCParam, int count) {
265 const SkTwoPointRadialGradient& twoPointRadialGradient =
266 static_cast<const SkTwoPointRadialGradient&>(fShader);
268 SkPMColor* SK_RESTRICT dstC = dstCParam;
270 // Zero difference between radii: fill with transparent black.
271 if (twoPointRadialGradient.fDiffRadius == 0) {
272 sk_bzero(dstC, count * sizeof(*dstC));
275 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
276 TileProc proc = twoPointRadialGradient.fTileProc;
277 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
279 SkScalar foura = twoPointRadialGradient.fA * 4;
280 bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
281 if (fDstToIndexClass != kPerspective_MatrixClass) {
283 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
284 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
285 SkScalar dx, fx = srcPt.fX;
286 SkScalar dy, fy = srcPt.fY;
288 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
289 SkFixed fixedX, fixedY;
290 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
291 dx = SkFixedToScalar(fixedX);
292 dy = SkFixedToScalar(fixedY);
294 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
295 dx = fDstToIndex.getScaleX();
296 dy = fDstToIndex.getSkewY();
298 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
299 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
300 twoPointRadialGradient.fStartRadius) * 2;
301 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
302 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
304 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
305 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
306 shadeProc = shadeSpan_twopoint_clamp;
307 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
308 shadeProc = shadeSpan_twopoint_mirror;
310 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
312 (*shadeProc)(fx, dx, fy, dy, b, db,
313 twoPointRadialGradient.fSr2D2, foura,
314 twoPointRadialGradient.fOneOverTwoA, posRoot,
316 } else { // perspective case
317 SkScalar dstX = SkIntToScalar(x);
318 SkScalar dstY = SkIntToScalar(y);
319 for (; count > 0; --count) {
321 dstProc(fDstToIndex, dstX, dstY, &srcPt);
322 SkScalar fx = srcPt.fX;
323 SkScalar fy = srcPt.fY;
324 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
325 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
326 twoPointRadialGradient.fStartRadius) * 2;
327 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
328 twoPointRadialGradient.fOneOverTwoA, posRoot);
329 SkFixed index = proc(t);
330 SkASSERT(index <= 0xFFFF);
331 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
337 #ifndef SK_IGNORE_TO_STRING
338 void SkTwoPointRadialGradient::toString(SkString* str) const {
339 str->append("SkTwoPointRadialGradient: (");
341 str->append("center1: (");
342 str->appendScalar(fCenter1.fX);
344 str->appendScalar(fCenter1.fY);
345 str->append(") radius1: ");
346 str->appendScalar(fRadius1);
349 str->append("center2: (");
350 str->appendScalar(fCenter2.fX);
352 str->appendScalar(fCenter2.fY);
353 str->append(") radius2: ");
354 str->appendScalar(fRadius2);
357 this->INHERITED::toString(str);
363 SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
364 DescriptorScope desc;
365 if (!desc.unflatten(buffer)) {
368 const SkPoint c1 = buffer.readPoint();
369 const SkPoint c2 = buffer.readPoint();
370 const SkScalar r1 = buffer.readScalar();
371 const SkScalar r2 = buffer.readScalar();
372 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
373 desc.fCount, desc.fTileMode, desc.fGradFlags,
377 void SkTwoPointRadialGradient::flatten(
378 SkWriteBuffer& buffer) const {
379 this->INHERITED::flatten(buffer);
380 buffer.writePoint(fCenter1);
381 buffer.writePoint(fCenter2);
382 buffer.writeScalar(fRadius1);
383 buffer.writeScalar(fRadius2);
386 /////////////////////////////////////////////////////////////////////
391 #include "gl/builders/GrGLProgramBuilder.h"
394 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
396 class GrGLRadial2Gradient : public GrGLGradientEffect {
400 GrGLRadial2Gradient(const GrProcessor&);
401 virtual ~GrGLRadial2Gradient() { }
403 virtual void emitCode(GrGLFPBuilder*,
404 const GrFragmentProcessor&,
405 const char* outputColor,
406 const char* inputColor,
407 const TransformedCoordsArray&,
408 const TextureSamplerArray&) override;
409 void setData(const GrGLProgramDataManager&, const GrProcessor&) override;
411 static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
415 UniformHandle fParamUni;
417 const char* fVSVaryingName;
418 const char* fFSVaryingName;
423 /// Values last uploaded as uniforms
425 SkScalar fCachedCenter;
426 SkScalar fCachedRadius;
433 typedef GrGLGradientEffect INHERITED;
437 /////////////////////////////////////////////////////////////////////
439 class GrRadial2Gradient : public GrGradientEffect {
441 static GrFragmentProcessor* Create(GrContext* ctx,
442 const SkTwoPointRadialGradient& shader,
443 const SkMatrix& matrix,
444 SkShader::TileMode tm) {
445 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
448 virtual ~GrRadial2Gradient() { }
450 const char* name() const override { return "Two-Point Radial Gradient"; }
452 virtual void getGLProcessorKey(const GrGLCaps& caps,
453 GrProcessorKeyBuilder* b) const override {
454 GrGLRadial2Gradient::GenKey(*this, caps, b);
457 GrGLFragmentProcessor* createGLInstance() const override {
458 return SkNEW_ARGS(GrGLRadial2Gradient, (*this));
461 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
462 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
463 SkScalar center() const { return fCenterX1; }
464 SkScalar radius() const { return fRadius0; }
465 bool isPosRoot() const { return SkToBool(fPosRoot); }
468 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
469 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
470 return (INHERITED::onIsEqual(sBase) &&
471 this->fCenterX1 == s.fCenterX1 &&
472 this->fRadius0 == s.fRadius0 &&
473 this->fPosRoot == s.fPosRoot);
476 GrRadial2Gradient(GrContext* ctx,
477 const SkTwoPointRadialGradient& shader,
478 const SkMatrix& matrix,
479 SkShader::TileMode tm)
480 : INHERITED(ctx, shader, matrix, tm)
481 , fCenterX1(shader.getCenterX1())
482 , fRadius0(shader.getStartRadius())
483 , fPosRoot(shader.getDiffRadius() < 0) {
484 this->initClassID<GrRadial2Gradient>();
485 // We pass the linear part of the quadratic as a varying.
486 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
487 fBTransform = this->getCoordTransform();
488 SkMatrix& bMatrix = *fBTransform.accessMatrix();
489 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
490 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
491 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
492 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
493 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
494 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
495 this->addCoordTransform(&fBTransform);
498 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
501 // Cache of values - these can change arbitrarily, EXCEPT
502 // we shouldn't change between degenerate and non-degenerate?!
504 GrCoordTransform fBTransform;
511 typedef GrGradientEffect INHERITED;
514 /////////////////////////////////////////////////////////////////////
516 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
518 GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
520 const GrDrawTargetCaps&,
522 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
523 SkScalar radius1 = random->nextUScalar1();
527 center2.set(random->nextUScalar1(), random->nextUScalar1());
528 radius2 = random->nextUScalar1 ();
529 // There is a bug in two point radial gradients with identical radii
530 } while (radius1 == radius2);
532 SkColor colors[kMaxRandomGradientColors];
533 SkScalar stopsArray[kMaxRandomGradientColors];
534 SkScalar* stops = stopsArray;
535 SkShader::TileMode tm;
536 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
537 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
539 colors, stops, colorCount,
542 GrFragmentProcessor* fp;
544 SkAssertResult(shader->asFragmentProcessor(context, paint,
545 GrProcessorUnitTest::TestMatrix(random), NULL,
550 /////////////////////////////////////////////////////////////////////
552 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
553 : fVSVaryingName(NULL)
554 , fFSVaryingName(NULL)
555 , fCachedCenter(SK_ScalarMax)
556 , fCachedRadius(-SK_ScalarMax)
557 , fCachedPosRoot(0) {
559 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
560 fIsDegenerate = data.isDegenerate();
563 void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
564 const GrFragmentProcessor& fp,
565 const char* outputColor,
566 const char* inputColor,
567 const TransformedCoordsArray& coords,
568 const TextureSamplerArray& samplers) {
569 const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
570 this->emitUniforms(builder, ge);
571 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
572 kFloat_GrSLType, kDefault_GrSLPrecision,
573 "Radial2FSParams", 6);
576 SkString ac4Name("ac4");
577 SkString rootName("root");
585 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
586 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
587 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
588 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
589 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
590 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
592 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
593 // We interpolate the linear component in coords[1].
594 SkASSERT(coords[0].getType() == coords[1].getType());
595 const char* coords2D;
597 if (kVec3f_GrSLType == coords[0].getType()) {
598 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
599 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
600 coords2D = "interpolants.xy";
601 bVar = "interpolants.z";
603 coords2D = coords[0].c_str();
604 bVar.printf("%s.x", coords[1].c_str());
607 // c = (x^2)+(y^2) - params[4]
608 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
609 cName.c_str(), coords2D, coords2D, p4.c_str());
611 // If we aren't degenerate, emit some extra code, and accept a slightly
612 // more complex coord.
613 if (!fIsDegenerate) {
615 // ac4 = 4.0 * params[0] * c
616 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
617 ac4Name.c_str(), p0.c_str(),
620 // root = sqrt(b^2-4ac)
621 // (abs to avoid exception due to fp precision)
622 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
623 rootName.c_str(), bVar.c_str(), bVar.c_str(),
626 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
627 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
628 rootName.c_str(), p1.c_str());
631 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
634 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
637 void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
638 const GrProcessor& processor) {
639 INHERITED::setData(pdman, processor);
640 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
641 SkASSERT(data.isDegenerate() == fIsDegenerate);
642 SkScalar centerX1 = data.center();
643 SkScalar radius0 = data.radius();
644 if (fCachedCenter != centerX1 ||
645 fCachedRadius != radius0 ||
646 fCachedPosRoot != data.isPosRoot()) {
648 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
650 // When we're in the degenerate (linear) case, the second
651 // value will be INF but the program doesn't read it. (We
652 // use the same 6 uniforms even though we don't need them
653 // all in the linear case just to keep the code complexity
657 1 / (2.f * SkScalarToFloat(a)),
658 SkScalarToFloat(centerX1),
659 SkScalarToFloat(radius0),
660 SkScalarToFloat(SkScalarMul(radius0, radius0)),
661 data.isPosRoot() ? 1.f : -1.f
664 pdman.set1fv(fParamUni, 6, values);
665 fCachedCenter = centerX1;
666 fCachedRadius = radius0;
667 fCachedPosRoot = data.isPosRoot();
671 void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
672 const GrGLCaps&, GrProcessorKeyBuilder* b) {
673 uint32_t* key = b->add32n(2);
674 key[0] = GenBaseGradientKey(processor);
675 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
678 /////////////////////////////////////////////////////////////////////
680 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
682 const SkMatrix* localMatrix, GrColor* paintColor,
683 GrFragmentProcessor** fp) const {
686 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
688 if (!this->getLocalMatrix().invert(&matrix)) {
693 if (!localMatrix->invert(&inv)) {
696 matrix.postConcat(inv);
698 matrix.postConcat(fPtsToUnit);
700 SkScalar diffLen = fDiff.length();
702 SkScalar invDiffLen = SkScalarInvert(diffLen);
704 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
705 SkScalarMul(invDiffLen, fDiff.fX));
706 matrix.postConcat(rot);
709 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
710 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
717 bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
719 GrColor*, GrFragmentProcessor**) const {
720 SkDEBUGFAIL("Should not call in GPU-less build");