blob: a598c6e0e371d0ae9d8d368ebae987b81895f9b9 [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
9 #include "SkTwoPointRadialGradient.h"
10
11/* Two-point radial gradients are specified by two circles, each with a center
12 point and radius. The gradient can be considered to be a series of
13 concentric circles, with the color interpolated from the start circle
14 (at t=0) to the end circle (at t=1).
15
16 For each point (x, y) in the span, we want to find the
17 interpolated circle that intersects that point. The center
18 of the desired circle (Cx, Cy) falls at some distance t
19 along the line segment between the start point (Sx, Sy) and
20 end point (Ex, Ey):
21
22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
23 Cy = (1 - t) * Sy + t * Ey
24
25 The radius of the desired circle (r) is also a linear interpolation t
26 between the start and end radii (Sr and Er):
27
28 r = (1 - t) * Sr + t * Er
29
30 But
31
32 (x - Cx)^2 + (y - Cy)^2 = r^2
33
34 so
35
36 (x - ((1 - t) * Sx + t * Ex))^2
37 + (y - ((1 - t) * Sy + t * Ey))^2
38 = ((1 - t) * Sr + t * Er)^2
39
40 Solving for t yields
41
42 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
43 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
44 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
45
46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
47
48 [Dx^2 + Dy^2 - Dr^2)] * t^2
49 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
50 + [dx^2 + dy^2 - Sr^2] = 0
51
52 A quadratic in t. The two roots of the quadratic reflect the two
53 possible circles on which the point may fall. Solving for t yields
54 the gradient value to use.
55
56 If a<0, the start circle is entirely contained in the
57 end circle, and one of the roots will be <0 or >1 (off the line
58 segment). If a>0, the start circle falls at least partially
59 outside the end circle (or vice versa), and the gradient
60 defines a "tube" where a point may be on one circle (on the
61 inside of the tube) or the other (outside of the tube). We choose
62 one arbitrarily.
63
64 In order to keep the math to within the limits of fixed point,
65 we divide the entire quadratic by Dr^2, and replace
66 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
67
68 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
69 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
70 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
71
72 (x' and y' are computed by appending the subtract and scale to the
73 fDstToIndex matrix in the constructor).
74
75 Since the 'A' component of the quadratic is independent of x' and y', it
76 is precomputed in the constructor. Since the 'B' component is linear in
77 x' and y', if x and y are linear in the span, 'B' can be computed
78 incrementally with a simple delta (db below). If it is not (e.g.,
79 a perspective projection), it must be computed in the loop.
80
81*/
82
83namespace {
84
85inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
86 SkScalar sr2d2, SkScalar foura,
87 SkScalar oneOverTwoA, bool posRoot) {
88 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
89 if (0 == foura) {
90 return SkScalarToFixed(SkScalarDiv(-c, b));
91 }
92
93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
94 if (discrim < 0) {
95 discrim = -discrim;
96 }
97 SkScalar rootDiscrim = SkScalarSqrt(discrim);
98 SkScalar result;
99 if (posRoot) {
100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
101 } else {
102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
103 }
104 return SkScalarToFixed(result);
105}
106
107typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
108 SkScalar fy, SkScalar dy,
109 SkScalar b, SkScalar db,
110 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
111 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
112 int count);
113
114void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
115 SkScalar fy, SkScalar dy,
116 SkScalar b, SkScalar db,
117 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
118 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
119 int count) {
120 for (; count > 0; --count) {
121 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
122 fOneOverTwoA, posRoot);
123 SkFixed index = SkClampMax(t, 0xFFFF);
124 SkASSERT(index <= 0xFFFF);
125 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
126 fx += dx;
127 fy += dy;
128 b += db;
129 }
130}
131void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
132 SkScalar fy, SkScalar dy,
133 SkScalar b, SkScalar db,
134 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
135 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
136 int count) {
137 for (; count > 0; --count) {
138 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
139 fOneOverTwoA, posRoot);
140 SkFixed index = mirror_tileproc(t);
141 SkASSERT(index <= 0xFFFF);
142 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
143 fx += dx;
144 fy += dy;
145 b += db;
146 }
147}
148
149void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
150 SkScalar fy, SkScalar dy,
151 SkScalar b, SkScalar db,
152 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
153 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
154 int count) {
155 for (; count > 0; --count) {
156 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
157 fOneOverTwoA, posRoot);
158 SkFixed index = repeat_tileproc(t);
159 SkASSERT(index <= 0xFFFF);
160 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
161 fx += dx;
162 fy += dy;
163 b += db;
164 }
165}
166}
167
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000168/////////////////////////////////////////////////////////////////////
169
rileya@google.com589708b2012-07-26 20:04:23 +0000170SkTwoPointRadialGradient::SkTwoPointRadialGradient(
171 const SkPoint& start, SkScalar startRadius,
172 const SkPoint& end, SkScalar endRadius,
reed@google.com437d6eb2013-05-23 19:03:05 +0000173 const Descriptor& desc)
174 : SkGradientShaderBase(desc),
rileya@google.com589708b2012-07-26 20:04:23 +0000175 fCenter1(start),
176 fCenter2(end),
177 fRadius1(startRadius),
178 fRadius2(endRadius) {
179 init();
180}
181
182SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
183 SkBitmap* bitmap,
184 SkMatrix* matrix,
185 SkShader::TileMode* xy) const {
186 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000187 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000188 }
189 SkScalar diffL = 0; // just to avoid gcc warning
190 if (matrix) {
191 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
192 SkScalarSquare(fDiff.fY));
193 }
194 if (matrix) {
195 if (diffL) {
196 SkScalar invDiffL = SkScalarInvert(diffL);
197 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
198 SkScalarMul(invDiffL, fDiff.fX));
199 } else {
200 matrix->reset();
201 }
202 matrix->preConcat(fPtsToUnit);
203 }
204 if (xy) {
205 xy[0] = fTileMode;
206 xy[1] = kClamp_TileMode;
207 }
208 return kTwoPointRadial_BitmapType;
209}
210
211SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
212 SkShader::GradientInfo* info) const {
213 if (info) {
214 commonAsAGradient(info);
215 info->fPoint[0] = fCenter1;
216 info->fPoint[1] = fCenter2;
217 info->fRadius[0] = fRadius1;
218 info->fRadius[1] = fRadius2;
219 }
220 return kRadial2_GradientType;
221}
222
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000223size_t SkTwoPointRadialGradient::contextSize() const {
224 return sizeof(TwoPointRadialGradientContext);
225}
226
227bool SkTwoPointRadialGradient::validContext(const SkBitmap& device, const SkPaint& paint,
228 const SkMatrix& matrix, SkMatrix* totalInverse) const {
229 // For now, we might have divided by zero, so detect that.
230 if (0 == fDiffRadius) {
231 return false;
232 }
233
234 return this->INHERITED::validContext(device, paint, matrix, totalInverse);
235}
236
237SkShader::Context* SkTwoPointRadialGradient::createContext(
238 const SkBitmap& device, const SkPaint& paint,
239 const SkMatrix& matrix, void* storage) const {
240 if (!this->validContext(device, paint, matrix)) {
241 return NULL;
242 }
243
244 return SkNEW_PLACEMENT_ARGS(storage, TwoPointRadialGradientContext,
245 (*this, device, paint, matrix));
246}
247
248SkTwoPointRadialGradient::TwoPointRadialGradientContext::TwoPointRadialGradientContext(
249 const SkTwoPointRadialGradient& shader, const SkBitmap& device,
250 const SkPaint& paint, const SkMatrix& matrix)
251 : INHERITED(shader, device, paint, matrix)
252{
253 // we don't have a span16 proc
254 fFlags &= ~kHasSpan16_Flag;
255}
256
257void SkTwoPointRadialGradient::TwoPointRadialGradientContext::shadeSpan(
258 int x, int y, SkPMColor* dstCParam, int count) {
rileya@google.com589708b2012-07-26 20:04:23 +0000259 SkASSERT(count > 0);
260
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000261 const SkTwoPointRadialGradient& twoPointRadialGradient =
262 static_cast<const SkTwoPointRadialGradient&>(fShader);
263
rileya@google.com589708b2012-07-26 20:04:23 +0000264 SkPMColor* SK_RESTRICT dstC = dstCParam;
265
266 // Zero difference between radii: fill with transparent black.
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000267 if (twoPointRadialGradient.fDiffRadius == 0) {
rileya@google.com589708b2012-07-26 20:04:23 +0000268 sk_bzero(dstC, count * sizeof(*dstC));
269 return;
270 }
271 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000272 TileProc proc = twoPointRadialGradient.fTileProc;
273 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
rileya@google.com589708b2012-07-26 20:04:23 +0000274
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000275 SkScalar foura = twoPointRadialGradient.fA * 4;
276 bool posRoot = twoPointRadialGradient.fDiffRadius < 0;
rileya@google.com589708b2012-07-26 20:04:23 +0000277 if (fDstToIndexClass != kPerspective_MatrixClass) {
278 SkPoint srcPt;
279 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
280 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
281 SkScalar dx, fx = srcPt.fX;
282 SkScalar dy, fy = srcPt.fY;
283
284 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
285 SkFixed fixedX, fixedY;
286 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
287 dx = SkFixedToScalar(fixedX);
288 dy = SkFixedToScalar(fixedY);
289 } else {
290 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
291 dx = fDstToIndex.getScaleX();
292 dy = fDstToIndex.getSkewY();
293 }
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000294 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
295 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
296 twoPointRadialGradient.fStartRadius) * 2;
297 SkScalar db = (SkScalarMul(twoPointRadialGradient.fDiff.fX, dx) +
298 SkScalarMul(twoPointRadialGradient.fDiff.fY, dy)) * 2;
rileya@google.com589708b2012-07-26 20:04:23 +0000299
300 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000301 if (SkShader::kClamp_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000302 shadeProc = shadeSpan_twopoint_clamp;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000303 } else if (SkShader::kMirror_TileMode == twoPointRadialGradient.fTileMode) {
rileya@google.com589708b2012-07-26 20:04:23 +0000304 shadeProc = shadeSpan_twopoint_mirror;
305 } else {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000306 SkASSERT(SkShader::kRepeat_TileMode == twoPointRadialGradient.fTileMode);
rileya@google.com589708b2012-07-26 20:04:23 +0000307 }
308 (*shadeProc)(fx, dx, fy, dy, b, db,
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000309 twoPointRadialGradient.fSr2D2, foura,
310 twoPointRadialGradient.fOneOverTwoA, posRoot,
rileya@google.com589708b2012-07-26 20:04:23 +0000311 dstC, cache, count);
312 } else { // perspective case
313 SkScalar dstX = SkIntToScalar(x);
314 SkScalar dstY = SkIntToScalar(y);
315 for (; count > 0; --count) {
316 SkPoint srcPt;
317 dstProc(fDstToIndex, dstX, dstY, &srcPt);
318 SkScalar fx = srcPt.fX;
319 SkScalar fy = srcPt.fY;
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000320 SkScalar b = (SkScalarMul(twoPointRadialGradient.fDiff.fX, fx) +
321 SkScalarMul(twoPointRadialGradient.fDiff.fY, fy) -
322 twoPointRadialGradient.fStartRadius) * 2;
323 SkFixed t = two_point_radial(b, fx, fy, twoPointRadialGradient.fSr2D2, foura,
324 twoPointRadialGradient.fOneOverTwoA, posRoot);
rileya@google.com589708b2012-07-26 20:04:23 +0000325 SkFixed index = proc(t);
326 SkASSERT(index <= 0xFFFF);
327 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
328 dstX += SK_Scalar1;
329 }
330 }
331}
332
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000333#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000334void SkTwoPointRadialGradient::toString(SkString* str) const {
335 str->append("SkTwoPointRadialGradient: (");
336
337 str->append("center1: (");
338 str->appendScalar(fCenter1.fX);
339 str->append(", ");
340 str->appendScalar(fCenter1.fY);
341 str->append(") radius1: ");
342 str->appendScalar(fRadius1);
343 str->append(" ");
344
345 str->append("center2: (");
346 str->appendScalar(fCenter2.fX);
347 str->append(", ");
348 str->appendScalar(fCenter2.fY);
349 str->append(") radius2: ");
350 str->appendScalar(fRadius2);
351 str->append(" ");
352
353 this->INHERITED::toString(str);
354
355 str->append(")");
356}
357#endif
358
rileya@google.com589708b2012-07-26 20:04:23 +0000359SkTwoPointRadialGradient::SkTwoPointRadialGradient(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000360 SkReadBuffer& buffer)
rileya@google.com589708b2012-07-26 20:04:23 +0000361 : INHERITED(buffer),
362 fCenter1(buffer.readPoint()),
363 fCenter2(buffer.readPoint()),
364 fRadius1(buffer.readScalar()),
365 fRadius2(buffer.readScalar()) {
366 init();
367};
368
369void SkTwoPointRadialGradient::flatten(
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000370 SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000371 this->INHERITED::flatten(buffer);
372 buffer.writePoint(fCenter1);
373 buffer.writePoint(fCenter2);
374 buffer.writeScalar(fRadius1);
375 buffer.writeScalar(fRadius2);
376}
377
378void SkTwoPointRadialGradient::init() {
379 fDiff = fCenter1 - fCenter2;
380 fDiffRadius = fRadius2 - fRadius1;
381 // hack to avoid zero-divide for now
382 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
383 fDiff.fX = SkScalarMul(fDiff.fX, inv);
384 fDiff.fY = SkScalarMul(fDiff.fY, inv);
385 fStartRadius = SkScalarMul(fRadius1, inv);
386 fSr2D2 = SkScalarSquare(fStartRadius);
387 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
388 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
389
390 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
391 fPtsToUnit.postScale(inv, inv);
392}
393
rileya@google.comd7cc6512012-07-27 14:00:39 +0000394/////////////////////////////////////////////////////////////////////
395
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000396#if SK_SUPPORT_GPU
397
bsalomon@google.com2eaaefd2012-10-29 19:51:22 +0000398#include "GrTBackendEffectFactory.h"
399
rileya@google.comd7cc6512012-07-27 14:00:39 +0000400// For brevity
401typedef GrGLUniformManager::UniformHandle UniformHandle;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000402
bsalomon@google.com0707c292012-10-25 21:45:42 +0000403class GrGLRadial2Gradient : public GrGLGradientEffect {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000404
405public:
406
bsalomon@google.comc7818882013-03-20 19:19:53 +0000407 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000408 virtual ~GrGLRadial2Gradient() { }
409
bsalomon@google.comf78df332012-10-29 12:43:38 +0000410 virtual void emitCode(GrGLShaderBuilder*,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000411 const GrDrawEffect&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000412 EffectKey,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000413 const char* outputColor,
414 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000415 const TransformedCoordsArray&,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000416 const TextureSamplerArray&) SK_OVERRIDE;
bsalomon@google.comc7818882013-03-20 19:19:53 +0000417 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000418
bsalomon@google.comc7818882013-03-20 19:19:53 +0000419 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000420
421protected:
422
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000423 UniformHandle fParamUni;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000424
425 const char* fVSVaryingName;
426 const char* fFSVaryingName;
427
428 bool fIsDegenerate;
429
430 // @{
431 /// Values last uploaded as uniforms
432
bsalomon@google.com81712882012-11-01 17:12:34 +0000433 SkScalar fCachedCenter;
434 SkScalar fCachedRadius;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000435 bool fCachedPosRoot;
436
437 // @}
438
439private:
440
bsalomon@google.com0707c292012-10-25 21:45:42 +0000441 typedef GrGLGradientEffect INHERITED;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000442
443};
444
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000445/////////////////////////////////////////////////////////////////////
446
447class GrRadial2Gradient : public GrGradientEffect {
448public:
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000449 static GrEffectRef* Create(GrContext* ctx,
450 const SkTwoPointRadialGradient& shader,
451 const SkMatrix& matrix,
452 SkShader::TileMode tm) {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000453 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
bsalomon@google.coma1ebbe42013-01-16 15:51:47 +0000454 return CreateEffectRef(effect);
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000455 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000456
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000457 virtual ~GrRadial2Gradient() { }
458
459 static const char* Name() { return "Two-Point Radial Gradient"; }
bsalomon@google.com396e61f2012-10-25 19:00:29 +0000460 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
461 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000462 }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000463
464 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
bsalomon@google.com81712882012-11-01 17:12:34 +0000465 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
466 SkScalar center() const { return fCenterX1; }
467 SkScalar radius() const { return fRadius0; }
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000468 bool isPosRoot() const { return SkToBool(fPosRoot); }
469
bsalomon@google.com422e81a2012-10-25 14:11:03 +0000470 typedef GrGLRadial2Gradient GLEffect;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000471
472private:
bsalomon@google.com8a252f72013-01-22 20:35:13 +0000473 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
bsalomon@google.com6340a412013-01-22 19:55:59 +0000474 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
bsalomon@google.com70db51f2013-01-17 18:39:59 +0000475 return (INHERITED::onIsEqual(sBase) &&
bsalomon@google.com68b58c92013-01-17 16:50:08 +0000476 this->fCenterX1 == s.fCenterX1 &&
477 this->fRadius0 == s.fRadius0 &&
478 this->fPosRoot == s.fPosRoot);
479 }
480
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000481 GrRadial2Gradient(GrContext* ctx,
482 const SkTwoPointRadialGradient& shader,
483 const SkMatrix& matrix,
484 SkShader::TileMode tm)
485 : INHERITED(ctx, shader, matrix, tm)
486 , fCenterX1(shader.getCenterX1())
487 , fRadius0(shader.getStartRadius())
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000488 , fPosRoot(shader.getDiffRadius() < 0) {
489 // We pass the linear part of the quadratic as a varying.
490 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
491 fBTransform = this->getCoordTransform();
492 SkMatrix& bMatrix = *fBTransform.accessMatrix();
493 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
494 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
495 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
496 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
497 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
498 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
499 this->addCoordTransform(&fBTransform);
500 }
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000501
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000502 GR_DECLARE_EFFECT_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000503
504 // @{
505 // Cache of values - these can change arbitrarily, EXCEPT
506 // we shouldn't change between degenerate and non-degenerate?!
507
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000508 GrCoordTransform fBTransform;
509 SkScalar fCenterX1;
510 SkScalar fRadius0;
511 SkBool8 fPosRoot;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000512
513 // @}
514
515 typedef GrGradientEffect INHERITED;
516};
517
518/////////////////////////////////////////////////////////////////////
519
bsalomon@google.comf271cc72012-10-24 19:35:13 +0000520GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000521
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000522GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000523 GrContext* context,
bsalomon@google.comc26d94f2013-03-25 18:19:00 +0000524 const GrDrawTargetCaps&,
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000525 GrTexture**) {
bsalomon@google.comd4726202012-08-03 14:34:46 +0000526 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
527 SkScalar radius1 = random->nextUScalar1();
528 SkPoint center2;
529 SkScalar radius2;
530 do {
bsalomon@google.comfb883bf2012-12-11 15:32:04 +0000531 center2.set(random->nextUScalar1(), random->nextUScalar1());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000532 radius2 = random->nextUScalar1 ();
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000533 // There is a bug in two point radial gradients with identical radii
bsalomon@google.comd4726202012-08-03 14:34:46 +0000534 } while (radius1 == radius2);
535
536 SkColor colors[kMaxRandomGradientColors];
537 SkScalar stopsArray[kMaxRandomGradientColors];
538 SkScalar* stops = stopsArray;
539 SkShader::TileMode tm;
540 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
541 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
542 center2, radius2,
543 colors, stops, colorCount,
544 tm));
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000545 SkPaint paint;
546 return shader->asNewEffect(context, paint);
bsalomon@google.comd4726202012-08-03 14:34:46 +0000547}
548
549/////////////////////////////////////////////////////////////////////
550
bsalomon@google.com6340a412013-01-22 19:55:59 +0000551GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000552 const GrDrawEffect& drawEffect)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000553 : INHERITED(factory)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000554 , fVSVaryingName(NULL)
555 , fFSVaryingName(NULL)
bsalomon@google.com81712882012-11-01 17:12:34 +0000556 , fCachedCenter(SK_ScalarMax)
557 , fCachedRadius(-SK_ScalarMax)
rileya@google.comd7cc6512012-07-27 14:00:39 +0000558 , fCachedPosRoot(0) {
559
bsalomon@google.comc7818882013-03-20 19:19:53 +0000560 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
rileya@google.comd7cc6512012-07-27 14:00:39 +0000561 fIsDegenerate = data.isDegenerate();
562}
563
bsalomon@google.comf78df332012-10-29 12:43:38 +0000564void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
bsalomon@google.comc7818882013-03-20 19:19:53 +0000565 const GrDrawEffect& drawEffect,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000566 EffectKey key,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000567 const char* outputColor,
568 const char* inputColor,
bsalomon@google.com77af6802013-10-02 13:04:56 +0000569 const TransformedCoordsArray& coords,
bsalomon@google.comf78df332012-10-29 12:43:38 +0000570 const TextureSamplerArray& samplers) {
571
bsalomon@google.com82d12232013-09-09 15:36:26 +0000572 this->emitUniforms(builder, key);
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000573 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
574 kFloat_GrSLType, "Radial2FSParams", 6);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000575
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000576 SkString cName("c");
577 SkString ac4Name("ac4");
578 SkString rootName("root");
579 SkString t;
580 SkString p0;
581 SkString p1;
582 SkString p2;
583 SkString p3;
584 SkString p4;
585 SkString p5;
586 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
587 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
588 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
589 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
590 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
591 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
592
593 // We interpolate the linear component in coords[1].
594 SkASSERT(coords[0].type() == coords[1].type());
595 const char* coords2D;
596 SkString bVar;
597 if (kVec3f_GrSLType == coords[0].type()) {
598 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
599 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]
608 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
609 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
616 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
617 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)
622 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
623 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
commit-bot@chromium.org5fd7d5c2013-10-04 01:20:09 +0000634 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000635}
636
bsalomon@google.comc7818882013-03-20 19:19:53 +0000637void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
638 const GrDrawEffect& drawEffect) {
639 INHERITED::setData(uman, drawEffect);
640 const GrRadial2Gradient& data = drawEffect.castEffect<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
commit-bot@chromium.orgd3baf202013-11-07 22:06:08 +0000664 uman.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
bsalomon@google.comc7818882013-03-20 19:19:53 +0000671GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
672 const GrGLCaps&) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000673 enum {
bsalomon@google.com82d12232013-09-09 15:36:26 +0000674 kIsDegenerate = 1 << kBaseKeyBitCnt,
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000675 };
676
bsalomon@google.com82d12232013-09-09 15:36:26 +0000677 EffectKey key = GenBaseGradientKey(drawEffect);
bsalomon@google.comc7818882013-03-20 19:19:53 +0000678 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
bsalomon@google.comd8b5fac2012-11-01 17:02:46 +0000679 key |= kIsDegenerate;
680 }
681 return key;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000682}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000683
684/////////////////////////////////////////////////////////////////////
685
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000686GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
bsalomon@google.com00835cc2013-01-14 17:07:22 +0000687 SkASSERT(NULL != context);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000688 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000689 SkMatrix matrix;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000690 if (!this->getLocalMatrix().invert(&matrix)) {
humper@google.com84831ac2013-01-14 22:09:54 +0000691 return NULL;
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000692 }
693 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
bsalomon@google.com0ac6af42013-01-16 15:16:18 +0000704 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000705}
706
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000707#else
708
bsalomon@google.com5d2cd202013-01-16 15:31:06 +0000709GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000710 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.come197cbf2013-01-14 16:46:26 +0000711 return NULL;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000712}
713
714#endif