blob: 3a3921d14ccc0459fc6b5ef57c67ab3ec8e2b679 [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;
joshualittb0a8a372014-09-23 09:50:21 -0700409 virtual 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
joshualitteb2a6762014-12-04 11:35:33 -0800450 virtual const char* name() const SK_OVERRIDE { return "Two-Point Radial Gradient"; }
451
452 virtual void getGLProcessorKey(const GrGLCaps& caps,
453 GrProcessorKeyBuilder* b) const SK_OVERRIDE {
454 GrGLRadial2Gradient::GenKey(*this, caps, b);
455 }
456
457 virtual GrGLFragmentProcessor* createGLInstance() const SK_OVERRIDE {
458 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:
bsalomon0e08fc12014-10-15 08:19:04 -0700468 virtual 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;
joshualittb0a8a372014-09-23 09:50:21 -0700544 SkAssertResult(shader->asFragmentProcessor(context, paint, NULL, &paintColor, &fp));
545 return fp;
bsalomon@google.comd4726202012-08-03 14:34:46 +0000546}
547
548/////////////////////////////////////////////////////////////////////
549
joshualitteb2a6762014-12-04 11:35:33 -0800550GrGLRadial2Gradient::GrGLRadial2Gradient(const GrProcessor& processor)
551 : fVSVaryingName(NULL)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000552 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000553 , fCachedCenter(SK_ScalarMax)
554 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000555 , fCachedPosRoot(0) {
556
joshualittb0a8a372014-09-23 09:50:21 -0700557 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000558 fIsDegenerate = data.isDegenerate();
559}
560
joshualitt15988992014-10-09 15:04:05 -0700561void GrGLRadial2Gradient::emitCode(GrGLFPBuilder* builder,
joshualitt60030bc2014-11-25 14:21:55 -0800562 const GrFragmentProcessor& fp,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000563 const char* outputColor,
564 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000565 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000566 const TextureSamplerArray& samplers) {
joshualitteb2a6762014-12-04 11:35:33 -0800567 const GrRadial2Gradient& ge = fp.cast<GrRadial2Gradient>();
joshualitt60030bc2014-11-25 14:21:55 -0800568 this->emitUniforms(builder, ge);
joshualitt30ba4362014-08-21 20:18:45 -0700569 fParamUni = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -0800570 kFloat_GrSLType, kDefault_GrSLPrecision,
571 "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000572
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000573 SkString cName("c");
574 SkString ac4Name("ac4");
575 SkString rootName("root");
576 SkString t;
577 SkString p0;
578 SkString p1;
579 SkString p2;
580 SkString p3;
581 SkString p4;
582 SkString p5;
583 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
584 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
585 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
586 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
587 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
588 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
589
joshualitt15988992014-10-09 15:04:05 -0700590 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000591 // We interpolate the linear component in coords[1].
joshualitt23e280d2014-09-18 12:26:38 -0700592 SkASSERT(coords[0].getType() == coords[1].getType());
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000593 const char* coords2D;
594 SkString bVar;
joshualitt23e280d2014-09-18 12:26:38 -0700595 if (kVec3f_GrSLType == coords[0].getType()) {
joshualitt30ba4362014-08-21 20:18:45 -0700596 fsBuilder->codeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000597 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
598 coords2D = "interpolants.xy";
599 bVar = "interpolants.z";
600 } else {
601 coords2D = coords[0].c_str();
602 bVar.printf("%s.x", coords[1].c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000603 }
rileya@google.comd7cc6512012-07-27 14:00:39 +0000604
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000605 // c = (x^2)+(y^2) - params[4]
joshualitt30ba4362014-08-21 20:18:45 -0700606 fsBuilder->codeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000607 cName.c_str(), coords2D, coords2D, p4.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000608
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000609 // If we aren't degenerate, emit some extra code, and accept a slightly
610 // more complex coord.
611 if (!fIsDegenerate) {
612
613 // ac4 = 4.0 * params[0] * c
joshualitt30ba4362014-08-21 20:18:45 -0700614 fsBuilder->codeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000615 ac4Name.c_str(), p0.c_str(),
616 cName.c_str());
617
618 // root = sqrt(b^2-4ac)
619 // (abs to avoid exception due to fp precision)
joshualitt30ba4362014-08-21 20:18:45 -0700620 fsBuilder->codeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000621 rootName.c_str(), bVar.c_str(), bVar.c_str(),
622 ac4Name.c_str());
623
624 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
625 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
626 rootName.c_str(), p1.c_str());
627 } else {
628 // t is: -c/b
629 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000630 }
631
joshualitt60030bc2014-11-25 14:21:55 -0800632 this->emitColor(builder, ge, t.c_str(), outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000633}
634
kkinnunen7510b222014-07-30 00:04:16 -0700635void GrGLRadial2Gradient::setData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700636 const GrProcessor& processor) {
637 INHERITED::setData(pdman, processor);
638 const GrRadial2Gradient& data = processor.cast<GrRadial2Gradient>();
commit-bot@chromium.org96ae6882013-08-14 12:09:00 +0000639 SkASSERT(data.isDegenerate() == fIsDegenerate);
bsalomon@google.com81712882012-11-01 17:12:34 +0000640 SkScalar centerX1 = data.center();
641 SkScalar radius0 = data.radius();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000642 if (fCachedCenter != centerX1 ||
643 fCachedRadius != radius0 ||
644 fCachedPosRoot != data.isPosRoot()) {
645
bsalomon@google.com81712882012-11-01 17:12:34 +0000646 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000647
648 // When we're in the degenerate (linear) case, the second
649 // value will be INF but the program doesn't read it. (We
650 // use the same 6 uniforms even though we don't need them
651 // all in the linear case just to keep the code complexity
652 // down).
653 float values[6] = {
bsalomon@google.com81712882012-11-01 17:12:34 +0000654 SkScalarToFloat(a),
655 1 / (2.f * SkScalarToFloat(a)),
656 SkScalarToFloat(centerX1),
657 SkScalarToFloat(radius0),
658 SkScalarToFloat(SkScalarMul(radius0, radius0)),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000659 data.isPosRoot() ? 1.f : -1.f
660 };
661
kkinnunen7510b222014-07-30 00:04:16 -0700662 pdman.set1fv(fParamUni, 6, values);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000663 fCachedCenter = centerX1;
664 fCachedRadius = radius0;
665 fCachedPosRoot = data.isPosRoot();
666 }
667}
668
joshualittb0a8a372014-09-23 09:50:21 -0700669void GrGLRadial2Gradient::GenKey(const GrProcessor& processor,
670 const GrGLCaps&, GrProcessorKeyBuilder* b) {
bsalomon63e99f72014-07-21 08:03:14 -0700671 uint32_t* key = b->add32n(2);
joshualittb0a8a372014-09-23 09:50:21 -0700672 key[0] = GenBaseGradientKey(processor);
673 key[1] = processor.cast<GrRadial2Gradient>().isDegenerate();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000674}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000675
676/////////////////////////////////////////////////////////////////////
677
joshualittb0a8a372014-09-23 09:50:21 -0700678bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext* context, const SkPaint& paint,
679 const SkMatrix* localMatrix, GrColor* paintColor,
680 GrFragmentProcessor** fp) const {
bsalomon49f085d2014-09-05 13:34:00 -0700681 SkASSERT(context);
mtklein3f3b3d02014-12-01 11:47:08 -0800682
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000683 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000684 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000685 if (!this->getLocalMatrix().invert(&matrix)) {
dandov9de5b512014-06-10 14:38:28 -0700686 return false;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000687 }
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000688 if (localMatrix) {
689 SkMatrix inv;
690 if (!localMatrix->invert(&inv)) {
dandov9de5b512014-06-10 14:38:28 -0700691 return false;
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000692 }
693 matrix.postConcat(inv);
694 }
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000695 matrix.postConcat(fPtsToUnit);
696
697 SkScalar diffLen = fDiff.length();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000698 if (0 != diffLen) {
699 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000700 SkMatrix rot;
701 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
702 SkScalarMul(invDiffLen, fDiff.fX));
703 matrix.postConcat(rot);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000704 }
705
bsalomon83d081a2014-07-08 09:56:10 -0700706 *paintColor = SkColor2GrColorJustAlpha(paint.getColor());
joshualittb0a8a372014-09-23 09:50:21 -0700707 *fp = GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
mtklein3f3b3d02014-12-01 11:47:08 -0800708
dandov9de5b512014-06-10 14:38:28 -0700709 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000710}
711
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000712#else
713
joshualittb0a8a372014-09-23 09:50:21 -0700714bool SkTwoPointRadialGradient::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*,
715 GrColor*, GrFragmentProcessor**) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000716 SkDEBUGFAIL("Should not call in GPU-less build");
dandov9de5b512014-06-10 14:38:28 -0700717 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000718}
719
720#endif