blob: 6784818761b8591ba41eefea4466d8de5c0c8880 [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,
173 const SkColor colors[], const SkScalar pos[],
174 int colorCount, SkShader::TileMode mode,
175 SkUnitMapper* mapper)
176 : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
177 fCenter1(start),
178 fCenter2(end),
179 fRadius1(startRadius),
180 fRadius2(endRadius) {
181 init();
182}
183
184SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
185 SkBitmap* bitmap,
186 SkMatrix* matrix,
187 SkShader::TileMode* xy) const {
188 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000189 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000190 }
191 SkScalar diffL = 0; // just to avoid gcc warning
192 if (matrix) {
193 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
194 SkScalarSquare(fDiff.fY));
195 }
196 if (matrix) {
197 if (diffL) {
198 SkScalar invDiffL = SkScalarInvert(diffL);
199 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
200 SkScalarMul(invDiffL, fDiff.fX));
201 } else {
202 matrix->reset();
203 }
204 matrix->preConcat(fPtsToUnit);
205 }
206 if (xy) {
207 xy[0] = fTileMode;
208 xy[1] = kClamp_TileMode;
209 }
210 return kTwoPointRadial_BitmapType;
211}
212
213SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
214 SkShader::GradientInfo* info) const {
215 if (info) {
216 commonAsAGradient(info);
217 info->fPoint[0] = fCenter1;
218 info->fPoint[1] = fCenter2;
219 info->fRadius[0] = fRadius1;
220 info->fRadius[1] = fRadius2;
221 }
222 return kRadial2_GradientType;
223}
224
rileya@google.com589708b2012-07-26 20:04:23 +0000225void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
226 int count) {
227 SkASSERT(count > 0);
228
229 SkPMColor* SK_RESTRICT dstC = dstCParam;
230
231 // Zero difference between radii: fill with transparent black.
232 if (fDiffRadius == 0) {
233 sk_bzero(dstC, count * sizeof(*dstC));
234 return;
235 }
236 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
237 TileProc proc = fTileProc;
238 const SkPMColor* SK_RESTRICT cache = this->getCache32();
239
240 SkScalar foura = fA * 4;
241 bool posRoot = fDiffRadius < 0;
242 if (fDstToIndexClass != kPerspective_MatrixClass) {
243 SkPoint srcPt;
244 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
245 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
246 SkScalar dx, fx = srcPt.fX;
247 SkScalar dy, fy = srcPt.fY;
248
249 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
250 SkFixed fixedX, fixedY;
251 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
252 dx = SkFixedToScalar(fixedX);
253 dy = SkFixedToScalar(fixedY);
254 } else {
255 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
256 dx = fDstToIndex.getScaleX();
257 dy = fDstToIndex.getSkewY();
258 }
259 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
260 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
261 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
262 SkScalarMul(fDiff.fY, dy)) * 2;
263
264 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
265 if (SkShader::kClamp_TileMode == fTileMode) {
266 shadeProc = shadeSpan_twopoint_clamp;
267 } else if (SkShader::kMirror_TileMode == fTileMode) {
268 shadeProc = shadeSpan_twopoint_mirror;
269 } else {
270 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
271 }
272 (*shadeProc)(fx, dx, fy, dy, b, db,
273 fSr2D2, foura, fOneOverTwoA, posRoot,
274 dstC, cache, count);
275 } else { // perspective case
276 SkScalar dstX = SkIntToScalar(x);
277 SkScalar dstY = SkIntToScalar(y);
278 for (; count > 0; --count) {
279 SkPoint srcPt;
280 dstProc(fDstToIndex, dstX, dstY, &srcPt);
281 SkScalar fx = srcPt.fX;
282 SkScalar fy = srcPt.fY;
283 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
284 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
285 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
286 fOneOverTwoA, posRoot);
287 SkFixed index = proc(t);
288 SkASSERT(index <= 0xFFFF);
289 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
290 dstX += SK_Scalar1;
291 }
292 }
293}
294
reed@google.coma641f3f2012-12-13 22:16:30 +0000295bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
296 const SkPaint& paint,
297 const SkMatrix& matrix){
298 // For now, we might have divided by zero, so detect that
299 if (0 == fDiffRadius) {
rileya@google.com589708b2012-07-26 20:04:23 +0000300 return false;
301 }
302
reed@google.coma641f3f2012-12-13 22:16:30 +0000303 if (!this->INHERITED::setContext(device, paint, matrix)) {
rileya@google.com589708b2012-07-26 20:04:23 +0000304 return false;
305 }
306
307 // we don't have a span16 proc
308 fFlags &= ~kHasSpan16_Flag;
309 return true;
310}
311
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000312#ifdef SK_DEVELOPER
313void SkTwoPointRadialGradient::toString(SkString* str) const {
314 str->append("SkTwoPointRadialGradient: (");
315
316 str->append("center1: (");
317 str->appendScalar(fCenter1.fX);
318 str->append(", ");
319 str->appendScalar(fCenter1.fY);
320 str->append(") radius1: ");
321 str->appendScalar(fRadius1);
322 str->append(" ");
323
324 str->append("center2: (");
325 str->appendScalar(fCenter2.fX);
326 str->append(", ");
327 str->appendScalar(fCenter2.fY);
328 str->append(") radius2: ");
329 str->appendScalar(fRadius2);
330 str->append(" ");
331
332 this->INHERITED::toString(str);
333
334 str->append(")");
335}
336#endif
337
rileya@google.com589708b2012-07-26 20:04:23 +0000338SkTwoPointRadialGradient::SkTwoPointRadialGradient(
339 SkFlattenableReadBuffer& buffer)
340 : INHERITED(buffer),
341 fCenter1(buffer.readPoint()),
342 fCenter2(buffer.readPoint()),
343 fRadius1(buffer.readScalar()),
344 fRadius2(buffer.readScalar()) {
345 init();
346};
347
348void SkTwoPointRadialGradient::flatten(
349 SkFlattenableWriteBuffer& buffer) const {
350 this->INHERITED::flatten(buffer);
351 buffer.writePoint(fCenter1);
352 buffer.writePoint(fCenter2);
353 buffer.writeScalar(fRadius1);
354 buffer.writeScalar(fRadius2);
355}
356
357void SkTwoPointRadialGradient::init() {
358 fDiff = fCenter1 - fCenter2;
359 fDiffRadius = fRadius2 - fRadius1;
360 // hack to avoid zero-divide for now
361 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
362 fDiff.fX = SkScalarMul(fDiff.fX, inv);
363 fDiff.fY = SkScalarMul(fDiff.fY, inv);
364 fStartRadius = SkScalarMul(fRadius1, inv);
365 fSr2D2 = SkScalarSquare(fStartRadius);
366 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
367 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
368
369 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
370 fPtsToUnit.postScale(inv, inv);
371}
372
rileya@google.comd7cc6512012-07-27 14:00:39 +0000373/////////////////////////////////////////////////////////////////////
374
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000375#if SK_SUPPORT_GPU
376
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000377#include "GrTBackendEffectFactory.h"
378
rileya@google.comd7cc6512012-07-27 14:00:39 +0000379// For brevity
380typedef GrGLUniformManager::UniformHandle UniformHandle;
381static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
382
bsalomon@google.com0707c292012-10-25 21:45:42 +0000383class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000384
385public:
386
bsalomon@google.comc7818882013-03-20 19:19:53 +0000387 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000388 virtual ~GrGLRadial2Gradient() { }
389
bsalomon@google.comf78df332012-10-29 12:43:38 +0000390 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000391 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000392 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000393 const char* outputColor,
394 const char* inputColor,
395 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000396 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000397
bsalomon@google.comc7818882013-03-20 19:19:53 +0000398 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000399
400protected:
401
402 UniformHandle fVSParamUni;
403 UniformHandle fFSParamUni;
404
405 const char* fVSVaryingName;
406 const char* fFSVaryingName;
407
408 bool fIsDegenerate;
409
410 // @{
411 /// Values last uploaded as uniforms
412
bsalomon@google.com81712882012-11-01 17:12:34 +0000413 SkScalar fCachedCenter;
414 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000415 bool fCachedPosRoot;
416
417 // @}
418
419private:
420
bsalomon@google.com0707c292012-10-25 21:45:42 +0000421 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000422
423};
424
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000425/////////////////////////////////////////////////////////////////////
426
427class GrRadial2Gradient : public GrGradientEffect {
428public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000429 static GrEffectRef* Create(GrContext* ctx,
430 const SkTwoPointRadialGradient& shader,
431 const SkMatrix& matrix,
432 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000433 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000434 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000435 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000436
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000437 virtual ~GrRadial2Gradient() { }
438
439 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000440 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
441 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000442 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000443
444 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000445 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
446 SkScalar center() const { return fCenterX1; }
447 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448 bool isPosRoot() const { return SkToBool(fPosRoot); }
449
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000450 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000451
452private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000453 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000454 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000455 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000456 this->fCenterX1 == s.fCenterX1 &&
457 this->fRadius0 == s.fRadius0 &&
458 this->fPosRoot == s.fPosRoot);
459 }
460
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000461 GrRadial2Gradient(GrContext* ctx,
462 const SkTwoPointRadialGradient& shader,
463 const SkMatrix& matrix,
464 SkShader::TileMode tm)
465 : INHERITED(ctx, shader, matrix, tm)
466 , fCenterX1(shader.getCenterX1())
467 , fRadius0(shader.getStartRadius())
468 , fPosRoot(shader.getDiffRadius() < 0) { }
469
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000470 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000471
472 // @{
473 // Cache of values - these can change arbitrarily, EXCEPT
474 // we shouldn't change between degenerate and non-degenerate?!
475
bsalomon@google.com81712882012-11-01 17:12:34 +0000476 SkScalar fCenterX1;
477 SkScalar fRadius0;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000478 SkBool8 fPosRoot;
479
480 // @}
481
482 typedef GrGradientEffect INHERITED;
483};
484
485/////////////////////////////////////////////////////////////////////
486
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000487GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000488
bsalomon@google.com73a96942013-02-13 16:31:19 +0000489GrEffectRef* GrRadial2Gradient::TestCreate(SkMWCRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000490 GrContext* context,
491 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000492 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
493 SkScalar radius1 = random->nextUScalar1();
494 SkPoint center2;
495 SkScalar radius2;
496 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000497 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000498 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000499 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000500 } while (radius1 == radius2);
501
502 SkColor colors[kMaxRandomGradientColors];
503 SkScalar stopsArray[kMaxRandomGradientColors];
504 SkScalar* stops = stopsArray;
505 SkShader::TileMode tm;
506 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
507 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
508 center2, radius2,
509 colors, stops, colorCount,
510 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000511 SkPaint paint;
512 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000513}
514
515/////////////////////////////////////////////////////////////////////
516
bsalomon@google.com6340a412013-01-22 19:55:59 +0000517GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000518 const GrDrawEffect& drawEffect)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000519 : INHERITED(factory)
520 , fVSParamUni(kInvalidUniformHandle)
521 , fFSParamUni(kInvalidUniformHandle)
522 , fVSVaryingName(NULL)
523 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000524 , fCachedCenter(SK_ScalarMax)
525 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000526 , fCachedPosRoot(0) {
527
bsalomon@google.comc7818882013-03-20 19:19:53 +0000528 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000529 fIsDegenerate = data.isDegenerate();
530}
531
bsalomon@google.comf78df332012-10-29 12:43:38 +0000532void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000533 const GrDrawEffect& drawEffect,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000534 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000535 const char* outputColor,
536 const char* inputColor,
537 const TextureSamplerArray& samplers) {
538
539 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000540 const char* fsCoords;
541 const char* vsCoordsVarying;
542 GrSLType coordsVaryingType;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000543 this->setupMatrix(builder, key, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000544
rileya@google.comd7cc6512012-07-27 14:00:39 +0000545 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
546 // to work around Xoom bug. Doesn't seem to cause performance decrease
547 // in test apps, but need to keep an eye on it.
548 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
549 kFloat_GrSLType, "Radial2VSParams", 6);
550 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
551 kFloat_GrSLType, "Radial2FSParams", 6);
552
553 // For radial gradients without perspective we can pass the linear
554 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000555 if (kVec2f_GrSLType == coordsVaryingType) {
556 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000557 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000558
bsalomon@google.comf78df332012-10-29 12:43:38 +0000559 // VS
560 {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000561 SkString p2;
562 SkString p3;
563 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
564 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000565
bsalomon@google.comf78df332012-10-29 12:43:38 +0000566 // For radial gradients without perspective we can pass the linear
567 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000568 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000569 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000570 builder->vsCodeAppendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
571 fVSVaryingName, p2.c_str(),
572 vsCoordsVarying, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000573 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000574 }
575
bsalomon@google.comf78df332012-10-29 12:43:38 +0000576 // FS
577 {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000578 SkString cName("c");
579 SkString ac4Name("ac4");
580 SkString rootName("root");
581 SkString t;
582 SkString p0;
583 SkString p1;
584 SkString p2;
585 SkString p3;
586 SkString p4;
587 SkString p5;
588 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
589 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
590 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
591 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
592 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
593 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000594
bsalomon@google.comf78df332012-10-29 12:43:38 +0000595 // If we we're able to interpolate the linear component,
596 // bVar is the varying; otherwise compute it
597 SkString bVar;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000598 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000599 bVar = fFSVaryingName;
600 } else {
601 bVar = "b";
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000602 builder->fsCodeAppendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
603 bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000604 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000605
bsalomon@google.comf78df332012-10-29 12:43:38 +0000606 // c = (x^2)+(y^2) - params[4]
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000607 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
608 cName.c_str(),
609 fsCoords,
610 fsCoords,
611 p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000612
bsalomon@google.comf78df332012-10-29 12:43:38 +0000613 // If we aren't degenerate, emit some extra code, and accept a slightly
614 // more complex coord.
615 if (!fIsDegenerate) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000616
bsalomon@google.comf78df332012-10-29 12:43:38 +0000617 // ac4 = 4.0 * params[0] * c
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000618 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
619 ac4Name.c_str(), p0.c_str(),
620 cName.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000621
622 // root = sqrt(b^2-4ac)
623 // (abs to avoid exception due to fp precision)
bsalomon@google.comf910d3b2013-03-07 17:06:57 +0000624 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
625 rootName.c_str(), bVar.c_str(), bVar.c_str(),
626 ac4Name.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000627
628 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
629 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
630 rootName.c_str(), p1.c_str());
631 } else {
632 // t is: -c/b
633 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
634 }
635
636 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000637 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000638}
639
bsalomon@google.comc7818882013-03-20 19:19:53 +0000640void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
641 const GrDrawEffect& drawEffect) {
642 INHERITED::setData(uman, drawEffect);
643 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000644 GrAssert(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000645 SkScalar centerX1 = data.center();
646 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000647 if (fCachedCenter != centerX1 ||
648 fCachedRadius != radius0 ||
649 fCachedPosRoot != data.isPosRoot()) {
650
bsalomon@google.com81712882012-11-01 17:12:34 +0000651 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000652
653 // When we're in the degenerate (linear) case, the second
654 // value will be INF but the program doesn't read it. (We
655 // use the same 6 uniforms even though we don't need them
656 // all in the linear case just to keep the code complexity
657 // down).
658 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000659 SkScalarToFloat(a),
660 1 / (2.f * SkScalarToFloat(a)),
661 SkScalarToFloat(centerX1),
662 SkScalarToFloat(radius0),
663 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000664 data.isPosRoot() ? 1.f : -1.f
665 };
666
667 uman.set1fv(fVSParamUni, 0, 6, values);
668 uman.set1fv(fFSParamUni, 0, 6, values);
669 fCachedCenter = centerX1;
670 fCachedRadius = radius0;
671 fCachedPosRoot = data.isPosRoot();
672 }
673}
674
bsalomon@google.comc7818882013-03-20 19:19:53 +0000675GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
676 const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000677 enum {
678 kIsDegenerate = 1 << kMatrixKeyBitCnt,
679 };
680
bsalomon@google.comc7818882013-03-20 19:19:53 +0000681 EffectKey key = GenMatrixKey(drawEffect);
682 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000683 key |= kIsDegenerate;
684 }
685 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000686}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000687
688/////////////////////////////////////////////////////////////////////
689
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000690GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000691 SkASSERT(NULL != context);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000692 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000693 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000694 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000695 return NULL;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000696 }
697 matrix.postConcat(fPtsToUnit);
698
699 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000700 if (0 != diffLen) {
701 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000702 SkMatrix rot;
703 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
704 SkScalarMul(invDiffLen, fDiff.fX));
705 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000706 }
707
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000708 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000709}
710
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000711#else
712
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000713GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000714 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000715 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000716}
717
718#endif