blob: 71b31260da582e1df16d529a868e7948e1d93eb2 [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
295bool SkTwoPointRadialGradient::setContext(
296 const SkBitmap& device,
297 const SkPaint& paint,
298 const SkMatrix& matrix){
299 if (!this->INHERITED::setContext(device, paint, matrix)) {
300 return false;
301 }
302
303 // For now, we might have divided by zero, so detect that
304 if (0 == fDiffRadius) {
305 return false;
306 }
307
308 // we don't have a span16 proc
309 fFlags &= ~kHasSpan16_Flag;
310 return true;
311}
312
313SkTwoPointRadialGradient::SkTwoPointRadialGradient(
314 SkFlattenableReadBuffer& buffer)
315 : INHERITED(buffer),
316 fCenter1(buffer.readPoint()),
317 fCenter2(buffer.readPoint()),
318 fRadius1(buffer.readScalar()),
319 fRadius2(buffer.readScalar()) {
320 init();
321};
322
323void SkTwoPointRadialGradient::flatten(
324 SkFlattenableWriteBuffer& buffer) const {
325 this->INHERITED::flatten(buffer);
326 buffer.writePoint(fCenter1);
327 buffer.writePoint(fCenter2);
328 buffer.writeScalar(fRadius1);
329 buffer.writeScalar(fRadius2);
330}
331
332void SkTwoPointRadialGradient::init() {
333 fDiff = fCenter1 - fCenter2;
334 fDiffRadius = fRadius2 - fRadius1;
335 // hack to avoid zero-divide for now
336 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
337 fDiff.fX = SkScalarMul(fDiff.fX, inv);
338 fDiff.fY = SkScalarMul(fDiff.fY, inv);
339 fStartRadius = SkScalarMul(fRadius1, inv);
340 fSr2D2 = SkScalarSquare(fStartRadius);
341 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
342 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
343
344 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
345 fPtsToUnit.postScale(inv, inv);
346}
347
rileya@google.comd7cc6512012-07-27 14:00:39 +0000348/////////////////////////////////////////////////////////////////////
349
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000350#if SK_SUPPORT_GPU
351
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000352#include "GrTBackendEffectFactory.h"
353
rileya@google.comd7cc6512012-07-27 14:00:39 +0000354// For brevity
355typedef GrGLUniformManager::UniformHandle UniformHandle;
356static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
357
bsalomon@google.com0707c292012-10-25 21:45:42 +0000358class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000359
360public:
361
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000362 GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000363 const GrEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000364 virtual ~GrGLRadial2Gradient() { }
365
bsalomon@google.comf78df332012-10-29 12:43:38 +0000366 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000367 const GrEffectStage&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000368 EffectKey,
369 const char* vertexCoords,
370 const char* outputColor,
371 const char* inputColor,
372 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.com28a15fb2012-10-26 17:53:18 +0000373 virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000374
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000375 static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000376
377protected:
378
379 UniformHandle fVSParamUni;
380 UniformHandle fFSParamUni;
381
382 const char* fVSVaryingName;
383 const char* fFSVaryingName;
384
385 bool fIsDegenerate;
386
387 // @{
388 /// Values last uploaded as uniforms
389
bsalomon@google.com81712882012-11-01 17:12:34 +0000390 SkScalar fCachedCenter;
391 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392 bool fCachedPosRoot;
393
394 // @}
395
396private:
397
bsalomon@google.com0707c292012-10-25 21:45:42 +0000398 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000399
400};
401
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000402/////////////////////////////////////////////////////////////////////
403
404class GrRadial2Gradient : public GrGradientEffect {
405public:
406
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000407 GrRadial2Gradient(GrContext* ctx,
408 const SkTwoPointRadialGradient& shader,
409 const SkMatrix& matrix,
410 SkShader::TileMode tm)
411 : INHERITED(ctx, shader, matrix, tm)
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000412 , fCenterX1(shader.getCenterX1())
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000413 , fRadius0(shader.getStartRadius())
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000414 , fPosRoot(shader.getDiffRadius() < 0) { }
415 virtual ~GrRadial2Gradient() { }
416
417 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000418 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
419 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000420 }
bsalomon@google.coma469c282012-10-24 18:28:34 +0000421 virtual bool isEqual(const GrEffect& sBase) const SK_OVERRIDE {
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000422 const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
423 return (INHERITED::isEqual(sBase) &&
424 this->fCenterX1 == s.fCenterX1 &&
425 this->fRadius0 == s.fRadius0 &&
426 this->fPosRoot == s.fPosRoot);
427 }
428
429 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000430 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
431 SkScalar center() const { return fCenterX1; }
432 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000433 bool isPosRoot() const { return SkToBool(fPosRoot); }
434
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000435 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000436
437private:
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000438 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000439
440 // @{
441 // Cache of values - these can change arbitrarily, EXCEPT
442 // we shouldn't change between degenerate and non-degenerate?!
443
bsalomon@google.com81712882012-11-01 17:12:34 +0000444 SkScalar fCenterX1;
445 SkScalar fRadius0;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000446 SkBool8 fPosRoot;
447
448 // @}
449
450 typedef GrGradientEffect INHERITED;
451};
452
453/////////////////////////////////////////////////////////////////////
454
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000455GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000456
bsalomon@google.coma469c282012-10-24 18:28:34 +0000457GrEffect* GrRadial2Gradient::TestCreate(SkRandom* random,
458 GrContext* context,
459 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000460 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
461 SkScalar radius1 = random->nextUScalar1();
462 SkPoint center2;
463 SkScalar radius2;
464 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000465 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000466 radius2 = random->nextUScalar1 ();
467 // There is a bug in two point radial gradients with idenitical radii
468 } while (radius1 == radius2);
469
470 SkColor colors[kMaxRandomGradientColors];
471 SkScalar stopsArray[kMaxRandomGradientColors];
472 SkScalar* stops = stopsArray;
473 SkShader::TileMode tm;
474 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
475 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
476 center2, radius2,
477 colors, stops, colorCount,
478 tm));
bsalomon@google.com08283af2012-10-26 13:01:20 +0000479 GrEffectStage stage;
480 shader->asNewEffect(context, &stage);
481 GrAssert(NULL != stage.getEffect());
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000482 // const_cast and ref is a hack! Will remove when asNewEffect returns GrEffect*
bsalomon@google.com08283af2012-10-26 13:01:20 +0000483 stage.getEffect()->ref();
484 return const_cast<GrEffect*>(stage.getEffect());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000485}
486
487/////////////////////////////////////////////////////////////////////
488
rileya@google.comd7cc6512012-07-27 14:00:39 +0000489GrGLRadial2Gradient::GrGLRadial2Gradient(
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000490 const GrBackendEffectFactory& factory,
bsalomon@google.coma469c282012-10-24 18:28:34 +0000491 const GrEffect& baseData)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000492 : INHERITED(factory)
493 , fVSParamUni(kInvalidUniformHandle)
494 , fFSParamUni(kInvalidUniformHandle)
495 , fVSVaryingName(NULL)
496 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000497 , fCachedCenter(SK_ScalarMax)
498 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000499 , fCachedPosRoot(0) {
500
501 const GrRadial2Gradient& data =
502 static_cast<const GrRadial2Gradient&>(baseData);
503 fIsDegenerate = data.isDegenerate();
504}
505
bsalomon@google.comf78df332012-10-29 12:43:38 +0000506void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000507 const GrEffectStage& stage,
508 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000509 const char* vertexCoords,
510 const char* outputColor,
511 const char* inputColor,
512 const TextureSamplerArray& samplers) {
513
514 this->emitYCoordUniform(builder);
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000515 const char* fsCoords;
516 const char* vsCoordsVarying;
517 GrSLType coordsVaryingType;
518 this->setupMatrix(builder, key, vertexCoords, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
519
rileya@google.comd7cc6512012-07-27 14:00:39 +0000520 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
521 // to work around Xoom bug. Doesn't seem to cause performance decrease
522 // in test apps, but need to keep an eye on it.
523 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
524 kFloat_GrSLType, "Radial2VSParams", 6);
525 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
526 kFloat_GrSLType, "Radial2FSParams", 6);
527
528 // For radial gradients without perspective we can pass the linear
529 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000530 if (kVec2f_GrSLType == coordsVaryingType) {
531 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000532 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000533
bsalomon@google.comf78df332012-10-29 12:43:38 +0000534 // VS
535 {
536 SkString* code = &builder->fVSCode;
537 SkString p2;
538 SkString p3;
539 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
540 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000541
bsalomon@google.comf78df332012-10-29 12:43:38 +0000542 // For radial gradients without perspective we can pass the linear
543 // part of the quadratic as a varying.
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000544 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000545 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
546 code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
547 fVSVaryingName, p2.c_str(),
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000548 vsCoordsVarying, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000549 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000550 }
551
bsalomon@google.comf78df332012-10-29 12:43:38 +0000552 // FS
553 {
554 SkString* code = &builder->fFSCode;
555 SkString cName("c");
556 SkString ac4Name("ac4");
557 SkString rootName("root");
558 SkString t;
559 SkString p0;
560 SkString p1;
561 SkString p2;
562 SkString p3;
563 SkString p4;
564 SkString p5;
565 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
566 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
567 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
568 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
569 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
570 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000571
bsalomon@google.comf78df332012-10-29 12:43:38 +0000572 // If we we're able to interpolate the linear component,
573 // bVar is the varying; otherwise compute it
574 SkString bVar;
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000575 if (kVec2f_GrSLType == coordsVaryingType) {
bsalomon@google.comf78df332012-10-29 12:43:38 +0000576 bVar = fFSVaryingName;
577 } else {
578 bVar = "b";
579 code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000580 bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
bsalomon@google.comf78df332012-10-29 12:43:38 +0000581 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000582
bsalomon@google.comf78df332012-10-29 12:43:38 +0000583 // c = (x^2)+(y^2) - params[4]
584 code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
585 cName.c_str(),
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000586 fsCoords,
587 fsCoords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000588 p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000589
bsalomon@google.comf78df332012-10-29 12:43:38 +0000590 // If we aren't degenerate, emit some extra code, and accept a slightly
591 // more complex coord.
592 if (!fIsDegenerate) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000593
bsalomon@google.comf78df332012-10-29 12:43:38 +0000594 // ac4 = 4.0 * params[0] * c
595 code->appendf("\tfloat %s = %s * 4.0 * %s;\n",
596 ac4Name.c_str(), p0.c_str(),
597 cName.c_str());
598
599 // root = sqrt(b^2-4ac)
600 // (abs to avoid exception due to fp precision)
601 code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
602 rootName.c_str(), bVar.c_str(), bVar.c_str(),
603 ac4Name.c_str());
604
605 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
606 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
607 rootName.c_str(), p1.c_str());
608 } else {
609 // t is: -c/b
610 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
611 }
612
613 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000614 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000615}
616
bsalomon@google.com28a15fb2012-10-26 17:53:18 +0000617void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
618 INHERITED::setData(uman, stage);
619 const GrRadial2Gradient& data = static_cast<const GrRadial2Gradient&>(*stage.getEffect());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000620 GrAssert(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000621 SkScalar centerX1 = data.center();
622 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000623 if (fCachedCenter != centerX1 ||
624 fCachedRadius != radius0 ||
625 fCachedPosRoot != data.isPosRoot()) {
626
bsalomon@google.com81712882012-11-01 17:12:34 +0000627 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000628
629 // When we're in the degenerate (linear) case, the second
630 // value will be INF but the program doesn't read it. (We
631 // use the same 6 uniforms even though we don't need them
632 // all in the linear case just to keep the code complexity
633 // down).
634 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000635 SkScalarToFloat(a),
636 1 / (2.f * SkScalarToFloat(a)),
637 SkScalarToFloat(centerX1),
638 SkScalarToFloat(radius0),
639 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000640 data.isPosRoot() ? 1.f : -1.f
641 };
642
643 uman.set1fv(fVSParamUni, 0, 6, values);
644 uman.set1fv(fFSParamUni, 0, 6, values);
645 fCachedCenter = centerX1;
646 fCachedRadius = radius0;
647 fCachedPosRoot = data.isPosRoot();
648 }
649}
650
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000651GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000652 enum {
653 kIsDegenerate = 1 << kMatrixKeyBitCnt,
654 };
655
656 EffectKey key = GenMatrixKey(s);
657 if (static_cast<const GrRadial2Gradient&>(*s.getEffect()).isDegenerate()) {
658 key |= kIsDegenerate;
659 }
660 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000661}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000662
663/////////////////////////////////////////////////////////////////////
664
bsalomon@google.com8ea78d82012-10-24 20:11:30 +0000665bool SkTwoPointRadialGradient::asNewEffect(GrContext* context,
bsalomon@google.com08283af2012-10-26 13:01:20 +0000666 GrEffectStage* stage) const {
667 SkASSERT(NULL != context && NULL != stage);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000668 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000669 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000670 if (!this->getLocalMatrix().invert(&matrix)) {
671 return false;
672 }
673 matrix.postConcat(fPtsToUnit);
674
675 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000676 if (0 != diffLen) {
677 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000678 SkMatrix rot;
679 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
680 SkScalarMul(invDiffLen, fDiff.fX));
681 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000682 }
683
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000684 stage->setEffect(SkNEW_ARGS(GrRadial2Gradient, (context, *this, matrix, fTileMode)))->unref();
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000685 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000686}
687
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000688#else
689
bsalomon@google.com08283af2012-10-26 13:01:20 +0000690bool SkTwoPointRadialGradient::asNewEffect(GrContext*, GrEffectStage*) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000691 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000692 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000693}
694
695#endif