blob: 2783112cb8065f049a730ecb63d0ab06e2587cf7 [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 -0700346SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
347 DescriptorScope desc;
348 if (!desc.unflatten(buffer)) {
349 return NULL;
350 }
351 const SkPoint c1 = buffer.readPoint();
352 const SkPoint c2 = buffer.readPoint();
353 const SkScalar r1 = buffer.readScalar();
354 const SkScalar r2 = buffer.readScalar();
355 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
356 desc.fCount, desc.fTileMode, desc.fGradFlags,
357 desc.fLocalMatrix);
358}
rileya@google.com589708b2012-07-26 20:04:23 +0000359
360void SkTwoPointRadialGradient::flatten(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000361 SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000362 this->INHERITED::flatten(buffer);
363 buffer.writePoint(fCenter1);
364 buffer.writePoint(fCenter2);
365 buffer.writeScalar(fRadius1);
366 buffer.writeScalar(fRadius2);
367}
368
369void SkTwoPointRadialGradient::init() {
370 fDiff = fCenter1 - fCenter2;
371 fDiffRadius = fRadius2 - fRadius1;
372 // hack to avoid zero-divide for now
373 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
374 fDiff.fX = SkScalarMul(fDiff.fX, inv);
375 fDiff.fY = SkScalarMul(fDiff.fY, inv);
376 fStartRadius = SkScalarMul(fRadius1, inv);
377 fSr2D2 = SkScalarSquare(fStartRadius);
378 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
379 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
380
381 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
382 fPtsToUnit.postScale(inv, inv);
383}
384
rileya@google.comd7cc6512012-07-27 14:00:39 +0000385/////////////////////////////////////////////////////////////////////
386
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000387#if SK_SUPPORT_GPU
388
dandov9de5b512014-06-10 14:38:28 -0700389#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800390#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000391
rileya@google.comd7cc6512012-07-27 14:00:39 +0000392// For brevity
kkinnunen7510b222014-07-30 00:04:16 -0700393typedef GrGLProgramDataManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000394
bsalomon@google.com0707c292012-10-25 21:45:42 +0000395class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000396
397public:
398
joshualitteb2a6762014-12-04 11:35:33 -0800399 GrGLRadial2Gradient(const GrProcessor&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000400 virtual ~GrGLRadial2Gradient() { }
401
joshualitt15988992014-10-09 15:04:05 -0700402 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700403 const GrFragmentProcessor&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000404 const char* outputColor,
405 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000406 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000407 const TextureSamplerArray&) SK_OVERRIDE;
joshualittb0a8a372014-09-23 09:50:21 -0700408 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000409
joshualittb0a8a372014-09-23 09:50:21 -0700410 static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000411
412protected:
413
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000414 UniformHandle fParamUni;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000415
416 const char* fVSVaryingName;
417 const char* fFSVaryingName;
418
419 bool fIsDegenerate;
420
421 // @{
422 /// Values last uploaded as uniforms
423
bsalomon@google.com81712882012-11-01 17:12:34 +0000424 SkScalar fCachedCenter;
425 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000426 bool fCachedPosRoot;
427
428 // @}
429
430private:
431
bsalomon@google.com0707c292012-10-25 21:45:42 +0000432 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000433
434};
435
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000436/////////////////////////////////////////////////////////////////////
437
438class GrRadial2Gradient : public GrGradientEffect {
439public:
joshualittb0a8a372014-09-23 09:50:21 -0700440 static GrFragmentProcessor* Create(GrContext* ctx,
441 const SkTwoPointRadialGradient& shader,
442 const SkMatrix& matrix,
443 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700444 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000445 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000446
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000447 virtual ~GrRadial2Gradient() { }
448
joshualitteb2a6762014-12-04 11:35:33 -0800449 virtual const char* name() const SK_OVERRIDE { return "Two-Point Radial Gradient"; }
450
451 virtual void getGLProcessorKey(const GrGLCaps& caps,
452 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
453 GrGLRadial2Gradient::GenKey(*this, caps, b);
454 }
455
456 virtual GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
457 return SkNEW_ARGS(GrGLRadial2Gradient, (*this));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000458 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000459
460 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000461 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
462 SkScalar center() const { return fCenterX1; }
463 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000464 bool isPosRoot() const { return SkToBool(fPosRoot); }
465
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000466private:
bsalomon0e08fc12014-10-15 08:19:04 -0700467 virtual bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700468 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000469 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000470 this->fCenterX1 == s.fCenterX1 &&
471 this->fRadius0 == s.fRadius0 &&
472 this->fPosRoot == s.fPosRoot);
473 }
474
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000475 GrRadial2Gradient(GrContext* ctx,
476 const SkTwoPointRadialGradient& shader,
477 const SkMatrix& matrix,
478 SkShader::TileMode tm)
479 : INHERITED(ctx, shader, matrix, tm)
480 , fCenterX1(shader.getCenterX1())
481 , fRadius0(shader.getStartRadius())
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000482 , fPosRoot(shader.getDiffRadius() < 0) {
joshualitteb2a6762014-12-04 11:35:33 -0800483 this->initClassID<GrRadial2Gradient>();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000484 // We pass the linear part of the quadratic as a varying.
485 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
486 fBTransform = this->getCoordTransform();
487 SkMatrix& bMatrix = *fBTransform.accessMatrix();
488 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
489 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
490 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
491 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
492 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
493 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
494 this->addCoordTransform(&fBTransform);
495 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000496
joshualittb0a8a372014-09-23 09:50:21 -0700497 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000498
499 // @{
500 // Cache of values - these can change arbitrarily, EXCEPT
501 // we shouldn't change between degenerate and non-degenerate?!
502
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000503 GrCoordTransform fBTransform;
504 SkScalar fCenterX1;
505 SkScalar fRadius0;
506 SkBool8 fPosRoot;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000507
508 // @}
509
510 typedef GrGradientEffect INHERITED;
511};
512
513/////////////////////////////////////////////////////////////////////
514
joshualittb0a8a372014-09-23 09:50:21 -0700515GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000516
joshualittb0a8a372014-09-23 09:50:21 -0700517GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
518 GrContext* context,
519 const GrDrawTargetCaps&,
520 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000521 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
522 SkScalar radius1 = random->nextUScalar1();
523 SkPoint center2;
524 SkScalar radius2;
525 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000526 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000527 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000528 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000529 } while (radius1 == radius2);
530
531 SkColor colors[kMaxRandomGradientColors];
532 SkScalar stopsArray[kMaxRandomGradientColors];
533 SkScalar* stops = stopsArray;
534 SkShader::TileMode tm;
535 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
536 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
537 center2, radius2,
538 colors, stops, colorCount,
539 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000540 SkPaint paint;
joshualittb0a8a372014-09-23 09:50:21 -0700541 GrFragmentProcessor* fp;
bsalomon83d081a2014-07-08 09:56:10 -0700542 GrColor paintColor;
joshualittb0a8a372014-09-23 09:50:21 -0700543 SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
544 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000545}
546
547/////////////////////////////////////////////////////////////////////
548
joshualitteb2a6762014-12-04 11:35:33 -0800549GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
550 : fVSVaryingName(NULL)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000551 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000552 , fCachedCenter(SK_ScalarMax)
553 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000554 , fCachedPosRoot(0) {
555
joshualittb0a8a372014-09-23 09:50:21 -0700556 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000557 fIsDegenerate = data.isDegenerate();
558}
559
joshualitt15988992014-10-09 15:04:05 -0700560void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800561 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000562 const char* outputColor,
563 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000564 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000565 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800566 const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800567 this->emitUniforms(builder, ge);
joshualitt30ba4362014-08-21 20:18:45 -0700568 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000569 kFloat_GrSLType, "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000570
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000571 SkString cName("c");
572 SkString ac4Name("ac4");
573 SkString rootName("root");
574 SkString t;
575 SkString p0;
576 SkString p1;
577 SkString p2;
578 SkString p3;
579 SkString p4;
580 SkString p5;
581 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
582 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
583 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
584 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
585 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
586 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
587
joshualitt15988992014-10-09 15:04:05 -0700588 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000589 // We interpolate the linear component in coords[1].
joshualitt23e280d2014-09-18 12:26:38 -0700590 SkASSERT(coords[0].getType() == coords[1].getType());
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000591 const char* coords2D;
592 SkString bVar;
joshualitt23e280d2014-09-18 12:26:38 -0700593 if (kVec3f_GrSLType == coords[0].getType()) {
joshualitt30ba4362014-08-21 20:18:45 -0700594 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000595 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
596 coords2D = "interpolants.xy";
597 bVar = "interpolants.z";
598 } else {
599 coords2D = coords[0].c_str();
600 bVar.printf("%s.x", coords[1].c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000601 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000602
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000603 // c = (x^2)+(y^2) - params[4]
joshualitt30ba4362014-08-21 20:18:45 -0700604 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000605 cName.c_str(), coords2D, coords2D, p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000606
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000607 // If we aren't degenerate, emit some extra code, and accept a slightly
608 // more complex coord.
609 if (!fIsDegenerate) {
610
611 // ac4 = 4.0 * params[0] * c
joshualitt30ba4362014-08-21 20:18:45 -0700612 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000613 ac4Name.c_str(), p0.c_str(),
614 cName.c_str());
615
616 // root = sqrt(b^2-4ac)
617 // (abs to avoid exception due to fp precision)
joshualitt30ba4362014-08-21 20:18:45 -0700618 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000619 rootName.c_str(), bVar.c_str(), bVar.c_str(),
620 ac4Name.c_str());
621
622 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
623 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
624 rootName.c_str(), p1.c_str());
625 } else {
626 // t is: -c/b
627 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000628 }
629
joshualitt60030bc2014-11-25 14:21:55 -0800630 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000631}
632
kkinnunen7510b222014-07-30 00:04:16 -0700633void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700634 const GrProcessor& processor) {
635 INHERITED::setData(pdman, processor);
636 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000637 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000638 SkScalar centerX1 = data.center();
639 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000640 if (fCachedCenter != centerX1 ||
641 fCachedRadius != radius0 ||
642 fCachedPosRoot != data.isPosRoot()) {
643
bsalomon@google.com81712882012-11-01 17:12:34 +0000644 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000645
646 // When we're in the degenerate (linear) case, the second
647 // value will be INF but the program doesn't read it. (We
648 // use the same 6 uniforms even though we don't need them
649 // all in the linear case just to keep the code complexity
650 // down).
651 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000652 SkScalarToFloat(a),
653 1 / (2.f * SkScalarToFloat(a)),
654 SkScalarToFloat(centerX1),
655 SkScalarToFloat(radius0),
656 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000657 data.isPosRoot() ? 1.f : -1.f
658 };
659
kkinnunen7510b222014-07-30 00:04:16 -0700660 pdman.set1fv(fParamUni, 6, values);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000661 fCachedCenter = centerX1;
662 fCachedRadius = radius0;
663 fCachedPosRoot = data.isPosRoot();
664 }
665}
666
joshualittb0a8a372014-09-23 09:50:21 -0700667void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
668 const GrGLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700669 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700670 key[0] = GenBaseGradientKey(processor);
671 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000672}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000673
674/////////////////////////////////////////////////////////////////////
675
joshualittb0a8a372014-09-23 09:50:21 -0700676bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
677 const SkMatrix* localMatrix, GrColor* paintColor,
678 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700679 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800680
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
bsalomon83d081a2014-07-08 09:56:10 -0700704 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700705 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800706
dandov9de5b512014-06-10 14:38:28 -0700707 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000708}
709
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000710#else
711
joshualittb0a8a372014-09-23 09:50:21 -0700712bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*,
713 GrColor*, GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000714 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700715 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000716}
717
718#endif