blob: 2715511e1b0503375ad3fd4adb281728e0958d8e [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,
173 const SkColor colors[], const SkScalar pos[],
174 int colorCount, SkShader::TileMode mode,
175 SkUnitMapper* mapper)
176 : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
177 fCenter1(start),
178 fCenter2(end),
179 fRadius1(startRadius),
180 fRadius2(endRadius) {
181 init();
182}
183
184SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
185 SkBitmap* bitmap,
186 SkMatrix* matrix,
187 SkShader::TileMode* xy) const {
188 if (bitmap) {
rileya@google.com1c6d64b2012-07-27 15:49:05 +0000189 this->getGradientTableBitmap(bitmap);
rileya@google.com589708b2012-07-26 20:04:23 +0000190 }
191 SkScalar diffL = 0; // just to avoid gcc warning
192 if (matrix) {
193 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
194 SkScalarSquare(fDiff.fY));
195 }
196 if (matrix) {
197 if (diffL) {
198 SkScalar invDiffL = SkScalarInvert(diffL);
199 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
200 SkScalarMul(invDiffL, fDiff.fX));
201 } else {
202 matrix->reset();
203 }
204 matrix->preConcat(fPtsToUnit);
205 }
206 if (xy) {
207 xy[0] = fTileMode;
208 xy[1] = kClamp_TileMode;
209 }
210 return kTwoPointRadial_BitmapType;
211}
212
213SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
214 SkShader::GradientInfo* info) const {
215 if (info) {
216 commonAsAGradient(info);
217 info->fPoint[0] = fCenter1;
218 info->fPoint[1] = fCenter2;
219 info->fRadius[0] = fRadius1;
220 info->fRadius[1] = fRadius2;
221 }
222 return kRadial2_GradientType;
223}
224
rileya@google.com589708b2012-07-26 20:04:23 +0000225void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
226 int count) {
227 SkASSERT(count > 0);
228
229 SkPMColor* SK_RESTRICT dstC = dstCParam;
230
231 // Zero difference between radii: fill with transparent black.
232 if (fDiffRadius == 0) {
233 sk_bzero(dstC, count * sizeof(*dstC));
234 return;
235 }
236 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
237 TileProc proc = fTileProc;
238 const SkPMColor* SK_RESTRICT cache = this->getCache32();
239
240 SkScalar foura = fA * 4;
241 bool posRoot = fDiffRadius < 0;
242 if (fDstToIndexClass != kPerspective_MatrixClass) {
243 SkPoint srcPt;
244 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
245 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
246 SkScalar dx, fx = srcPt.fX;
247 SkScalar dy, fy = srcPt.fY;
248
249 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
250 SkFixed fixedX, fixedY;
251 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
252 dx = SkFixedToScalar(fixedX);
253 dy = SkFixedToScalar(fixedY);
254 } else {
255 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
256 dx = fDstToIndex.getScaleX();
257 dy = fDstToIndex.getSkewY();
258 }
259 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
260 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
261 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
262 SkScalarMul(fDiff.fY, dy)) * 2;
263
264 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
265 if (SkShader::kClamp_TileMode == fTileMode) {
266 shadeProc = shadeSpan_twopoint_clamp;
267 } else if (SkShader::kMirror_TileMode == fTileMode) {
268 shadeProc = shadeSpan_twopoint_mirror;
269 } else {
270 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
271 }
272 (*shadeProc)(fx, dx, fy, dy, b, db,
273 fSr2D2, foura, fOneOverTwoA, posRoot,
274 dstC, cache, count);
275 } else { // perspective case
276 SkScalar dstX = SkIntToScalar(x);
277 SkScalar dstY = SkIntToScalar(y);
278 for (; count > 0; --count) {
279 SkPoint srcPt;
280 dstProc(fDstToIndex, dstX, dstY, &srcPt);
281 SkScalar fx = srcPt.fX;
282 SkScalar fy = srcPt.fY;
283 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
284 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
285 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
286 fOneOverTwoA, posRoot);
287 SkFixed index = proc(t);
288 SkASSERT(index <= 0xFFFF);
289 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
290 dstX += SK_Scalar1;
291 }
292 }
293}
294
295bool SkTwoPointRadialGradient::setContext(
296 const SkBitmap& device,
297 const SkPaint& paint,
298 const SkMatrix& matrix){
299 if (!this->INHERITED::setContext(device, paint, matrix)) {
300 return false;
301 }
302
303 // For now, we might have divided by zero, so detect that
304 if (0 == fDiffRadius) {
305 return false;
306 }
307
308 // we don't have a span16 proc
309 fFlags &= ~kHasSpan16_Flag;
310 return true;
311}
312
313SkTwoPointRadialGradient::SkTwoPointRadialGradient(
314 SkFlattenableReadBuffer& buffer)
315 : INHERITED(buffer),
316 fCenter1(buffer.readPoint()),
317 fCenter2(buffer.readPoint()),
318 fRadius1(buffer.readScalar()),
319 fRadius2(buffer.readScalar()) {
320 init();
321};
322
323void SkTwoPointRadialGradient::flatten(
324 SkFlattenableWriteBuffer& buffer) const {
325 this->INHERITED::flatten(buffer);
326 buffer.writePoint(fCenter1);
327 buffer.writePoint(fCenter2);
328 buffer.writeScalar(fRadius1);
329 buffer.writeScalar(fRadius2);
330}
331
332void SkTwoPointRadialGradient::init() {
333 fDiff = fCenter1 - fCenter2;
334 fDiffRadius = fRadius2 - fRadius1;
335 // hack to avoid zero-divide for now
336 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
337 fDiff.fX = SkScalarMul(fDiff.fX, inv);
338 fDiff.fY = SkScalarMul(fDiff.fY, inv);
339 fStartRadius = SkScalarMul(fRadius1, inv);
340 fSr2D2 = SkScalarSquare(fStartRadius);
341 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
342 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
343
344 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
345 fPtsToUnit.postScale(inv, inv);
346}
347
rileya@google.comd7cc6512012-07-27 14:00:39 +0000348/////////////////////////////////////////////////////////////////////
349
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000350#if SK_SUPPORT_GPU
351
rileya@google.comd7cc6512012-07-27 14:00:39 +0000352// For brevity
353typedef GrGLUniformManager::UniformHandle UniformHandle;
354static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
355
356class GrGLRadial2Gradient : public GrGLGradientStage {
357
358public:
359
360 GrGLRadial2Gradient(const GrProgramStageFactory& factory,
361 const GrCustomStage&);
362 virtual ~GrGLRadial2Gradient() { }
363
364 virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
365 virtual void emitVS(GrGLShaderBuilder* builder,
366 const char* vertexCoords) SK_OVERRIDE;
367 virtual void emitFS(GrGLShaderBuilder* builder,
368 const char* outputColor,
369 const char* inputColor,
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000370 const TextureSamplerArray&) SK_OVERRIDE;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000371 virtual void setData(const GrGLUniformManager&,
372 const GrCustomStage&,
373 const GrRenderTarget*,
374 int stageNum) SK_OVERRIDE;
375
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000376 static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000377
378protected:
379
380 UniformHandle fVSParamUni;
381 UniformHandle fFSParamUni;
382
383 const char* fVSVaryingName;
384 const char* fFSVaryingName;
385
386 bool fIsDegenerate;
387
388 // @{
389 /// Values last uploaded as uniforms
390
391 GrScalar fCachedCenter;
392 GrScalar fCachedRadius;
393 bool fCachedPosRoot;
394
395 // @}
396
397private:
398
399 typedef GrGLGradientStage INHERITED;
400
401};
402
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000403/////////////////////////////////////////////////////////////////////
404
405class GrRadial2Gradient : public GrGradientEffect {
406public:
407
bsalomon@google.com1ce49fc2012-09-18 14:14:49 +0000408 GrRadial2Gradient(GrContext* ctx, const SkTwoPointRadialGradient& shader, SkShader::TileMode tm)
409 : INHERITED(ctx, shader, tm)
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000410 , fCenterX1(shader.getCenterX1())
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000411 , fRadius0(shader.getStartRadius())
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000412 , fPosRoot(shader.getDiffRadius() < 0) { }
413 virtual ~GrRadial2Gradient() { }
414
415 static const char* Name() { return "Two-Point Radial Gradient"; }
416 virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
417 return GrTProgramStageFactory<GrRadial2Gradient>::getInstance();
418 }
419 virtual bool isEqual(const GrCustomStage& sBase) const SK_OVERRIDE {
420 const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
421 return (INHERITED::isEqual(sBase) &&
422 this->fCenterX1 == s.fCenterX1 &&
423 this->fRadius0 == s.fRadius0 &&
424 this->fPosRoot == s.fPosRoot);
425 }
426
427 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
428 bool isDegenerate() const { return GR_Scalar1 == fCenterX1; }
429 GrScalar center() const { return fCenterX1; }
430 GrScalar radius() const { return fRadius0; }
431 bool isPosRoot() const { return SkToBool(fPosRoot); }
432
433 typedef GrGLRadial2Gradient GLProgramStage;
434
435private:
bsalomon@google.comd4726202012-08-03 14:34:46 +0000436 GR_DECLARE_CUSTOM_STAGE_TEST;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000437
438 // @{
439 // Cache of values - these can change arbitrarily, EXCEPT
440 // we shouldn't change between degenerate and non-degenerate?!
441
442 GrScalar fCenterX1;
443 GrScalar fRadius0;
444 SkBool8 fPosRoot;
445
446 // @}
447
448 typedef GrGradientEffect INHERITED;
449};
450
451/////////////////////////////////////////////////////////////////////
452
bsalomon@google.comd4726202012-08-03 14:34:46 +0000453GR_DEFINE_CUSTOM_STAGE_TEST(GrRadial2Gradient);
454
455GrCustomStage* GrRadial2Gradient::TestCreate(SkRandom* random,
456 GrContext* context,
457 GrTexture**) {
458 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
459 SkScalar radius1 = random->nextUScalar1();
460 SkPoint center2;
461 SkScalar radius2;
462 do {
463 center1.set(random->nextUScalar1(), random->nextUScalar1());
464 radius2 = random->nextUScalar1 ();
465 // There is a bug in two point radial gradients with idenitical radii
466 } while (radius1 == radius2);
467
468 SkColor colors[kMaxRandomGradientColors];
469 SkScalar stopsArray[kMaxRandomGradientColors];
470 SkScalar* stops = stopsArray;
471 SkShader::TileMode tm;
472 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
473 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
474 center2, radius2,
475 colors, stops, colorCount,
476 tm));
477 GrSamplerState sampler;
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000478 shader->asNewCustomStage(context, &sampler);
479 GrAssert(NULL != sampler.getCustomStage());
480 // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
481 sampler.getCustomStage()->ref();
482 return const_cast<GrCustomStage*>(sampler.getCustomStage());
bsalomon@google.comd4726202012-08-03 14:34:46 +0000483}
484
485/////////////////////////////////////////////////////////////////////
486
rileya@google.comd7cc6512012-07-27 14:00:39 +0000487GrGLRadial2Gradient::GrGLRadial2Gradient(
488 const GrProgramStageFactory& factory,
489 const GrCustomStage& baseData)
490 : INHERITED(factory)
491 , fVSParamUni(kInvalidUniformHandle)
492 , fFSParamUni(kInvalidUniformHandle)
493 , fVSVaryingName(NULL)
494 , fFSVaryingName(NULL)
495 , fCachedCenter(GR_ScalarMax)
496 , fCachedRadius(-GR_ScalarMax)
497 , fCachedPosRoot(0) {
498
499 const GrRadial2Gradient& data =
500 static_cast<const GrRadial2Gradient&>(baseData);
501 fIsDegenerate = data.isDegenerate();
502}
503
504void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) {
rileya@google.comb3e50f22012-08-20 17:43:08 +0000505 INHERITED::setupVariables(builder);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000506 // 2 copies of uniform array, 1 for each of vertex & fragment shader,
507 // to work around Xoom bug. Doesn't seem to cause performance decrease
508 // in test apps, but need to keep an eye on it.
509 fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
510 kFloat_GrSLType, "Radial2VSParams", 6);
511 fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
512 kFloat_GrSLType, "Radial2FSParams", 6);
513
514 // For radial gradients without perspective we can pass the linear
515 // part of the quadratic as a varying.
bsalomon@google.com34bcb9f2012-08-28 18:20:18 +0000516 if (!builder->defaultTextureMatrixIsPerspective()) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000517 builder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
518 &fVSVaryingName, &fFSVaryingName);
519 }
520}
521
522void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* builder,
523 const char* vertexCoords) {
524 SkString* code = &builder->fVSCode;
525 SkString p2;
526 SkString p3;
527 builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
528 builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
529
530 // For radial gradients without perspective we can pass the linear
531 // part of the quadratic as a varying.
bsalomon@google.com34bcb9f2012-08-28 18:20:18 +0000532 if (!builder->defaultTextureMatrixIsPerspective()) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000533 // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
534 code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
535 fVSVaryingName, p2.c_str(),
536 vertexCoords, p3.c_str());
537 }
538}
539
540void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* builder,
541 const char* outputColor,
542 const char* inputColor,
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000543 const TextureSamplerArray& samplers) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000544 SkString* code = &builder->fFSCode;
545 SkString cName("c");
546 SkString ac4Name("ac4");
547 SkString rootName("root");
548 SkString t;
549 SkString p0;
550 SkString p1;
551 SkString p2;
552 SkString p3;
553 SkString p4;
554 SkString p5;
555 builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
556 builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
557 builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
558 builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
559 builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
560 builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
561
562 // If we we're able to interpolate the linear component,
563 // bVar is the varying; otherwise compute it
564 SkString bVar;
bsalomon@google.com34bcb9f2012-08-28 18:20:18 +0000565 if (!builder->defaultTextureMatrixIsPerspective()) {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000566 bVar = fFSVaryingName;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000567 } else {
rileya@google.comd7cc6512012-07-27 14:00:39 +0000568 bVar = "b";
569 //bVar.appendS32(stageNum);
570 code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
571 bVar.c_str(), p2.c_str(),
bsalomon@google.com34bcb9f2012-08-28 18:20:18 +0000572 builder->defaultTexCoordsName(), p3.c_str());
rileya@google.comd7cc6512012-07-27 14:00:39 +0000573 }
574
575 // c = (x^2)+(y^2) - params[4]
576 code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
bsalomon@google.com34bcb9f2012-08-28 18:20:18 +0000577 cName.c_str(),
578 builder->defaultTexCoordsName(),
579 builder->defaultTexCoordsName(),
rileya@google.comd7cc6512012-07-27 14:00:39 +0000580 p4.c_str());
581
582 // If we aren't degenerate, emit some extra code, and accept a slightly
583 // more complex coord.
584 if (!fIsDegenerate) {
585
586 // ac4 = 4.0 * params[0] * c
587 code->appendf("\tfloat %s = %s * 4.0 * %s;\n",
588 ac4Name.c_str(), p0.c_str(),
589 cName.c_str());
590
591 // root = sqrt(b^2-4ac)
592 // (abs to avoid exception due to fp precision)
593 code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
594 rootName.c_str(), bVar.c_str(), bVar.c_str(),
595 ac4Name.c_str());
596
597 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
598 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
599 rootName.c_str(), p1.c_str());
600 } else {
601 // t is: -c/b
602 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
603 }
604
bsalomon@google.comf06df1b2012-09-06 20:22:31 +0000605 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000606}
607
608void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
609 const GrCustomStage& baseData,
rileya@google.comb3e50f22012-08-20 17:43:08 +0000610 const GrRenderTarget* target,
rileya@google.comd7cc6512012-07-27 14:00:39 +0000611 int stageNum) {
rileya@google.comb3e50f22012-08-20 17:43:08 +0000612 INHERITED::setData(uman, baseData, target, stageNum);
rileya@google.comd7cc6512012-07-27 14:00:39 +0000613 const GrRadial2Gradient& data =
614 static_cast<const GrRadial2Gradient&>(baseData);
615 GrAssert(data.isDegenerate() == fIsDegenerate);
616 GrScalar centerX1 = data.center();
617 GrScalar radius0 = data.radius();
618 if (fCachedCenter != centerX1 ||
619 fCachedRadius != radius0 ||
620 fCachedPosRoot != data.isPosRoot()) {
621
622 GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
623
624 // When we're in the degenerate (linear) case, the second
625 // value will be INF but the program doesn't read it. (We
626 // use the same 6 uniforms even though we don't need them
627 // all in the linear case just to keep the code complexity
628 // down).
629 float values[6] = {
630 GrScalarToFloat(a),
631 1 / (2.f * GrScalarToFloat(a)),
632 GrScalarToFloat(centerX1),
633 GrScalarToFloat(radius0),
634 GrScalarToFloat(GrMul(radius0, radius0)),
635 data.isPosRoot() ? 1.f : -1.f
636 };
637
638 uman.set1fv(fVSParamUni, 0, 6, values);
639 uman.set1fv(fFSParamUni, 0, 6, values);
640 fCachedCenter = centerX1;
641 fCachedRadius = radius0;
642 fCachedPosRoot = data.isPosRoot();
643 }
644}
645
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000646GrCustomStage::StageKey GrGLRadial2Gradient::GenKey(const GrCustomStage& s, const GrGLCaps& caps) {
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000647 return (static_cast<const GrRadial2Gradient&>(s).isDegenerate());
648}
rileya@google.comd7cc6512012-07-27 14:00:39 +0000649
650/////////////////////////////////////////////////////////////////////
651
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000652bool SkTwoPointRadialGradient::asNewCustomStage(GrContext* context,
653 GrSamplerState* sampler) const {
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000654 SkASSERT(NULL != context && NULL != sampler);
655 SkScalar diffLen = fDiff.length();
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000656 SkMatrix matrix;
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000657 if (0 != diffLen) {
658 SkScalar invDiffLen = SkScalarInvert(diffLen);
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000659 matrix.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
660 SkScalarMul(invDiffLen, fDiff.fX));
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000661 } else {
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000662 matrix.reset();
rileya@google.com98e8b6d2012-07-31 20:38:06 +0000663 }
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000664
665 matrix.preConcat(fPtsToUnit);
666
667 SkMatrix localM;
668 if (this->getLocalMatrix(&localM)) {
669 if (!localM.invert(&localM)) {
670 return false;
671 }
672 matrix.preConcat(localM);
673 }
674
675 sampler->setCustomStage(SkNEW_ARGS(GrRadial2Gradient, (context, *this, fTileMode)), matrix)->unref();
676 return true;
rileya@google.comd7cc6512012-07-27 14:00:39 +0000677}
678
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000679#else
680
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000681bool SkTwoPointRadialGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000682 SkDEBUGFAIL("Should not call in GPU-less build");
bsalomon@google.comdfdb7e52012-10-16 15:19:45 +0000683 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000684}
685
686#endif