blob: e0ea35e8609049140c6a365813fd3b70e74a519c [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
mtkleincc695fe2014-12-10 10:29:19 -0800170static SkMatrix pts_to_unit(const SkPoint& start, SkScalar diffRadius) {
171 SkScalar inv = diffRadius ? SkScalarInvert(diffRadius) : 0;
172 SkMatrix matrix;
173 matrix.setTranslate(-start.fX, -start.fY);
174 matrix.postScale(inv, inv);
175 return matrix;
176}
177
reedaddf2ed2014-08-11 08:28:24 -0700178SkTwoPointRadialGradient::SkTwoPointRadialGradient(const SkPoint& start, SkScalar startRadius,
179 const SkPoint& end, SkScalar endRadius,
180 const Descriptor& desc)
mtkleincc695fe2014-12-10 10:29:19 -0800181 : SkGradientShaderBase(desc, pts_to_unit(start, endRadius - startRadius))
reedaddf2ed2014-08-11 08:28:24 -0700182 , fCenter1(start)
183 , fCenter2(end)
184 , fRadius1(startRadius)
185 , fRadius2(endRadius)
186{
mtkleincc695fe2014-12-10 10:29:19 -0800187 fDiff = fCenter1 - fCenter2;
188 fDiffRadius = fRadius2 - fRadius1;
189 // hack to avoid zero-divide for now
190 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
191 fDiff.fX = SkScalarMul(fDiff.fX, inv);
192 fDiff.fY = SkScalarMul(fDiff.fY, inv);
193 fStartRadius = SkScalarMul(fRadius1, inv);
194 fSr2D2 = SkScalarSquare(fStartRadius);
195 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
196 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
rileya@google.com589708b2012-07-26 20:04:23 +0000197}
198
199SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
200 SkBitmap* bitmap,
201 SkMatrix* matrix,
202 SkShader::TileMode* xy) const {
203 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000204 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000205 }
206 SkScalar diffL = 0; // just to avoid gcc warning
207 if (matrix) {
208 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
209 SkScalarSquare(fDiff.fY));
210 }
211 if (matrix) {
212 if (diffL) {
213 SkScalar invDiffL = SkScalarInvert(diffL);
214 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
215 SkScalarMul(invDiffL, fDiff.fX));
216 } else {
217 matrix->reset();
218 }
219 matrix->preConcat(fPtsToUnit);
220 }
221 if (xy) {
222 xy[0] = fTileMode;
223 xy[1] = kClamp_TileMode;
224 }
225 return kTwoPointRadial_BitmapType;
226}
227
228SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
229 SkShader::GradientInfo* info) const {
230 if (info) {
231 commonAsAGradient(info);
232 info->fPoint[0] = fCenter1;
233 info->fPoint[1] = fCenter2;
234 info->fRadius[0] = fRadius1;
235 info->fRadius[1] = fRadius2;
236 }
237 return kRadial2_GradientType;
238}
239
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000240size_t SkTwoPointRadialGradient::contextSize() const {
241 return sizeof(TwoPointRadialGradientContext);
242}
243
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000244SkShader::Context* SkTwoPointRadialGradient::onCreateContext(const ContextRec& rec,
245 void* storage) const {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000246 // For now, we might have divided by zero, so detect that.
247 if (0 == fDiffRadius) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000248 return NULL;
249 }
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000250 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext, (*this, rec));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000251}
252
253SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000254 const SkTwoPointRadialGradient& shader, const ContextRec& rec)
255 : INHERITED(shader, rec)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000256{
257 // we don't have a span16 proc
258 fFlags &= ~kHasSpan16_Flag;
259}
260
261void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
262 int x, int y, SkPMColor* dstCParam, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000263 SkASSERT(count > 0);
264
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000265 const SkTwoPointRadialGradient& twoPointRadialGradient =
266 static_cast<const SkTwoPointRadialGradient&>(fShader);
267
rileya@google.com589708b2012-07-26 20:04:23 +0000268 SkPMColor* SK_RESTRICT dstC = dstCParam;
269
270 // Zero difference between radii: fill with transparent black.
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000271 if (twoPointRadialGradient.fDiffRadius == 0) {
rileya@google.com589708b2012-07-26 20:04:23 +0000272 sk_bzero(dstC, count * sizeof(*dstC));
273 return;
274 }
275 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000276 TileProc proc = twoPointRadialGradient.fTileProc;
277 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000278
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000279 SkScalar foura = twoPointRadialGradient.fA * 4;
280 bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
rileya@google.com589708b2012-07-26 20:04:23 +0000281 if (fDstToIndexClass != kPerspective_MatrixClass) {
282 SkPoint srcPt;
283 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
284 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
285 SkScalar dx, fx = srcPt.fX;
286 SkScalar dy, fy = srcPt.fY;
287
288 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
289 SkFixed fixedX, fixedY;
290 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
291 dx = SkFixedToScalar(fixedX);
292 dy = SkFixedToScalar(fixedY);
293 } else {
294 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
295 dx = fDstToIndex.getScaleX();
296 dy = fDstToIndex.getSkewY();
297 }
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000298 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
299 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
300 twoPointRadialGradient.fStartRadius) * 2;
301 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
302 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
rileya@google.com589708b2012-07-26 20:04:23 +0000303
304 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000305 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000306 shadeProc = shadeSpan_twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000307 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000308 shadeProc = shadeSpan_twopoint_mirror;
309 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000310 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000311 }
312 (*shadeProc)(fx, dx, fy, dy, b, db,
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000313 twoPointRadialGradient.fSr2D2, foura,
314 twoPointRadialGradient.fOneOverTwoA, posRoot,
rileya@google.com589708b2012-07-26 20:04:23 +0000315 dstC, cache, count);
316 } else { // perspective case
317 SkScalar dstX = SkIntToScalar(x);
318 SkScalar dstY = SkIntToScalar(y);
319 for (; count > 0; --count) {
320 SkPoint srcPt;
321 dstProc(fDstToIndex, dstX, dstY, &srcPt);
322 SkScalar fx = srcPt.fX;
323 SkScalar fy = srcPt.fY;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000324 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
325 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
326 twoPointRadialGradient.fStartRadius) * 2;
327 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
328 twoPointRadialGradient.fOneOverTwoA, posRoot);
rileya@google.com589708b2012-07-26 20:04:23 +0000329 SkFixed index = proc(t);
330 SkASSERT(index <= 0xFFFF);
331 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
332 dstX += SK_Scalar1;
333 }
334 }
335}
336
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000337#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000338void SkTwoPointRadialGradient::toString(SkString* str) const {
339 str->append("SkTwoPointRadialGradient: (");
340
341 str->append("center1: (");
342 str->appendScalar(fCenter1.fX);
343 str->append(", ");
344 str->appendScalar(fCenter1.fY);
345 str->append(") radius1: ");
346 str->appendScalar(fRadius1);
347 str->append(" ");
348
349 str->append("center2: (");
350 str->appendScalar(fCenter2.fX);
351 str->append(", ");
352 str->appendScalar(fCenter2.fY);
353 str->append(") radius2: ");
354 str->appendScalar(fRadius2);
355 str->append(" ");
356
357 this->INHERITED::toString(str);
358
359 str->append(")");
360}
361#endif
362
reed9fa60da2014-08-21 07:59:51 -0700363SkFlattenable* SkTwoPointRadialGradient::CreateProc(SkReadBuffer& buffer) {
364 DescriptorScope desc;
365 if (!desc.unflatten(buffer)) {
366 return NULL;
367 }
368 const SkPoint c1 = buffer.readPoint();
369 const SkPoint c2 = buffer.readPoint();
370 const SkScalar r1 = buffer.readScalar();
371 const SkScalar r2 = buffer.readScalar();
372 return SkGradientShader::CreateTwoPointRadial(c1, r1, c2, r2, desc.fColors, desc.fPos,
373 desc.fCount, desc.fTileMode, desc.fGradFlags,
374 desc.fLocalMatrix);
375}
rileya@google.com589708b2012-07-26 20:04:23 +0000376
377void SkTwoPointRadialGradient::flatten(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000378 SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000379 this->INHERITED::flatten(buffer);
380 buffer.writePoint(fCenter1);
381 buffer.writePoint(fCenter2);
382 buffer.writeScalar(fRadius1);
383 buffer.writeScalar(fRadius2);
384}
385
rileya@google.comd7cc6512012-07-27 14:00:39 +0000386/////////////////////////////////////////////////////////////////////
387
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000388#if SK_SUPPORT_GPU
389
dandov9de5b512014-06-10 14:38:28 -0700390#include "SkGr.h"
joshualitteb2a6762014-12-04 11:35:33 -0800391#include "gl/builders/GrGLProgramBuilder.h"
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000392
rileya@google.comd7cc6512012-07-27 14:00:39 +0000393// For brevity
kkinnunen7510b222014-07-30 00:04:16 -0700394typedef GrGLProgramDataManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000395
bsalomon@google.com0707c292012-10-25 21:45:42 +0000396class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000397
398public:
399
joshualitteb2a6762014-12-04 11:35:33 -0800400 GrGLRadial2Gradient(const GrProcessor&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000401 virtual ~GrGLRadial2Gradient() { }
402
joshualitt15988992014-10-09 15:04:05 -0700403 virtual void emitCode(GrGLFPBuilder*,
joshualittb0a8a372014-09-23 09:50:21 -0700404 const GrFragmentProcessor&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000405 const char* outputColor,
406 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000407 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000408 const TextureSamplerArray&) SK_OVERRIDE;
mtklein72c9faa2015-01-09 10:06:39 -0800409 void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000410
joshualittb0a8a372014-09-23 09:50:21 -0700411 static void GenKey(const GrProcessor&, const GrGLCaps& caps, GrProcessorKeyBuilder* b);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000412
413protected:
414
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000415 UniformHandle fParamUni;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000416
417 const char* fVSVaryingName;
418 const char* fFSVaryingName;
419
420 bool fIsDegenerate;
421
422 // @{
423 /// Values last uploaded as uniforms
424
bsalomon@google.com81712882012-11-01 17:12:34 +0000425 SkScalar fCachedCenter;
426 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000427 bool fCachedPosRoot;
428
429 // @}
430
431private:
432
bsalomon@google.com0707c292012-10-25 21:45:42 +0000433 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000434
435};
436
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000437/////////////////////////////////////////////////////////////////////
438
439class GrRadial2Gradient : public GrGradientEffect {
440public:
joshualittb0a8a372014-09-23 09:50:21 -0700441 static GrFragmentProcessor* Create(GrContext* ctx,
442 const SkTwoPointRadialGradient& shader,
443 const SkMatrix& matrix,
444 SkShader::TileMode tm) {
bsalomon55fad7a2014-07-08 07:34:20 -0700445 return SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm));
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000446 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000447
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000448 virtual ~GrRadial2Gradient() { }
449
mtklein72c9faa2015-01-09 10:06:39 -0800450 const char* name() const SK_OVERRIDE { return "Two-Point Radial Gradient"; }
joshualitteb2a6762014-12-04 11:35:33 -0800451
452 virtual void getGLProcessorKey(const GrGLCaps& caps,
453 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
454 GrGLRadial2Gradient::GenKey(*this, caps, b);
455 }
456
mtklein72c9faa2015-01-09 10:06:39 -0800457 GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
joshualitteb2a6762014-12-04 11:35:33 -0800458 return SkNEW_ARGS(GrGLRadial2Gradient, (*this));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000459 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000460
461 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000462 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
463 SkScalar center() const { return fCenterX1; }
464 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000465 bool isPosRoot() const { return SkToBool(fPosRoot); }
466
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000467private:
mtklein72c9faa2015-01-09 10:06:39 -0800468 bool onIsEqual(const GrFragmentProcessor& sBase) const SK_OVERRIDE {
joshualitt49586be2014-09-16 08:21:41 -0700469 const GrRadial2Gradient& s = sBase.cast<GrRadial2Gradient>();
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000470 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000471 this->fCenterX1 == s.fCenterX1 &&
472 this->fRadius0 == s.fRadius0 &&
473 this->fPosRoot == s.fPosRoot);
474 }
475
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000476 GrRadial2Gradient(GrContext* ctx,
477 const SkTwoPointRadialGradient& shader,
478 const SkMatrix& matrix,
479 SkShader::TileMode tm)
480 : INHERITED(ctx, shader, matrix, tm)
481 , fCenterX1(shader.getCenterX1())
482 , fRadius0(shader.getStartRadius())
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000483 , fPosRoot(shader.getDiffRadius() < 0) {
joshualitteb2a6762014-12-04 11:35:33 -0800484 this->initClassID<GrRadial2Gradient>();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000485 // We pass the linear part of the quadratic as a varying.
486 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
487 fBTransform = this->getCoordTransform();
488 SkMatrix& bMatrix = *fBTransform.accessMatrix();
489 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
490 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
491 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
492 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
493 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
494 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
495 this->addCoordTransform(&fBTransform);
496 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000497
joshualittb0a8a372014-09-23 09:50:21 -0700498 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000499
500 // @{
501 // Cache of values - these can change arbitrarily, EXCEPT
502 // we shouldn't change between degenerate and non-degenerate?!
503
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000504 GrCoordTransform fBTransform;
505 SkScalar fCenterX1;
506 SkScalar fRadius0;
507 SkBool8 fPosRoot;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000508
509 // @}
510
511 typedef GrGradientEffect INHERITED;
512};
513
514/////////////////////////////////////////////////////////////////////
515
joshualittb0a8a372014-09-23 09:50:21 -0700516GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000517
joshualittb0a8a372014-09-23 09:50:21 -0700518GrFragmentProcessor* GrRadial2Gradient::TestCreate(SkRandom* random,
519 GrContext* context,
520 const GrDrawTargetCaps&,
521 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000522 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
523 SkScalar radius1 = random->nextUScalar1();
524 SkPoint center2;
525 SkScalar radius2;
526 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000527 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000528 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000529 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000530 } while (radius1 == radius2);
531
532 SkColor colors[kMaxRandomGradientColors];
533 SkScalar stopsArray[kMaxRandomGradientColors];
534 SkScalar* stops = stopsArray;
535 SkShader::TileMode tm;
536 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
537 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
538 center2, radius2,
539 colors, stops, colorCount,
540 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000541 SkPaint paint;
joshualittb0a8a372014-09-23 09:50:21 -0700542 GrFragmentProcessor* fp;
bsalomon83d081a2014-07-08 09:56:10 -0700543 GrColor paintColor;
joshualitt5531d512014-12-17 15:50:11 -0800544 SkAssertResult(shader->asFragmentProcessor(context, paint,
545 GrProcessorUnitTest::TestMatrix(random), NULL,
546 &paintColor, &fp));
joshualittb0a8a372014-09-23 09:50:21 -0700547 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000548}
549
550/////////////////////////////////////////////////////////////////////
551
joshualitteb2a6762014-12-04 11:35:33 -0800552GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
553 : fVSVaryingName(NULL)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000554 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000555 , fCachedCenter(SK_ScalarMax)
556 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000557 , fCachedPosRoot(0) {
558
joshualittb0a8a372014-09-23 09:50:21 -0700559 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000560 fIsDegenerate = data.isDegenerate();
561}
562
joshualitt15988992014-10-09 15:04:05 -0700563void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800564 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000565 const char* outputColor,
566 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000567 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000568 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800569 const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800570 this->emitUniforms(builder, ge);
joshualitt30ba4362014-08-21 20:18:45 -0700571 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -0800572 kFloat_GrSLType, kDefault_GrSLPrecision,
573 "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000574
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000575 SkString cName("c");
576 SkString ac4Name("ac4");
577 SkString rootName("root");
578 SkString t;
579 SkString p0;
580 SkString p1;
581 SkString p2;
582 SkString p3;
583 SkString p4;
584 SkString p5;
585 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
586 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
587 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
588 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
589 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
590 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
591
joshualitt15988992014-10-09 15:04:05 -0700592 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000593 // We interpolate the linear component in coords[1].
joshualitt23e280d2014-09-18 12:26:38 -0700594 SkASSERT(coords[0].getType() == coords[1].getType());
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000595 const char* coords2D;
596 SkString bVar;
joshualitt23e280d2014-09-18 12:26:38 -0700597 if (kVec3f_GrSLType == coords[0].getType()) {
joshualitt30ba4362014-08-21 20:18:45 -0700598 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000599 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
600 coords2D = "interpolants.xy";
601 bVar = "interpolants.z";
602 } else {
603 coords2D = coords[0].c_str();
604 bVar.printf("%s.x", coords[1].c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000605 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000606
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000607 // c = (x^2)+(y^2) - params[4]
joshualitt30ba4362014-08-21 20:18:45 -0700608 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000609 cName.c_str(), coords2D, coords2D, p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000610
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000611 // If we aren't degenerate, emit some extra code, and accept a slightly
612 // more complex coord.
613 if (!fIsDegenerate) {
614
615 // ac4 = 4.0 * params[0] * c
joshualitt30ba4362014-08-21 20:18:45 -0700616 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000617 ac4Name.c_str(), p0.c_str(),
618 cName.c_str());
619
620 // root = sqrt(b^2-4ac)
621 // (abs to avoid exception due to fp precision)
joshualitt30ba4362014-08-21 20:18:45 -0700622 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000623 rootName.c_str(), bVar.c_str(), bVar.c_str(),
624 ac4Name.c_str());
625
626 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
627 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
628 rootName.c_str(), p1.c_str());
629 } else {
630 // t is: -c/b
631 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000632 }
633
joshualitt60030bc2014-11-25 14:21:55 -0800634 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000635}
636
kkinnunen7510b222014-07-30 00:04:16 -0700637void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700638 const GrProcessor& processor) {
639 INHERITED::setData(pdman, processor);
640 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000641 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000642 SkScalar centerX1 = data.center();
643 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000644 if (fCachedCenter != centerX1 ||
645 fCachedRadius != radius0 ||
646 fCachedPosRoot != data.isPosRoot()) {
647
bsalomon@google.com81712882012-11-01 17:12:34 +0000648 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000649
650 // When we're in the degenerate (linear) case, the second
651 // value will be INF but the program doesn't read it. (We
652 // use the same 6 uniforms even though we don't need them
653 // all in the linear case just to keep the code complexity
654 // down).
655 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000656 SkScalarToFloat(a),
657 1 / (2.f * SkScalarToFloat(a)),
658 SkScalarToFloat(centerX1),
659 SkScalarToFloat(radius0),
660 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000661 data.isPosRoot() ? 1.f : -1.f
662 };
663
kkinnunen7510b222014-07-30 00:04:16 -0700664 pdman.set1fv(fParamUni, 6, values);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000665 fCachedCenter = centerX1;
666 fCachedRadius = radius0;
667 fCachedPosRoot = data.isPosRoot();
668 }
669}
670
joshualittb0a8a372014-09-23 09:50:21 -0700671void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
672 const GrGLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700673 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700674 key[0] = GenBaseGradientKey(processor);
675 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000676}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000677
678/////////////////////////////////////////////////////////////////////
679
joshualittb0a8a372014-09-23 09:50:21 -0700680bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
joshualitt5531d512014-12-17 15:50:11 -0800681 const SkMatrix&,
joshualittb0a8a372014-09-23 09:50:21 -0700682 const SkMatrix* localMatrix, GrColor* paintColor,
683 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700684 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800685
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000686 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000687 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000688 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700689 return false;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000690 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000691 if (localMatrix) {
692 SkMatrix inv;
693 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700694 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000695 }
696 matrix.postConcat(inv);
697 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000698 matrix.postConcat(fPtsToUnit);
699
700 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000701 if (0 != diffLen) {
702 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000703 SkMatrix rot;
704 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
705 SkScalarMul(invDiffLen, fDiff.fX));
706 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000707 }
708
bsalomon83d081a2014-07-08 09:56:10 -0700709 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700710 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800711
dandov9de5b512014-06-10 14:38:28 -0700712 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000713}
714
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000715#else
716
joshualitt5531d512014-12-17 15:50:11 -0800717bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&,
718 const SkMatrix*,
joshualittb0a8a372014-09-23 09:50:21 -0700719 GrColor*, GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000720 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700721 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000722}
723
724#endif