blob: 22feb7189c0cd33642e4bc465949f8f8961ac216 [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
dandov9de5b512014-06-10 14:38:28 -07009#include "SkTwoPointRadialGradient.h"
rileya@google.com589708b2012-07-26 20:04:23 +000010
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,
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000173 const Descriptor& desc, const SkMatrix* localMatrix)
174 : SkGradientShaderBase(desc, localMatrix),
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
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000223size_t SkTwoPointRadialGradient::contextSize() const {
224 return sizeof(TwoPointRadialGradientContext);
225}
226
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000227SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
228 void* storage) const {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000229 // For now, we might have divided by zero, so detect that.
230 if (0 == fDiffRadius) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000231 return NULL;
232 }
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000233 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000234}
235
236SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000237 const SkTwoPointRadialGradient& shader, const ContextRec& rec)
238 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000239{
240 // we don't have a span16 proc
241 fFlags &= ~kHasSpan16_Flag;
242}
243
244void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
245 int x, int y, SkPMColor* dstCParam, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000246 SkASSERT(count > 0);
247
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000248 const SkTwoPointRadialGradient& twoPointRadialGradient =
249 static_cast<const SkTwoPointRadialGradient&>(fShader);
250
rileya@google.com589708b2012-07-26 20:04:23 +0000251 SkPMColor* SK_RESTRICT dstC = dstCParam;
252
253 // Zero difference between radii: fill with transparent black.
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000254 if (twoPointRadialGradient.fDiffRadius == 0) {
rileya@google.com589708b2012-07-26 20:04:23 +0000255 sk_bzero(dstC, count * sizeof(*dstC));
256 return;
257 }
258 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000259 TileProc proc = twoPointRadialGradient.fTileProc;
260 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000261
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000262 SkScalar foura = twoPointRadialGradient.fA * 4;
263 bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
rileya@google.com589708b2012-07-26 20:04:23 +0000264 if (fDstToIndexClass != kPerspective_MatrixClass) {
265 SkPoint srcPt;
266 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
267 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
268 SkScalar dx, fx = srcPt.fX;
269 SkScalar dy, fy = srcPt.fY;
270
271 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
272 SkFixed fixedX, fixedY;
273 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
274 dx = SkFixedToScalar(fixedX);
275 dy = SkFixedToScalar(fixedY);
276 } else {
277 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
278 dx = fDstToIndex.getScaleX();
279 dy = fDstToIndex.getSkewY();
280 }
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000281 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
282 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
283 twoPointRadialGradient.fStartRadius) * 2;
284 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
285 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
rileya@google.com589708b2012-07-26 20:04:23 +0000286
287 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000288 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000289 shadeProc = shadeSpan_twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000290 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000291 shadeProc = shadeSpan_twopoint_mirror;
292 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000293 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000294 }
295 (*shadeProc)(fx, dx, fy, dy, b, db,
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000296 twoPointRadialGradient.fSr2D2, foura,
297 twoPointRadialGradient.fOneOverTwoA, posRoot,
rileya@google.com589708b2012-07-26 20:04:23 +0000298 dstC, cache, count);
299 } else { // perspective case
300 SkScalar dstX = SkIntToScalar(x);
301 SkScalar dstY = SkIntToScalar(y);
302 for (; count > 0; --count) {
303 SkPoint srcPt;
304 dstProc(fDstToIndex, dstX, dstY, &srcPt);
305 SkScalar fx = srcPt.fX;
306 SkScalar fy = srcPt.fY;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000307 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
308 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
309 twoPointRadialGradient.fStartRadius) * 2;
310 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
311 twoPointRadialGradient.fOneOverTwoA, posRoot);
rileya@google.com589708b2012-07-26 20:04:23 +0000312 SkFixed index = proc(t);
313 SkASSERT(index <= 0xFFFF);
314 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
315 dstX += SK_Scalar1;
316 }
317 }
318}
319
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000320#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000321void SkTwoPointRadialGradient::toString(SkString* str) const {
322 str->append("SkTwoPointRadialGradient: (");
323
324 str->append("center1: (");
325 str->appendScalar(fCenter1.fX);
326 str->append(", ");
327 str->appendScalar(fCenter1.fY);
328 str->append(") radius1: ");
329 str->appendScalar(fRadius1);
330 str->append(" ");
331
332 str->append("center2: (");
333 str->appendScalar(fCenter2.fX);
334 str->append(", ");
335 str->appendScalar(fCenter2.fY);
336 str->append(") radius2: ");
337 str->appendScalar(fRadius2);
338 str->append(" ");
339
340 this->INHERITED::toString(str);
341
342 str->append(")");
343}
344#endif
345
rileya@google.com589708b2012-07-26 20:04:23 +0000346SkTwoPointRadialGradient::SkTwoPointRadialGradient(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000347 SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000348 : INHERITED(buffer),
349 fCenter1(buffer.readPoint()),
350 fCenter2(buffer.readPoint()),
351 fRadius1(buffer.readScalar()),
352 fRadius2(buffer.readScalar()) {
353 init();
354};
355
356void SkTwoPointRadialGradient::flatten(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000357 SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000358 this->INHERITED::flatten(buffer);
359 buffer.writePoint(fCenter1);
360 buffer.writePoint(fCenter2);
361 buffer.writeScalar(fRadius1);
362 buffer.writeScalar(fRadius2);
363}
364
365void SkTwoPointRadialGradient::init() {
366 fDiff = fCenter1 - fCenter2;
367 fDiffRadius = fRadius2 - fRadius1;
368 // hack to avoid zero-divide for now
369 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
370 fDiff.fX = SkScalarMul(fDiff.fX, inv);
371 fDiff.fY = SkScalarMul(fDiff.fY, inv);
372 fStartRadius = SkScalarMul(fRadius1, inv);
373 fSr2D2 = SkScalarSquare(fStartRadius);
374 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
375 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
376
377 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
378 fPtsToUnit.postScale(inv, inv);
379}
380
rileya@google.comd7cc6512012-07-27 14:00:39 +0000381/////////////////////////////////////////////////////////////////////
382
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000383#if SK_SUPPORT_GPU
384
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000385#include "GrTBackendEffectFactory.h"
dandov9de5b512014-06-10 14:38:28 -0700386#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000387
rileya@google.comd7cc6512012-07-27 14:00:39 +0000388// For brevity
389typedef GrGLUniformManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000390
bsalomon@google.com0707c292012-10-25 21:45:42 +0000391class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392
393public:
394
bsalomon@google.comc7818882013-03-20 19:19:53 +0000395 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000396 virtual ~GrGLRadial2Gradient() { }
397
bsalomon@google.comf78df332012-10-29 12:43:38 +0000398 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000399 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000400 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000401 const char* outputColor,
402 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000403 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000404 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000405 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000406
bsalomon@google.comc7818882013-03-20 19:19:53 +0000407 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000408
409protected:
410
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000411 UniformHandle fParamUni;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000412
413 const char* fVSVaryingName;
414 const char* fFSVaryingName;
415
416 bool fIsDegenerate;
417
418 // @{
419 /// Values last uploaded as uniforms
420
bsalomon@google.com81712882012-11-01 17:12:34 +0000421 SkScalar fCachedCenter;
422 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000423 bool fCachedPosRoot;
424
425 // @}
426
427private:
428
bsalomon@google.com0707c292012-10-25 21:45:42 +0000429 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000430
431};
432
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000433/////////////////////////////////////////////////////////////////////
434
435class GrRadial2Gradient : public GrGradientEffect {
436public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000437 static GrEffectRef* Create(GrContext* ctx,
438 const SkTwoPointRadialGradient& shader,
439 const SkMatrix& matrix,
440 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700441 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000442 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000443
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000444 virtual ~GrRadial2Gradient() { }
445
446 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000447 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
448 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000449 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000450
451 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000452 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
453 SkScalar center() const { return fCenterX1; }
454 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000455 bool isPosRoot() const { return SkToBool(fPosRoot); }
456
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000457 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000458
459private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000460 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000461 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000462 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000463 this->fCenterX1 == s.fCenterX1 &&
464 this->fRadius0 == s.fRadius0 &&
465 this->fPosRoot == s.fPosRoot);
466 }
467
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000468 GrRadial2Gradient(GrContext* ctx,
469 const SkTwoPointRadialGradient& shader,
470 const SkMatrix& matrix,
471 SkShader::TileMode tm)
472 : INHERITED(ctx, shader, matrix, tm)
473 , fCenterX1(shader.getCenterX1())
474 , fRadius0(shader.getStartRadius())
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000475 , fPosRoot(shader.getDiffRadius() < 0) {
476 // We pass the linear part of the quadratic as a varying.
477 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
478 fBTransform = this->getCoordTransform();
479 SkMatrix& bMatrix = *fBTransform.accessMatrix();
480 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
481 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
482 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
483 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
484 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
485 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
486 this->addCoordTransform(&fBTransform);
487 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000488
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000489 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000490
491 // @{
492 // Cache of values - these can change arbitrarily, EXCEPT
493 // we shouldn't change between degenerate and non-degenerate?!
494
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000495 GrCoordTransform fBTransform;
496 SkScalar fCenterX1;
497 SkScalar fRadius0;
498 SkBool8 fPosRoot;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000499
500 // @}
501
502 typedef GrGradientEffect INHERITED;
503};
504
505/////////////////////////////////////////////////////////////////////
506
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000507GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000508
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000509GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000510 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000511 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000512 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000513 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
514 SkScalar radius1 = random->nextUScalar1();
515 SkPoint center2;
516 SkScalar radius2;
517 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000518 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000519 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000520 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000521 } while (radius1 == radius2);
522
523 SkColor colors[kMaxRandomGradientColors];
524 SkScalar stopsArray[kMaxRandomGradientColors];
525 SkScalar* stops = stopsArray;
526 SkShader::TileMode tm;
527 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
528 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
529 center2, radius2,
530 colors, stops, colorCount,
531 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000532 SkPaint paint;
dandov9de5b512014-06-10 14:38:28 -0700533 GrEffectRef* effect;
534 GrColor grColor;
535 shader->asNewEffect(context, paint, NULL, &grColor, &effect);
536 return effect;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000537}
538
539/////////////////////////////////////////////////////////////////////
540
bsalomon@google.com6340a412013-01-22 19:55:59 +0000541GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000542 const GrDrawEffect& drawEffect)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000543 : INHERITED(factory)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000544 , fVSVaryingName(NULL)
545 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000546 , fCachedCenter(SK_ScalarMax)
547 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000548 , fCachedPosRoot(0) {
549
bsalomon@google.comc7818882013-03-20 19:19:53 +0000550 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000551 fIsDegenerate = data.isDegenerate();
552}
553
bsalomon@google.comf78df332012-10-29 12:43:38 +0000554void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000555 const GrDrawEffect& drawEffect,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000556 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000557 const char* outputColor,
558 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000559 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000560 const TextureSamplerArray& samplers) {
561
bsalomon@google.com82d12232013-09-09 15:36:26 +0000562 this->emitUniforms(builder, key);
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000563 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
564 kFloat_GrSLType, "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000565
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000566 SkString cName("c");
567 SkString ac4Name("ac4");
568 SkString rootName("root");
569 SkString t;
570 SkString p0;
571 SkString p1;
572 SkString p2;
573 SkString p3;
574 SkString p4;
575 SkString p5;
576 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
577 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
578 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
579 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
580 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
581 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
582
583 // We interpolate the linear component in coords[1].
584 SkASSERT(coords[0].type() == coords[1].type());
585 const char* coords2D;
586 SkString bVar;
587 if (kVec3f_GrSLType == coords[0].type()) {
588 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
589 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
590 coords2D = "interpolants.xy";
591 bVar = "interpolants.z";
592 } else {
593 coords2D = coords[0].c_str();
594 bVar.printf("%s.x", coords[1].c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000595 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000596
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000597 // c = (x^2)+(y^2) - params[4]
598 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
599 cName.c_str(), coords2D, coords2D, p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000600
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000601 // If we aren't degenerate, emit some extra code, and accept a slightly
602 // more complex coord.
603 if (!fIsDegenerate) {
604
605 // ac4 = 4.0 * params[0] * c
606 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
607 ac4Name.c_str(), p0.c_str(),
608 cName.c_str());
609
610 // root = sqrt(b^2-4ac)
611 // (abs to avoid exception due to fp precision)
612 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
613 rootName.c_str(), bVar.c_str(), bVar.c_str(),
614 ac4Name.c_str());
615
616 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
617 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
618 rootName.c_str(), p1.c_str());
619 } else {
620 // t is: -c/b
621 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000622 }
623
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000624 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000625}
626
bsalomon@google.comc7818882013-03-20 19:19:53 +0000627void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
628 const GrDrawEffect& drawEffect) {
629 INHERITED::setData(uman, drawEffect);
630 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000631 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000632 SkScalar centerX1 = data.center();
633 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000634 if (fCachedCenter != centerX1 ||
635 fCachedRadius != radius0 ||
636 fCachedPosRoot != data.isPosRoot()) {
637
bsalomon@google.com81712882012-11-01 17:12:34 +0000638 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000639
640 // When we're in the degenerate (linear) case, the second
641 // value will be INF but the program doesn't read it. (We
642 // use the same 6 uniforms even though we don't need them
643 // all in the linear case just to keep the code complexity
644 // down).
645 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000646 SkScalarToFloat(a),
647 1 / (2.f * SkScalarToFloat(a)),
648 SkScalarToFloat(centerX1),
649 SkScalarToFloat(radius0),
650 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000651 data.isPosRoot() ? 1.f : -1.f
652 };
653
commit-bot@chromium.orgd3baf202013-11-07 22:06:08 +0000654 uman.set1fv(fParamUni, 6, values);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000655 fCachedCenter = centerX1;
656 fCachedRadius = radius0;
657 fCachedPosRoot = data.isPosRoot();
658 }
659}
660
bsalomon@google.comc7818882013-03-20 19:19:53 +0000661GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
662 const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000663 enum {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000664 kIsDegenerate = 1 << kBaseKeyBitCnt,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000665 };
666
bsalomon@google.com82d12232013-09-09 15:36:26 +0000667 EffectKey key = GenBaseGradientKey(drawEffect);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000668 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000669 key |= kIsDegenerate;
670 }
671 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000672}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000673
674/////////////////////////////////////////////////////////////////////
675
dandov9de5b512014-06-10 14:38:28 -0700676bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
677 const SkMatrix* localMatrix, GrColor* grColor,
678 GrEffectRef** grEffect) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000679 SkASSERT(NULL != context);
dandov9de5b512014-06-10 14:38:28 -0700680
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000681 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000682 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000683 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700684 return false;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000685 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000686 if (localMatrix) {
687 SkMatrix inv;
688 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700689 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000690 }
691 matrix.postConcat(inv);
692 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000693 matrix.postConcat(fPtsToUnit);
694
695 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000696 if (0 != diffLen) {
697 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000698 SkMatrix rot;
699 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
700 SkScalarMul(invDiffLen, fDiff.fX));
701 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000702 }
703
dandov9de5b512014-06-10 14:38:28 -0700704 *grColor = SkColor2GrColorJustAlpha(paint.getColor());
705 *grEffect = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
706
707 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000708}
709
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000710#else
711
dandov9de5b512014-06-10 14:38:28 -0700712bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
713 const SkMatrix* localMatrix, GrColor* grColor,
bsalomon97b9ab72014-07-08 06:52:35 -0700714 GrEffect** grEffect) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000715 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700716 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000717}
718
719#endif