blob: fe56c73fada82f9114f5adcb4d8854fd9c902456 [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
reedaddf2ed2014-08-11 08:28:24 -0700170SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
171 const SkPoint& end, SkScalar endRadius,
172 const Descriptor& desc)
173 : SkGradientShaderBase(desc)
174 , fCenter1(start)
175 , fCenter2(end)
176 , fRadius1(startRadius)
177 , fRadius2(endRadius)
178{
rileya@google.com589708b2012-07-26 20:04:23 +0000179 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
reed9fa60da2014-08-21 07:59:51 -0700346#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
347SkTwoPointRadialGradient::SkTwoPointRadialGradient(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};
reed9fa60da2014-08-21 07:59:51 -0700355#endif
356
357SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
358 DescriptorScope desc;
359 if (!desc.unflatten(buffer)) {
360 return NULL;
361 }
362 const SkPoint c1 = buffer.readPoint();
363 const SkPoint c2 = buffer.readPoint();
364 const SkScalar r1 = buffer.readScalar();
365 const SkScalar r2 = buffer.readScalar();
366 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
367 desc.fCount, desc.fTileMode, desc.fGradFlags,
368 desc.fLocalMatrix);
369}
rileya@google.com589708b2012-07-26 20:04:23 +0000370
371void SkTwoPointRadialGradient::flatten(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000372 SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000373 this->INHERITED::flatten(buffer);
374 buffer.writePoint(fCenter1);
375 buffer.writePoint(fCenter2);
376 buffer.writeScalar(fRadius1);
377 buffer.writeScalar(fRadius2);
378}
379
380void SkTwoPointRadialGradient::init() {
381 fDiff = fCenter1 - fCenter2;
382 fDiffRadius = fRadius2 - fRadius1;
383 // hack to avoid zero-divide for now
384 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
385 fDiff.fX = SkScalarMul(fDiff.fX, inv);
386 fDiff.fY = SkScalarMul(fDiff.fY, inv);
387 fStartRadius = SkScalarMul(fRadius1, inv);
388 fSr2D2 = SkScalarSquare(fStartRadius);
389 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
390 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
391
392 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
393 fPtsToUnit.postScale(inv, inv);
394}
395
rileya@google.comd7cc6512012-07-27 14:00:39 +0000396/////////////////////////////////////////////////////////////////////
397
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000398#if SK_SUPPORT_GPU
399
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000400#include "GrTBackendEffectFactory.h"
joshualitt30ba4362014-08-21 20:18:45 -0700401#include "gl/builders/GrGLProgramBuilder.h"
dandov9de5b512014-06-10 14:38:28 -0700402#include "SkGr.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000403
rileya@google.comd7cc6512012-07-27 14:00:39 +0000404// For brevity
kkinnunen7510b222014-07-30 00:04:16 -0700405typedef GrGLProgramDataManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000406
bsalomon@google.com0707c292012-10-25 21:45:42 +0000407class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000408
409public:
410
joshualitt49586be2014-09-16 08:21:41 -0700411 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000412 virtual ~GrGLRadial2Gradient() { }
413
joshualitt30ba4362014-08-21 20:18:45 -0700414 virtual void emitCode(GrGLProgramBuilder*,
joshualitt49586be2014-09-16 08:21:41 -0700415 const GrEffect&,
bsalomon63e99f72014-07-21 08:03:14 -0700416 const GrEffectKey&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000417 const char* outputColor,
418 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000419 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000420 const TextureSamplerArray&) SK_OVERRIDE;
joshualitt49586be2014-09-16 08:21:41 -0700421 virtual void setData(const GrGLProgramDataManager&, const GrEffect&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000422
joshualitt49586be2014-09-16 08:21:41 -0700423 static void GenKey(const GrEffect&, const GrGLCaps& caps, GrEffectKeyBuilder* b);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000424
425protected:
426
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000427 UniformHandle fParamUni;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000428
429 const char* fVSVaryingName;
430 const char* fFSVaryingName;
431
432 bool fIsDegenerate;
433
434 // @{
435 /// Values last uploaded as uniforms
436
bsalomon@google.com81712882012-11-01 17:12:34 +0000437 SkScalar fCachedCenter;
438 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000439 bool fCachedPosRoot;
440
441 // @}
442
443private:
444
bsalomon@google.com0707c292012-10-25 21:45:42 +0000445 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000446
447};
448
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000449/////////////////////////////////////////////////////////////////////
450
451class GrRadial2Gradient : public GrGradientEffect {
452public:
bsalomon83d081a2014-07-08 09:56:10 -0700453 static GrEffect* Create(GrContext* ctx,
454 const SkTwoPointRadialGradient& shader,
455 const SkMatrix& matrix,
456 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700457 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000458 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000459
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000460 virtual ~GrRadial2Gradient() { }
461
462 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000463 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
464 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000465 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000466
467 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000468 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
469 SkScalar center() const { return fCenterX1; }
470 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000471 bool isPosRoot() const { return SkToBool(fPosRoot); }
472
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000473 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000474
475private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000476 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700477 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000478 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000479 this->fCenterX1 == s.fCenterX1 &&
480 this->fRadius0 == s.fRadius0 &&
481 this->fPosRoot == s.fPosRoot);
482 }
483
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000484 GrRadial2Gradient(GrContext* ctx,
485 const SkTwoPointRadialGradient& shader,
486 const SkMatrix& matrix,
487 SkShader::TileMode tm)
488 : INHERITED(ctx, shader, matrix, tm)
489 , fCenterX1(shader.getCenterX1())
490 , fRadius0(shader.getStartRadius())
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000491 , fPosRoot(shader.getDiffRadius() < 0) {
492 // We pass the linear part of the quadratic as a varying.
493 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
494 fBTransform = this->getCoordTransform();
495 SkMatrix& bMatrix = *fBTransform.accessMatrix();
496 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
497 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
498 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
499 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
500 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
501 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
502 this->addCoordTransform(&fBTransform);
503 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000504
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000505 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000506
507 // @{
508 // Cache of values - these can change arbitrarily, EXCEPT
509 // we shouldn't change between degenerate and non-degenerate?!
510
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000511 GrCoordTransform fBTransform;
512 SkScalar fCenterX1;
513 SkScalar fRadius0;
514 SkBool8 fPosRoot;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000515
516 // @}
517
518 typedef GrGradientEffect INHERITED;
519};
520
521/////////////////////////////////////////////////////////////////////
522
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000523GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000524
bsalomon83d081a2014-07-08 09:56:10 -0700525GrEffect* GrRadial2Gradient::TestCreate(SkRandom* random,
526 GrContext* context,
527 const GrDrawTargetCaps&,
528 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000529 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
530 SkScalar radius1 = random->nextUScalar1();
531 SkPoint center2;
532 SkScalar radius2;
533 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000534 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000535 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000536 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000537 } while (radius1 == radius2);
538
539 SkColor colors[kMaxRandomGradientColors];
540 SkScalar stopsArray[kMaxRandomGradientColors];
541 SkScalar* stops = stopsArray;
542 SkShader::TileMode tm;
543 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
544 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
545 center2, radius2,
546 colors, stops, colorCount,
547 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000548 SkPaint paint;
bsalomon83d081a2014-07-08 09:56:10 -0700549 GrEffect* effect;
550 GrColor paintColor;
551 SkAssertResult(shader->asNewEffect(context, paint, NULL, &paintColor, &effect));
dandov9de5b512014-06-10 14:38:28 -0700552 return effect;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000553}
554
555/////////////////////////////////////////////////////////////////////
556
bsalomon@google.com6340a412013-01-22 19:55:59 +0000557GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
joshualitt49586be2014-09-16 08:21:41 -0700558 const GrEffect& effect)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000559 : INHERITED(factory)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000560 , fVSVaryingName(NULL)
561 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000562 , fCachedCenter(SK_ScalarMax)
563 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000564 , fCachedPosRoot(0) {
565
joshualitt49586be2014-09-16 08:21:41 -0700566 const GrRadial2Gradient& data = effect.cast<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000567 fIsDegenerate = data.isDegenerate();
568}
569
joshualitt30ba4362014-08-21 20:18:45 -0700570void GrGLRadial2Gradient::emitCode(GrGLProgramBuilder* builder,
joshualitt49586be2014-09-16 08:21:41 -0700571 const GrEffect& effect,
bsalomon63e99f72014-07-21 08:03:14 -0700572 const GrEffectKey& key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000573 const char* outputColor,
574 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000575 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000576 const TextureSamplerArray& samplers) {
bsalomon63e99f72014-07-21 08:03:14 -0700577 uint32_t baseKey = key.get32(0);
578 this->emitUniforms(builder, baseKey);
joshualitt30ba4362014-08-21 20:18:45 -0700579 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000580 kFloat_GrSLType, "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000581
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000582 SkString cName("c");
583 SkString ac4Name("ac4");
584 SkString rootName("root");
585 SkString t;
586 SkString p0;
587 SkString p1;
588 SkString p2;
589 SkString p3;
590 SkString p4;
591 SkString p5;
592 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
593 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
594 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
595 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
596 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
597 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
598
joshualitt30ba4362014-08-21 20:18:45 -0700599 GrGLFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000600 // We interpolate the linear component in coords[1].
joshualitt23e280d2014-09-18 12:26:38 -0700601 SkASSERT(coords[0].getType() == coords[1].getType());
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000602 const char* coords2D;
603 SkString bVar;
joshualitt23e280d2014-09-18 12:26:38 -0700604 if (kVec3f_GrSLType == coords[0].getType()) {
joshualitt30ba4362014-08-21 20:18:45 -0700605 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000606 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
607 coords2D = "interpolants.xy";
608 bVar = "interpolants.z";
609 } else {
610 coords2D = coords[0].c_str();
611 bVar.printf("%s.x", coords[1].c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000612 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000613
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000614 // c = (x^2)+(y^2) - params[4]
joshualitt30ba4362014-08-21 20:18:45 -0700615 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000616 cName.c_str(), coords2D, coords2D, p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000617
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000618 // If we aren't degenerate, emit some extra code, and accept a slightly
619 // more complex coord.
620 if (!fIsDegenerate) {
621
622 // ac4 = 4.0 * params[0] * c
joshualitt30ba4362014-08-21 20:18:45 -0700623 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000624 ac4Name.c_str(), p0.c_str(),
625 cName.c_str());
626
627 // root = sqrt(b^2-4ac)
628 // (abs to avoid exception due to fp precision)
joshualitt30ba4362014-08-21 20:18:45 -0700629 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000630 rootName.c_str(), bVar.c_str(), bVar.c_str(),
631 ac4Name.c_str());
632
633 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
634 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
635 rootName.c_str(), p1.c_str());
636 } else {
637 // t is: -c/b
638 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000639 }
640
bsalomon63e99f72014-07-21 08:03:14 -0700641 this->emitColor(builder, t.c_str(), baseKey, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000642}
643
kkinnunen7510b222014-07-30 00:04:16 -0700644void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
joshualitt49586be2014-09-16 08:21:41 -0700645 const GrEffect& effect) {
646 INHERITED::setData(pdman, effect);
647 const GrRadial2Gradient& data = effect.cast<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000648 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000649 SkScalar centerX1 = data.center();
650 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000651 if (fCachedCenter != centerX1 ||
652 fCachedRadius != radius0 ||
653 fCachedPosRoot != data.isPosRoot()) {
654
bsalomon@google.com81712882012-11-01 17:12:34 +0000655 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000656
657 // When we're in the degenerate (linear) case, the second
658 // value will be INF but the program doesn't read it. (We
659 // use the same 6 uniforms even though we don't need them
660 // all in the linear case just to keep the code complexity
661 // down).
662 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000663 SkScalarToFloat(a),
664 1 / (2.f * SkScalarToFloat(a)),
665 SkScalarToFloat(centerX1),
666 SkScalarToFloat(radius0),
667 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000668 data.isPosRoot() ? 1.f : -1.f
669 };
670
kkinnunen7510b222014-07-30 00:04:16 -0700671 pdman.set1fv(fParamUni, 6, values);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000672 fCachedCenter = centerX1;
673 fCachedRadius = radius0;
674 fCachedPosRoot = data.isPosRoot();
675 }
676}
677
joshualitt49586be2014-09-16 08:21:41 -0700678void GrGLRadial2Gradient::GenKey(const GrEffect& effect,
bsalomon63e99f72014-07-21 08:03:14 -0700679 const GrGLCaps&, GrEffectKeyBuilder* b) {
680 uint32_t* key = b->add32n(2);
joshualitt49586be2014-09-16 08:21:41 -0700681 key[0] = GenBaseGradientKey(effect);
682 key[1] = effect.cast<GrRadial2Gradient>().isDegenerate();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000683}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000684
685/////////////////////////////////////////////////////////////////////
686
dandov9de5b512014-06-10 14:38:28 -0700687bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700688 const SkMatrix* localMatrix, GrColor* paintColor,
689 GrEffect** effect) const {
bsalomon49f085d2014-09-05 13:34:00 -0700690 SkASSERT(context);
dandov9de5b512014-06-10 14:38:28 -0700691
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)) {
dandov9de5b512014-06-10 14:38:28 -0700695 return false;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000696 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000697 if (localMatrix) {
698 SkMatrix inv;
699 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700700 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000701 }
702 matrix.postConcat(inv);
703 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000704 matrix.postConcat(fPtsToUnit);
705
706 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000707 if (0 != diffLen) {
708 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000709 SkMatrix rot;
710 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
711 SkScalarMul(invDiffLen, fDiff.fX));
712 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000713 }
714
bsalomon83d081a2014-07-08 09:56:10 -0700715 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
716 *effect = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
dandov9de5b512014-06-10 14:38:28 -0700717
718 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000719}
720
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000721#else
722
dandov9de5b512014-06-10 14:38:28 -0700723bool SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint& paint,
bsalomon83d081a2014-07-08 09:56:10 -0700724 const SkMatrix* localMatrix, GrColor* paintColor,
725 GrEffect** effect) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000726 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700727 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000728}
729
730#endif