Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
2 /*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
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
20 end point (Ex, Ey):
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
30 But
32 (x - Cx)^2 + (y - Cy)^2 = r^2
34 so
36 (x - ((1 - t) * Sx + t * Ex))^2
37 + (y - ((1 - t) * Sy + t * Ey))^2
38 = ((1 - t) * Sr + t * Er)^2
40 Solving for t yields
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
62 one arbitrarily.
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.
81 */
83 namespace {
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;
89 if (0 == foura) {
90 return SkScalarToFixed(SkScalarDiv(-c, b));
91 }
93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
94 if (discrim < 0) {
95 discrim = -discrim;
96 }
97 SkScalar rootDiscrim = SkScalarSqrt(discrim);
98 SkScalar result;
99 if (posRoot) {
100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
101 } else {
102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
103 }
104 return SkScalarToFixed(result);
105 }
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,
112 int count);
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,
119 int count) {
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];
126 fx += dx;
127 fy += dy;
128 b += db;
129 }
130 }
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,
136 int count) {
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];
143 fx += dx;
144 fy += dy;
145 b += db;
146 }
147 }
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,
154 int count) {
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];
161 fx += dx;
162 fy += dy;
163 b += db;
164 }
165 }
166 }
168 /////////////////////////////////////////////////////////////////////
170 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
171 const SkPoint& start, SkScalar startRadius,
172 const SkPoint& end, SkScalar endRadius,
173 const Descriptor& desc)
174 : SkGradientShaderBase(desc),
175 fCenter1(start),
176 fCenter2(end),
177 fRadius1(startRadius),
178 fRadius2(endRadius) {
179 init();
180 }
182 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
183 SkBitmap* bitmap,
184 SkMatrix* matrix,
185 SkShader::TileMode* xy) const {
186 if (bitmap) {
187 this->getGradientTableBitmap(bitmap);
188 }
189 SkScalar diffL = 0; // just to avoid gcc warning
190 if (matrix) {
191 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
192 SkScalarSquare(fDiff.fY));
193 }
194 if (matrix) {
195 if (diffL) {
196 SkScalar invDiffL = SkScalarInvert(diffL);
197 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
198 SkScalarMul(invDiffL, fDiff.fX));
199 } else {
200 matrix->reset();
201 }
202 matrix->preConcat(fPtsToUnit);
203 }
204 if (xy) {
205 xy[0] = fTileMode;
206 xy[1] = kClamp_TileMode;
207 }
208 return kTwoPointRadial_BitmapType;
209 }
211 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
212 SkShader::GradientInfo* info) const {
213 if (info) {
214 commonAsAGradient(info);
215 info->fPoint[0] = fCenter1;
216 info->fPoint[1] = fCenter2;
217 info->fRadius[0] = fRadius1;
218 info->fRadius[1] = fRadius2;
219 }
220 return kRadial2_GradientType;
221 }
223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
224 int count) {
225 SkASSERT(count > 0);
227 SkPMColor* SK_RESTRICT dstC = dstCParam;
229 // Zero difference between radii: fill with transparent black.
230 if (fDiffRadius == 0) {
231 sk_bzero(dstC, count * sizeof(*dstC));
232 return;
233 }
234 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
235 TileProc proc = fTileProc;
236 const SkPMColor* SK_RESTRICT cache = this->getCache32();
238 SkScalar foura = fA * 4;
239 bool posRoot = fDiffRadius < 0;
240 if (fDstToIndexClass != kPerspective_MatrixClass) {
241 SkPoint srcPt;
242 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
243 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
244 SkScalar dx, fx = srcPt.fX;
245 SkScalar dy, fy = srcPt.fY;
247 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
248 SkFixed fixedX, fixedY;
249 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
250 dx = SkFixedToScalar(fixedX);
251 dy = SkFixedToScalar(fixedY);
252 } else {
253 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
254 dx = fDstToIndex.getScaleX();
255 dy = fDstToIndex.getSkewY();
256 }
257 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
258 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
259 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
260 SkScalarMul(fDiff.fY, dy)) * 2;
262 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
263 if (SkShader::kClamp_TileMode == fTileMode) {
264 shadeProc = shadeSpan_twopoint_clamp;
265 } else if (SkShader::kMirror_TileMode == fTileMode) {
266 shadeProc = shadeSpan_twopoint_mirror;
267 } else {
268 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
269 }
270 (*shadeProc)(fx, dx, fy, dy, b, db,
271 fSr2D2, foura, fOneOverTwoA, posRoot,
272 dstC, cache, count);
273 } else { // perspective case
274 SkScalar dstX = SkIntToScalar(x);
275 SkScalar dstY = SkIntToScalar(y);
276 for (; count > 0; --count) {
277 SkPoint srcPt;
278 dstProc(fDstToIndex, dstX, dstY, &srcPt);
279 SkScalar fx = srcPt.fX;
280 SkScalar fy = srcPt.fY;
281 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
282 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
283 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
284 fOneOverTwoA, posRoot);
285 SkFixed index = proc(t);
286 SkASSERT(index <= 0xFFFF);
287 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
288 dstX += SK_Scalar1;
289 }
290 }
291 }
293 bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
294 const SkPaint& paint,
295 const SkMatrix& matrix){
296 // For now, we might have divided by zero, so detect that
297 if (0 == fDiffRadius) {
298 return false;
299 }
301 if (!this->INHERITED::setContext(device, paint, matrix)) {
302 return false;
303 }
305 // we don't have a span16 proc
306 fFlags &= ~kHasSpan16_Flag;
307 return true;
308 }
310 #ifndef SK_IGNORE_TO_STRING
311 void SkTwoPointRadialGradient::toString(SkString* str) const {
312 str->append("SkTwoPointRadialGradient: (");
314 str->append("center1: (");
315 str->appendScalar(fCenter1.fX);
316 str->append(", ");
317 str->appendScalar(fCenter1.fY);
318 str->append(") radius1: ");
319 str->appendScalar(fRadius1);
320 str->append(" ");
322 str->append("center2: (");
323 str->appendScalar(fCenter2.fX);
324 str->append(", ");
325 str->appendScalar(fCenter2.fY);
326 str->append(") radius2: ");
327 str->appendScalar(fRadius2);
328 str->append(" ");
330 this->INHERITED::toString(str);
332 str->append(")");
333 }
334 #endif
336 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
337 SkReadBuffer& buffer)
338 : INHERITED(buffer),
339 fCenter1(buffer.readPoint()),
340 fCenter2(buffer.readPoint()),
341 fRadius1(buffer.readScalar()),
342 fRadius2(buffer.readScalar()) {
343 init();
344 };
346 void SkTwoPointRadialGradient::flatten(
347 SkWriteBuffer& buffer) const {
348 this->INHERITED::flatten(buffer);
349 buffer.writePoint(fCenter1);
350 buffer.writePoint(fCenter2);
351 buffer.writeScalar(fRadius1);
352 buffer.writeScalar(fRadius2);
353 }
355 void SkTwoPointRadialGradient::init() {
356 fDiff = fCenter1 - fCenter2;
357 fDiffRadius = fRadius2 - fRadius1;
358 // hack to avoid zero-divide for now
359 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
360 fDiff.fX = SkScalarMul(fDiff.fX, inv);
361 fDiff.fY = SkScalarMul(fDiff.fY, inv);
362 fStartRadius = SkScalarMul(fRadius1, inv);
363 fSr2D2 = SkScalarSquare(fStartRadius);
364 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
365 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
367 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
368 fPtsToUnit.postScale(inv, inv);
369 }
371 /////////////////////////////////////////////////////////////////////
373 #if SK_SUPPORT_GPU
375 #include "GrTBackendEffectFactory.h"
377 // For brevity
378 typedef GrGLUniformManager::UniformHandle UniformHandle;
380 class GrGLRadial2Gradient : public GrGLGradientEffect {
382 public:
384 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
385 virtual ~GrGLRadial2Gradient() { }
387 virtual void emitCode(GrGLShaderBuilder*,
388 const GrDrawEffect&,
389 EffectKey,
390 const char* outputColor,
391 const char* inputColor,
392 const TransformedCoordsArray&,
393 const TextureSamplerArray&) SK_OVERRIDE;
394 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
396 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
398 protected:
400 UniformHandle fParamUni;
402 const char* fVSVaryingName;
403 const char* fFSVaryingName;
405 bool fIsDegenerate;
407 // @{
408 /// Values last uploaded as uniforms
410 SkScalar fCachedCenter;
411 SkScalar fCachedRadius;
412 bool fCachedPosRoot;
414 // @}
416 private:
418 typedef GrGLGradientEffect INHERITED;
420 };
422 /////////////////////////////////////////////////////////////////////
424 class GrRadial2Gradient : public GrGradientEffect {
425 public:
426 static GrEffectRef* Create(GrContext* ctx,
427 const SkTwoPointRadialGradient& shader,
428 const SkMatrix& matrix,
429 SkShader::TileMode tm) {
430 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
431 return CreateEffectRef(effect);
432 }
434 virtual ~GrRadial2Gradient() { }
436 static const char* Name() { return "Two-Point Radial Gradient"; }
437 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
438 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
439 }
441 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
442 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
443 SkScalar center() const { return fCenterX1; }
444 SkScalar radius() const { return fRadius0; }
445 bool isPosRoot() const { return SkToBool(fPosRoot); }
447 typedef GrGLRadial2Gradient GLEffect;
449 private:
450 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
451 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
452 return (INHERITED::onIsEqual(sBase) &&
453 this->fCenterX1 == s.fCenterX1 &&
454 this->fRadius0 == s.fRadius0 &&
455 this->fPosRoot == s.fPosRoot);
456 }
458 GrRadial2Gradient(GrContext* ctx,
459 const SkTwoPointRadialGradient& shader,
460 const SkMatrix& matrix,
461 SkShader::TileMode tm)
462 : INHERITED(ctx, shader, matrix, tm)
463 , fCenterX1(shader.getCenterX1())
464 , fRadius0(shader.getStartRadius())
465 , fPosRoot(shader.getDiffRadius() < 0) {
466 // We pass the linear part of the quadratic as a varying.
467 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
468 fBTransform = this->getCoordTransform();
469 SkMatrix& bMatrix = *fBTransform.accessMatrix();
470 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
471 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
472 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
473 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
474 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
475 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
476 this->addCoordTransform(&fBTransform);
477 }
479 GR_DECLARE_EFFECT_TEST;
481 // @{
482 // Cache of values - these can change arbitrarily, EXCEPT
483 // we shouldn't change between degenerate and non-degenerate?!
485 GrCoordTransform fBTransform;
486 SkScalar fCenterX1;
487 SkScalar fRadius0;
488 SkBool8 fPosRoot;
490 // @}
492 typedef GrGradientEffect INHERITED;
493 };
495 /////////////////////////////////////////////////////////////////////
497 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
499 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
500 GrContext* context,
501 const GrDrawTargetCaps&,
502 GrTexture**) {
503 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
504 SkScalar radius1 = random->nextUScalar1();
505 SkPoint center2;
506 SkScalar radius2;
507 do {
508 center2.set(random->nextUScalar1(), random->nextUScalar1());
509 radius2 = random->nextUScalar1 ();
510 // There is a bug in two point radial gradients with identical radii
511 } while (radius1 == radius2);
513 SkColor colors[kMaxRandomGradientColors];
514 SkScalar stopsArray[kMaxRandomGradientColors];
515 SkScalar* stops = stopsArray;
516 SkShader::TileMode tm;
517 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
518 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
519 center2, radius2,
520 colors, stops, colorCount,
521 tm));
522 SkPaint paint;
523 return shader->asNewEffect(context, paint);
524 }
526 /////////////////////////////////////////////////////////////////////
528 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
529 const GrDrawEffect& drawEffect)
530 : INHERITED(factory)
531 , fVSVaryingName(NULL)
532 , fFSVaryingName(NULL)
533 , fCachedCenter(SK_ScalarMax)
534 , fCachedRadius(-SK_ScalarMax)
535 , fCachedPosRoot(0) {
537 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
538 fIsDegenerate = data.isDegenerate();
539 }
541 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
542 const GrDrawEffect& drawEffect,
543 EffectKey key,
544 const char* outputColor,
545 const char* inputColor,
546 const TransformedCoordsArray& coords,
547 const TextureSamplerArray& samplers) {
549 this->emitUniforms(builder, key);
550 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
551 kFloat_GrSLType, "Radial2FSParams", 6);
553 SkString cName("c");
554 SkString ac4Name("ac4");
555 SkString rootName("root");
556 SkString t;
557 SkString p0;
558 SkString p1;
559 SkString p2;
560 SkString p3;
561 SkString p4;
562 SkString p5;
563 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
564 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
565 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
566 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
567 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
568 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
570 // We interpolate the linear component in coords[1].
571 SkASSERT(coords[0].type() == coords[1].type());
572 const char* coords2D;
573 SkString bVar;
574 if (kVec3f_GrSLType == coords[0].type()) {
575 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
576 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
577 coords2D = "interpolants.xy";
578 bVar = "interpolants.z";
579 } else {
580 coords2D = coords[0].c_str();
581 bVar.printf("%s.x", coords[1].c_str());
582 }
584 // c = (x^2)+(y^2) - params[4]
585 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
586 cName.c_str(), coords2D, coords2D, p4.c_str());
588 // If we aren't degenerate, emit some extra code, and accept a slightly
589 // more complex coord.
590 if (!fIsDegenerate) {
592 // ac4 = 4.0 * params[0] * c
593 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
594 ac4Name.c_str(), p0.c_str(),
595 cName.c_str());
597 // root = sqrt(b^2-4ac)
598 // (abs to avoid exception due to fp precision)
599 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
600 rootName.c_str(), bVar.c_str(), bVar.c_str(),
601 ac4Name.c_str());
603 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
604 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
605 rootName.c_str(), p1.c_str());
606 } else {
607 // t is: -c/b
608 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
609 }
611 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
612 }
614 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
615 const GrDrawEffect& drawEffect) {
616 INHERITED::setData(uman, drawEffect);
617 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
618 SkASSERT(data.isDegenerate() == fIsDegenerate);
619 SkScalar centerX1 = data.center();
620 SkScalar radius0 = data.radius();
621 if (fCachedCenter != centerX1 ||
622 fCachedRadius != radius0 ||
623 fCachedPosRoot != data.isPosRoot()) {
625 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
627 // When we're in the degenerate (linear) case, the second
628 // value will be INF but the program doesn't read it. (We
629 // use the same 6 uniforms even though we don't need them
630 // all in the linear case just to keep the code complexity
631 // down).
632 float values[6] = {
633 SkScalarToFloat(a),
634 1 / (2.f * SkScalarToFloat(a)),
635 SkScalarToFloat(centerX1),
636 SkScalarToFloat(radius0),
637 SkScalarToFloat(SkScalarMul(radius0, radius0)),
638 data.isPosRoot() ? 1.f : -1.f
639 };
641 uman.set1fv(fParamUni, 6, values);
642 fCachedCenter = centerX1;
643 fCachedRadius = radius0;
644 fCachedPosRoot = data.isPosRoot();
645 }
646 }
648 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
649 const GrGLCaps&) {
650 enum {
651 kIsDegenerate = 1 << kBaseKeyBitCnt,
652 };
654 EffectKey key = GenBaseGradientKey(drawEffect);
655 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
656 key |= kIsDegenerate;
657 }
658 return key;
659 }
661 /////////////////////////////////////////////////////////////////////
663 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
664 SkASSERT(NULL != context);
665 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
666 SkMatrix matrix;
667 if (!this->getLocalMatrix().invert(&matrix)) {
668 return NULL;
669 }
670 matrix.postConcat(fPtsToUnit);
672 SkScalar diffLen = fDiff.length();
673 if (0 != diffLen) {
674 SkScalar invDiffLen = SkScalarInvert(diffLen);
675 SkMatrix rot;
676 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
677 SkScalarMul(invDiffLen, fDiff.fX));
678 matrix.postConcat(rot);
679 }
681 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
682 }
684 #else
686 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
687 SkDEBUGFAIL("Should not call in GPU-less build");
688 return NULL;
689 }
691 #endif