blob: e8ec73625b76c7bc2be585c7b740047650b16f34 [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001
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 */
8
9 #include "SkTwoPointRadialGradient.h"
10
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).
15
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):
21
22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
23 Cy = (1 - t) * Sy + t * Ey
24
25 The radius of the desired circle (r) is also a linear interpolation t
26 between the start and end radii (Sr and Er):
27
28 r = (1 - t) * Sr + t * Er
29
30 But
31
32 (x - Cx)^2 + (y - Cy)^2 = r^2
33
34 so
35
36 (x - ((1 - t) * Sx + t * Ex))^2
37 + (y - ((1 - t) * Sy + t * Ey))^2
38 = ((1 - t) * Sr + t * Er)^2
39
40 Solving for t yields
41
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
45
46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
47
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
51
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.
55
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.
63
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
67
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
71
72 (x' and y' are computed by appending the subtract and scale to the
73 fDstToIndex matrix in the constructor).
74
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.
80
81*/
82
83namespace {
84
85inline 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 }
92
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}
106
107typedef 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);
113
114void 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}
131void 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}
148
149void 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}
167
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000168/////////////////////////////////////////////////////////////////////
169
rileya@google.com589708b2012-07-26 20:04:23 +0000170SkTwoPointRadialGradient::SkTwoPointRadialGradient(
171 const SkPoint& start, SkScalar startRadius,
172 const SkPoint& end, SkScalar endRadius,
reed@google.com437d6eb2013-05-23 19:03:05 +0000173 const Descriptor& desc)
174 : SkGradientShaderBase(desc),
rileya@google.com589708b2012-07-26 20:04:23 +0000175 fCenter1(start),
176 fCenter2(end),
177 fRadius1(startRadius),
178 fRadius2(endRadius) {
179 init();
180}
181
182SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
183 SkBitmap* bitmap,
184 SkMatrix* matrix,
185 SkShader::TileMode* xy) const {
186 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000187 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000188 }
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}
210
211SkShader::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}
222
rileya@google.com589708b2012-07-26 20:04:23 +0000223void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
224 int count) {
225 SkASSERT(count > 0);
226
227 SkPMColor* SK_RESTRICT dstC = dstCParam;
228
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();
237
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;
246
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;
261
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}
292
reed@google.coma641f3f2012-12-13 22:16:30 +0000293bool 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) {
rileya@google.com589708b2012-07-26 20:04:23 +0000298 return false;
299 }
300
reed@google.coma641f3f2012-12-13 22:16:30 +0000301 if (!this->INHERITED::setContext(device, paint, matrix)) {
rileya@google.com589708b2012-07-26 20:04:23 +0000302 return false;
303 }
304
305 // we don't have a span16 proc
306 fFlags &= ~kHasSpan16_Flag;
307 return true;
308}
309
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000310#ifdef SK_DEVELOPER
311void SkTwoPointRadialGradient::toString(SkString* str) const {
312 str->append("SkTwoPointRadialGradient: (");
313
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(" ");
321
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(" ");
329
330 this->INHERITED::toString(str);
331
332 str->append(")");
333}
334#endif
335
rileya@google.com589708b2012-07-26 20:04:23 +0000336SkTwoPointRadialGradient::SkTwoPointRadialGradient(
337 SkFlattenableReadBuffer& buffer)
338 : INHERITED(buffer),
339 fCenter1(buffer.readPoint()),
340 fCenter2(buffer.readPoint()),
341 fRadius1(buffer.readScalar()),
342 fRadius2(buffer.readScalar()) {
343 init();
344};
345
346void SkTwoPointRadialGradient::flatten(
347 SkFlattenableWriteBuffer& buffer) const {
348 this->INHERITED::flatten(buffer);
349 buffer.writePoint(fCenter1);
350 buffer.writePoint(fCenter2);
351 buffer.writeScalar(fRadius1);
352 buffer.writeScalar(fRadius2);
353}
354
355void 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;
366
367 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
368 fPtsToUnit.postScale(inv, inv);
369}
370
rileya@google.comd7cc6512012-07-27 14:00:39 +0000371/////////////////////////////////////////////////////////////////////
372
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000373#if SK_SUPPORT_GPU
374
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000375#include "GrTBackendEffectFactory.h"
376
rileya@google.comd7cc6512012-07-27 14:00:39 +0000377// For brevity
378typedef GrGLUniformManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000379
bsalomon@google.com0707c292012-10-25 21:45:42 +0000380class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000381
382public:
383
bsalomon@google.comc7818882013-03-20 19:19:53 +0000384 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000385 virtual ~GrGLRadial2Gradient() { }
386
bsalomon@google.comf78df332012-10-29 12:43:38 +0000387 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000388 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000389 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000390 const char* outputColor,
391 const char* inputColor,
392 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000393 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000394
bsalomon@google.comc7818882013-03-20 19:19:53 +0000395 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000396
397protected:
398
399 UniformHandle fVSParamUni;
400 UniformHandle fFSParamUni;
401
402 const char* fVSVaryingName;
403 const char* fFSVaryingName;
404
405 bool fIsDegenerate;
406
407 // @{
408 /// Values last uploaded as uniforms
409
bsalomon@google.com81712882012-11-01 17:12:34 +0000410 SkScalar fCachedCenter;
411 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000412 bool fCachedPosRoot;
413
414 // @}
415
416private:
417
bsalomon@google.com0707c292012-10-25 21:45:42 +0000418 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000419
420};
421
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000422/////////////////////////////////////////////////////////////////////
423
424class GrRadial2Gradient : public GrGradientEffect {
425public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000426 static GrEffectRef* Create(GrContext* ctx,
427 const SkTwoPointRadialGradient& shader,
428 const SkMatrix& matrix,
429 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000430 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000431 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000432 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000433
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000434 virtual ~GrRadial2Gradient() { }
435
436 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000437 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
438 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000439 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000440
441 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000442 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
443 SkScalar center() const { return fCenterX1; }
444 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000445 bool isPosRoot() const { return SkToBool(fPosRoot); }
446
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000447 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448
449private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000450 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000451 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000452 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000453 this->fCenterX1 == s.fCenterX1 &&
454 this->fRadius0 == s.fRadius0 &&
455 this->fPosRoot == s.fPosRoot);
456 }
457
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000458 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
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000467 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000468
469 // @{
470 // Cache of values - these can change arbitrarily, EXCEPT
471 // we shouldn't change between degenerate and non-degenerate?!
472
bsalomon@google.com81712882012-11-01 17:12:34 +0000473 SkScalar fCenterX1;
474 SkScalar fRadius0;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000475 SkBool8 fPosRoot;
476
477 // @}
478
479 typedef GrGradientEffect INHERITED;
480};
481
482/////////////////////////////////////////////////////////////////////
483
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000484GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000485
bsalomon@google.com73a96942013-02-13 16:31:19 +0000486GrEffectRef* GrRadial2Gradient::TestCreate(SkMWCRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000487 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000488 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000489 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000490 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
491 SkScalar radius1 = random->nextUScalar1();
492 SkPoint center2;
493 SkScalar radius2;
494 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000495 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000496 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000497 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000498 } while (radius1 == radius2);
499
500 SkColor colors[kMaxRandomGradientColors];
501 SkScalar stopsArray[kMaxRandomGradientColors];
502 SkScalar* stops = stopsArray;
503 SkShader::TileMode tm;
504 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
505 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
506 center2, radius2,
507 colors, stops, colorCount,
508 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000509 SkPaint paint;
510 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000511}
512
513/////////////////////////////////////////////////////////////////////
514
bsalomon@google.com6340a412013-01-22 19:55:59 +0000515GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000516 const GrDrawEffect& drawEffect)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000517 : INHERITED(factory)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000518 , fVSVaryingName(NULL)
519 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000520 , fCachedCenter(SK_ScalarMax)
521 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000522 , fCachedPosRoot(0) {
523
bsalomon@google.comc7818882013-03-20 19:19:53 +0000524 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000525 fIsDegenerate = data.isDegenerate();
526}
527
bsalomon@google.comf78df332012-10-29 12:43:38 +0000528void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000529 const GrDrawEffect& drawEffect,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000530 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000531 const char* outputColor,
532 const char* inputColor,
533 const TextureSamplerArray& samplers) {
534
535 this->emitYCoordUniform(builder);
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000536 SkString fsCoords;
537 SkString vsCoordsVarying;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000538 GrSLType coordsVaryingType;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000539 this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000540
rileya@google.comd7cc6512012-07-27 14:00:39 +0000541 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
542 // to work around Xoom bug. Doesn't seem to cause performance decrease
543 // in test apps, but need to keep an eye on it.
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000544 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_Visibility,
rileya@google.comd7cc6512012-07-27 14:00:39 +0000545 kFloat_GrSLType, "Radial2VSParams", 6);
commit-bot@chromium.org74a3a212013-08-30 19:43:59 +0000546 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
rileya@google.comd7cc6512012-07-27 14:00:39 +0000547 kFloat_GrSLType, "Radial2FSParams", 6);
548
549 // For radial gradients without perspective we can pass the linear
550 // part of the quadratic as a varying.
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000551 GrGLShaderBuilder::VertexBuilder* vertexBuilder =
552 (kVec2f_GrSLType == coordsVaryingType) ? builder->getVertexBuilder() : NULL;
553 if (NULL != vertexBuilder) {
554 vertexBuilder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
555 &fVSVaryingName, &fFSVaryingName);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000556 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000557
bsalomon@google.comf78df332012-10-29 12:43:38 +0000558 // VS
559 {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000560 SkString p2;
561 SkString p3;
562 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
563 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564
bsalomon@google.comf78df332012-10-29 12:43:38 +0000565 // For radial gradients without perspective we can pass the linear
566 // part of the quadratic as a varying.
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000567 if (NULL != vertexBuilder) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000568 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000569 vertexBuilder->vsCodeAppendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
570 fVSVaryingName, p2.c_str(),
571 vsCoordsVarying.c_str(), p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000572 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000573 }
574
bsalomon@google.comf78df332012-10-29 12:43:38 +0000575 // FS
576 {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000577 SkString cName("c");
578 SkString ac4Name("ac4");
579 SkString rootName("root");
580 SkString t;
581 SkString p0;
582 SkString p1;
583 SkString p2;
584 SkString p3;
585 SkString p4;
586 SkString p5;
587 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
588 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
589 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
590 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
591 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
592 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000593
bsalomon@google.comf78df332012-10-29 12:43:38 +0000594 // If we we're able to interpolate the linear component,
595 // bVar is the varying; otherwise compute it
596 SkString bVar;
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000597 if (NULL != vertexBuilder) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000598 bVar = fFSVaryingName;
599 } else {
600 bVar = "b";
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000601 builder->fsCodeAppendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000602 bVar.c_str(), p2.c_str(), fsCoords.c_str(), p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000603 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000604
bsalomon@google.comf78df332012-10-29 12:43:38 +0000605 // c = (x^2)+(y^2) - params[4]
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000606 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
607 cName.c_str(),
commit-bot@chromium.org7ab7ca42013-08-28 15:59:13 +0000608 fsCoords.c_str(),
609 fsCoords.c_str(),
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000610 p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000611
bsalomon@google.comf78df332012-10-29 12:43:38 +0000612 // If we aren't degenerate, emit some extra code, and accept a slightly
613 // more complex coord.
614 if (!fIsDegenerate) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000615
bsalomon@google.comf78df332012-10-29 12:43:38 +0000616 // ac4 = 4.0 * params[0] * c
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000617 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
618 ac4Name.c_str(), p0.c_str(),
619 cName.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000620
621 // root = sqrt(b^2-4ac)
622 // (abs to avoid exception due to fp precision)
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000623 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
624 rootName.c_str(), bVar.c_str(), bVar.c_str(),
625 ac4Name.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000626
627 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
628 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
629 rootName.c_str(), p1.c_str());
630 } else {
631 // t is: -c/b
632 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
633 }
634
635 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000636 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000637}
638
bsalomon@google.comc7818882013-03-20 19:19:53 +0000639void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
640 const GrDrawEffect& drawEffect) {
641 INHERITED::setData(uman, drawEffect);
642 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000643 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000644 SkScalar centerX1 = data.center();
645 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000646 if (fCachedCenter != centerX1 ||
647 fCachedRadius != radius0 ||
648 fCachedPosRoot != data.isPosRoot()) {
649
bsalomon@google.com81712882012-11-01 17:12:34 +0000650 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000651
652 // When we're in the degenerate (linear) case, the second
653 // value will be INF but the program doesn't read it. (We
654 // use the same 6 uniforms even though we don't need them
655 // all in the linear case just to keep the code complexity
656 // down).
657 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000658 SkScalarToFloat(a),
659 1 / (2.f * SkScalarToFloat(a)),
660 SkScalarToFloat(centerX1),
661 SkScalarToFloat(radius0),
662 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000663 data.isPosRoot() ? 1.f : -1.f
664 };
665
666 uman.set1fv(fVSParamUni, 0, 6, values);
667 uman.set1fv(fFSParamUni, 0, 6, values);
668 fCachedCenter = centerX1;
669 fCachedRadius = radius0;
670 fCachedPosRoot = data.isPosRoot();
671 }
672}
673
bsalomon@google.comc7818882013-03-20 19:19:53 +0000674GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
675 const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000676 enum {
677 kIsDegenerate = 1 << kMatrixKeyBitCnt,
678 };
679
bsalomon@google.comc7818882013-03-20 19:19:53 +0000680 EffectKey key = GenMatrixKey(drawEffect);
681 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000682 key |= kIsDegenerate;
683 }
684 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000685}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000686
687/////////////////////////////////////////////////////////////////////
688
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000689GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000690 SkASSERT(NULL != context);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000691 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000692 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000693 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000694 return NULL;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000695 }
696 matrix.postConcat(fPtsToUnit);
697
698 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000699 if (0 != diffLen) {
700 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000701 SkMatrix rot;
702 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
703 SkScalarMul(invDiffLen, fDiff.fX));
704 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000705 }
706
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000707 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000708}
709
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000710#else
711
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000712GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000713 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000714 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000715}
716
717#endif