blob: 6615c4d3736d3253d64c6d39af5a844c4ac71949 [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
312SkTwoPointRadialGradient::SkTwoPointRadialGradient(
313 SkFlattenableReadBuffer& buffer)
314 : INHERITED(buffer),
315 fCenter1(buffer.readPoint()),
316 fCenter2(buffer.readPoint()),
317 fRadius1(buffer.readScalar()),
318 fRadius2(buffer.readScalar()) {
319 init();
320};
321
322void SkTwoPointRadialGradient::flatten(
323 SkFlattenableWriteBuffer& buffer) const {
324 this->INHERITED::flatten(buffer);
325 buffer.writePoint(fCenter1);
326 buffer.writePoint(fCenter2);
327 buffer.writeScalar(fRadius1);
328 buffer.writeScalar(fRadius2);
329}
330
331void SkTwoPointRadialGradient::init() {
332 fDiff = fCenter1 - fCenter2;
333 fDiffRadius = fRadius2 - fRadius1;
334 // hack to avoid zero-divide for now
335 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
336 fDiff.fX = SkScalarMul(fDiff.fX, inv);
337 fDiff.fY = SkScalarMul(fDiff.fY, inv);
338 fStartRadius = SkScalarMul(fRadius1, inv);
339 fSr2D2 = SkScalarSquare(fStartRadius);
340 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
341 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
342
343 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
344 fPtsToUnit.postScale(inv, inv);
345}
346
rileya@google.comd7cc6512012-07-27 14:00:39 +0000347/////////////////////////////////////////////////////////////////////
348
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000349#if SK_SUPPORT_GPU
350
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000351#include "GrTBackendEffectFactory.h"
352
rileya@google.comd7cc6512012-07-27 14:00:39 +0000353// For brevity
354typedef GrGLUniformManager::UniformHandle UniformHandle;
355static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
356
bsalomon@google.com0707c292012-10-25 21:45:42 +0000357class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000358
359public:
360
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000361 GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000362 const GrEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000363 virtual ~GrGLRadial2Gradient() { }
364
bsalomon@google.comf78df332012-10-29 12:43:38 +0000365 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000366 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000367 EffectKey,
368 const char* vertexCoords,
369 const char* outputColor,
370 const char* inputColor,
371 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.com28a15fb2012-10-26 17:53:18 +0000372 virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000373
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000374 static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000375
376protected:
377
378 UniformHandle fVSParamUni;
379 UniformHandle fFSParamUni;
380
381 const char* fVSVaryingName;
382 const char* fFSVaryingName;
383
384 bool fIsDegenerate;
385
386 // @{
387 /// Values last uploaded as uniforms
388
bsalomon@google.com81712882012-11-01 17:12:34 +0000389 SkScalar fCachedCenter;
390 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000391 bool fCachedPosRoot;
392
393 // @}
394
395private:
396
bsalomon@google.com0707c292012-10-25 21:45:42 +0000397 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000398
399};
400
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000401/////////////////////////////////////////////////////////////////////
402
403class GrRadial2Gradient : public GrGradientEffect {
404public:
405
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000406 GrRadial2Gradient(GrContext* ctx,
407 const SkTwoPointRadialGradient& shader,
408 const SkMatrix& matrix,
409 SkShader::TileMode tm)
410 : INHERITED(ctx, shader, matrix, tm)
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000411 , fCenterX1(shader.getCenterX1())
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000412 , fRadius0(shader.getStartRadius())
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000413 , fPosRoot(shader.getDiffRadius() < 0) { }
414 virtual ~GrRadial2Gradient() { }
415
416 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000417 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
418 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000419 }
bsalomon@google.coma469c282012-10-24 18:28:34 +0000420 virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000421 const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
422 return (INHERITED::isEqual(sBase) &&
423 this->fCenterX1 == s.fCenterX1 &&
424 this->fRadius0 == s.fRadius0 &&
425 this->fPosRoot == s.fPosRoot);
426 }
427
428 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000429 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
430 SkScalar center() const { return fCenterX1; }
431 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000432 bool isPosRoot() const { return SkToBool(fPosRoot); }
433
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000434 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000435
436private:
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000437 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000438
439 // @{
440 // Cache of values - these can change arbitrarily, EXCEPT
441 // we shouldn't change between degenerate and non-degenerate?!
442
bsalomon@google.com81712882012-11-01 17:12:34 +0000443 SkScalar fCenterX1;
444 SkScalar fRadius0;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000445 SkBool8 fPosRoot;
446
447 // @}
448
449 typedef GrGradientEffect INHERITED;
450};
451
452/////////////////////////////////////////////////////////////////////
453
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000454GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000455
bsalomon@google.coma469c282012-10-24 18:28:34 +0000456GrEffect* GrRadial2Gradient::TestCreate(SkRandom* random,
457 GrContext* context,
458 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000459 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
460 SkScalar radius1 = random->nextUScalar1();
461 SkPoint center2;
462 SkScalar radius2;
463 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000464 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000465 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000466 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000467 } while (radius1 == radius2);
468
469 SkColor colors[kMaxRandomGradientColors];
470 SkScalar stopsArray[kMaxRandomGradientColors];
471 SkScalar* stops = stopsArray;
472 SkShader::TileMode tm;
473 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
474 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
475 center2, radius2,
476 colors, stops, colorCount,
477 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000478 SkPaint paint;
479 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000480}
481
482/////////////////////////////////////////////////////////////////////
483
rileya@google.comd7cc6512012-07-27 14:00:39 +0000484GrGLRadial2Gradient::GrGLRadial2Gradient(
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000485 const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000486 const GrEffect& baseData)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487 : INHERITED(factory)
488 , fVSParamUni(kInvalidUniformHandle)
489 , fFSParamUni(kInvalidUniformHandle)
490 , fVSVaryingName(NULL)
491 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000492 , fCachedCenter(SK_ScalarMax)
493 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000494 , fCachedPosRoot(0) {
495
496 const GrRadial2Gradient& data =
497 static_cast<const GrRadial2Gradient&>(baseData);
498 fIsDegenerate = data.isDegenerate();
499}
500
bsalomon@google.comf78df332012-10-29 12:43:38 +0000501void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000502 const GrEffectStage& stage,
503 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000504 const char* vertexCoords,
505 const char* outputColor,
506 const char* inputColor,
507 const TextureSamplerArray& samplers) {
508
509 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000510 const char* fsCoords;
511 const char* vsCoordsVarying;
512 GrSLType coordsVaryingType;
513 this->setupMatrix(builder, key, vertexCoords, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
514
rileya@google.comd7cc6512012-07-27 14:00:39 +0000515 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
516 // to work around Xoom bug. Doesn't seem to cause performance decrease
517 // in test apps, but need to keep an eye on it.
518 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
519 kFloat_GrSLType, "Radial2VSParams", 6);
520 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
521 kFloat_GrSLType, "Radial2FSParams", 6);
522
523 // For radial gradients without perspective we can pass the linear
524 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000525 if (kVec2f_GrSLType == coordsVaryingType) {
526 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000527 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000528
bsalomon@google.comf78df332012-10-29 12:43:38 +0000529 // VS
530 {
531 SkString* code = &builder->fVSCode;
532 SkString p2;
533 SkString p3;
534 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
535 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000536
bsalomon@google.comf78df332012-10-29 12:43:38 +0000537 // For radial gradients without perspective we can pass the linear
538 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000539 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000540 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
541 code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
542 fVSVaryingName, p2.c_str(),
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000543 vsCoordsVarying, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000544 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000545 }
546
bsalomon@google.comf78df332012-10-29 12:43:38 +0000547 // FS
548 {
549 SkString* code = &builder->fFSCode;
550 SkString cName("c");
551 SkString ac4Name("ac4");
552 SkString rootName("root");
553 SkString t;
554 SkString p0;
555 SkString p1;
556 SkString p2;
557 SkString p3;
558 SkString p4;
559 SkString p5;
560 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
561 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
562 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
563 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
564 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
565 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000566
bsalomon@google.comf78df332012-10-29 12:43:38 +0000567 // If we we're able to interpolate the linear component,
568 // bVar is the varying; otherwise compute it
569 SkString bVar;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000570 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000571 bVar = fFSVaryingName;
572 } else {
573 bVar = "b";
574 code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000575 bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000576 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000577
bsalomon@google.comf78df332012-10-29 12:43:38 +0000578 // c = (x^2)+(y^2) - params[4]
579 code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
580 cName.c_str(),
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000581 fsCoords,
582 fsCoords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000583 p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000584
bsalomon@google.comf78df332012-10-29 12:43:38 +0000585 // If we aren't degenerate, emit some extra code, and accept a slightly
586 // more complex coord.
587 if (!fIsDegenerate) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000588
bsalomon@google.comf78df332012-10-29 12:43:38 +0000589 // ac4 = 4.0 * params[0] * c
590 code->appendf("\tfloat %s = %s * 4.0 * %s;\n",
591 ac4Name.c_str(), p0.c_str(),
592 cName.c_str());
593
594 // root = sqrt(b^2-4ac)
595 // (abs to avoid exception due to fp precision)
596 code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
597 rootName.c_str(), bVar.c_str(), bVar.c_str(),
598 ac4Name.c_str());
599
600 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
601 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
602 rootName.c_str(), p1.c_str());
603 } else {
604 // t is: -c/b
605 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
606 }
607
608 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000609 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000610}
611
bsalomon@google.com28a15fb2012-10-26 17:53:18 +0000612void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
613 INHERITED::setData(uman, stage);
614 const GrRadial2Gradient& data = static_cast<const GrRadial2Gradient&>(*stage.getEffect());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000615 GrAssert(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000616 SkScalar centerX1 = data.center();
617 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000618 if (fCachedCenter != centerX1 ||
619 fCachedRadius != radius0 ||
620 fCachedPosRoot != data.isPosRoot()) {
621
bsalomon@google.com81712882012-11-01 17:12:34 +0000622 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000623
624 // When we're in the degenerate (linear) case, the second
625 // value will be INF but the program doesn't read it. (We
626 // use the same 6 uniforms even though we don't need them
627 // all in the linear case just to keep the code complexity
628 // down).
629 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000630 SkScalarToFloat(a),
631 1 / (2.f * SkScalarToFloat(a)),
632 SkScalarToFloat(centerX1),
633 SkScalarToFloat(radius0),
634 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000635 data.isPosRoot() ? 1.f : -1.f
636 };
637
638 uman.set1fv(fVSParamUni, 0, 6, values);
639 uman.set1fv(fFSParamUni, 0, 6, values);
640 fCachedCenter = centerX1;
641 fCachedRadius = radius0;
642 fCachedPosRoot = data.isPosRoot();
643 }
644}
645
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000646GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000647 enum {
648 kIsDegenerate = 1 << kMatrixKeyBitCnt,
649 };
650
651 EffectKey key = GenMatrixKey(s);
652 if (static_cast<const GrRadial2Gradient&>(*s.getEffect()).isDegenerate()) {
653 key |= kIsDegenerate;
654 }
655 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000656}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000657
658/////////////////////////////////////////////////////////////////////
659
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000660GrEffect* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com08283af2012-10-26 13:01:20 +0000661 SkASSERT(NULL != context && NULL != stage);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000662 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000663 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000664 if (!this->getLocalMatrix().invert(&matrix)) {
665 return false;
666 }
667 matrix.postConcat(fPtsToUnit);
668
669 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000670 if (0 != diffLen) {
671 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000672 SkMatrix rot;
673 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
674 SkScalarMul(invDiffLen, fDiff.fX));
675 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000676 }
677
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000678 return SkNEW_ARGS(GrRadial2Gradient, (context, *this, matrix, fTileMode));
rileya@google.comd7cc6512012-07-27 14:00:39 +0000679}
680
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000681#else
682
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000683GrEffect* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000684 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000685 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000686}
687
688#endif