gfx/skia/trunk/src/effects/gradients/SkTwoPointRadialGradient.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1
michael@0 2 /*
michael@0 3 * Copyright 2012 Google Inc.
michael@0 4 *
michael@0 5 * Use of this source code is governed by a BSD-style license that can be
michael@0 6 * found in the LICENSE file.
michael@0 7 */
michael@0 8
michael@0 9 #include "SkTwoPointRadialGradient.h"
michael@0 10
michael@0 11 /* Two-point radial gradients are specified by two circles, each with a center
michael@0 12 point and radius. The gradient can be considered to be a series of
michael@0 13 concentric circles, with the color interpolated from the start circle
michael@0 14 (at t=0) to the end circle (at t=1).
michael@0 15
michael@0 16 For each point (x, y) in the span, we want to find the
michael@0 17 interpolated circle that intersects that point. The center
michael@0 18 of the desired circle (Cx, Cy) falls at some distance t
michael@0 19 along the line segment between the start point (Sx, Sy) and
michael@0 20 end point (Ex, Ey):
michael@0 21
michael@0 22 Cx = (1 - t) * Sx + t * Ex (0 <= t <= 1)
michael@0 23 Cy = (1 - t) * Sy + t * Ey
michael@0 24
michael@0 25 The radius of the desired circle (r) is also a linear interpolation t
michael@0 26 between the start and end radii (Sr and Er):
michael@0 27
michael@0 28 r = (1 - t) * Sr + t * Er
michael@0 29
michael@0 30 But
michael@0 31
michael@0 32 (x - Cx)^2 + (y - Cy)^2 = r^2
michael@0 33
michael@0 34 so
michael@0 35
michael@0 36 (x - ((1 - t) * Sx + t * Ex))^2
michael@0 37 + (y - ((1 - t) * Sy + t * Ey))^2
michael@0 38 = ((1 - t) * Sr + t * Er)^2
michael@0 39
michael@0 40 Solving for t yields
michael@0 41
michael@0 42 [(Sx - Ex)^2 + (Sy - Ey)^2 - (Er - Sr)^2)] * t^2
michael@0 43 + [2 * (Sx - Ex)(x - Sx) + 2 * (Sy - Ey)(y - Sy) - 2 * (Er - Sr) * Sr] * t
michael@0 44 + [(x - Sx)^2 + (y - Sy)^2 - Sr^2] = 0
michael@0 45
michael@0 46 To simplify, let Dx = Sx - Ex, Dy = Sy - Ey, Dr = Er - Sr, dx = x - Sx, dy = y - Sy
michael@0 47
michael@0 48 [Dx^2 + Dy^2 - Dr^2)] * t^2
michael@0 49 + 2 * [Dx * dx + Dy * dy - Dr * Sr] * t
michael@0 50 + [dx^2 + dy^2 - Sr^2] = 0
michael@0 51
michael@0 52 A quadratic in t. The two roots of the quadratic reflect the two
michael@0 53 possible circles on which the point may fall. Solving for t yields
michael@0 54 the gradient value to use.
michael@0 55
michael@0 56 If a<0, the start circle is entirely contained in the
michael@0 57 end circle, and one of the roots will be <0 or >1 (off the line
michael@0 58 segment). If a>0, the start circle falls at least partially
michael@0 59 outside the end circle (or vice versa), and the gradient
michael@0 60 defines a "tube" where a point may be on one circle (on the
michael@0 61 inside of the tube) or the other (outside of the tube). We choose
michael@0 62 one arbitrarily.
michael@0 63
michael@0 64 In order to keep the math to within the limits of fixed point,
michael@0 65 we divide the entire quadratic by Dr^2, and replace
michael@0 66 (x - Sx)/Dr with x' and (y - Sy)/Dr with y', giving
michael@0 67
michael@0 68 [Dx^2 / Dr^2 + Dy^2 / Dr^2 - 1)] * t^2
michael@0 69 + 2 * [x' * Dx / Dr + y' * Dy / Dr - Sr / Dr] * t
michael@0 70 + [x'^2 + y'^2 - Sr^2/Dr^2] = 0
michael@0 71
michael@0 72 (x' and y' are computed by appending the subtract and scale to the
michael@0 73 fDstToIndex matrix in the constructor).
michael@0 74
michael@0 75 Since the 'A' component of the quadratic is independent of x' and y', it
michael@0 76 is precomputed in the constructor. Since the 'B' component is linear in
michael@0 77 x' and y', if x and y are linear in the span, 'B' can be computed
michael@0 78 incrementally with a simple delta (db below). If it is not (e.g.,
michael@0 79 a perspective projection), it must be computed in the loop.
michael@0 80
michael@0 81 */
michael@0 82
michael@0 83 namespace {
michael@0 84
michael@0 85 inline SkFixed two_point_radial(SkScalar b, SkScalar fx, SkScalar fy,
michael@0 86 SkScalar sr2d2, SkScalar foura,
michael@0 87 SkScalar oneOverTwoA, bool posRoot) {
michael@0 88 SkScalar c = SkScalarSquare(fx) + SkScalarSquare(fy) - sr2d2;
michael@0 89 if (0 == foura) {
michael@0 90 return SkScalarToFixed(SkScalarDiv(-c, b));
michael@0 91 }
michael@0 92
michael@0 93 SkScalar discrim = SkScalarSquare(b) - SkScalarMul(foura, c);
michael@0 94 if (discrim < 0) {
michael@0 95 discrim = -discrim;
michael@0 96 }
michael@0 97 SkScalar rootDiscrim = SkScalarSqrt(discrim);
michael@0 98 SkScalar result;
michael@0 99 if (posRoot) {
michael@0 100 result = SkScalarMul(-b + rootDiscrim, oneOverTwoA);
michael@0 101 } else {
michael@0 102 result = SkScalarMul(-b - rootDiscrim, oneOverTwoA);
michael@0 103 }
michael@0 104 return SkScalarToFixed(result);
michael@0 105 }
michael@0 106
michael@0 107 typedef void (* TwoPointRadialShadeProc)(SkScalar fx, SkScalar dx,
michael@0 108 SkScalar fy, SkScalar dy,
michael@0 109 SkScalar b, SkScalar db,
michael@0 110 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
michael@0 111 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
michael@0 112 int count);
michael@0 113
michael@0 114 void shadeSpan_twopoint_clamp(SkScalar fx, SkScalar dx,
michael@0 115 SkScalar fy, SkScalar dy,
michael@0 116 SkScalar b, SkScalar db,
michael@0 117 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
michael@0 118 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
michael@0 119 int count) {
michael@0 120 for (; count > 0; --count) {
michael@0 121 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
michael@0 122 fOneOverTwoA, posRoot);
michael@0 123 SkFixed index = SkClampMax(t, 0xFFFF);
michael@0 124 SkASSERT(index <= 0xFFFF);
michael@0 125 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
michael@0 126 fx += dx;
michael@0 127 fy += dy;
michael@0 128 b += db;
michael@0 129 }
michael@0 130 }
michael@0 131 void shadeSpan_twopoint_mirror(SkScalar fx, SkScalar dx,
michael@0 132 SkScalar fy, SkScalar dy,
michael@0 133 SkScalar b, SkScalar db,
michael@0 134 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
michael@0 135 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
michael@0 136 int count) {
michael@0 137 for (; count > 0; --count) {
michael@0 138 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
michael@0 139 fOneOverTwoA, posRoot);
michael@0 140 SkFixed index = mirror_tileproc(t);
michael@0 141 SkASSERT(index <= 0xFFFF);
michael@0 142 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
michael@0 143 fx += dx;
michael@0 144 fy += dy;
michael@0 145 b += db;
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 void shadeSpan_twopoint_repeat(SkScalar fx, SkScalar dx,
michael@0 150 SkScalar fy, SkScalar dy,
michael@0 151 SkScalar b, SkScalar db,
michael@0 152 SkScalar fSr2D2, SkScalar foura, SkScalar fOneOverTwoA, bool posRoot,
michael@0 153 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
michael@0 154 int count) {
michael@0 155 for (; count > 0; --count) {
michael@0 156 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
michael@0 157 fOneOverTwoA, posRoot);
michael@0 158 SkFixed index = repeat_tileproc(t);
michael@0 159 SkASSERT(index <= 0xFFFF);
michael@0 160 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
michael@0 161 fx += dx;
michael@0 162 fy += dy;
michael@0 163 b += db;
michael@0 164 }
michael@0 165 }
michael@0 166 }
michael@0 167
michael@0 168 /////////////////////////////////////////////////////////////////////
michael@0 169
michael@0 170 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
michael@0 171 const SkPoint& start, SkScalar startRadius,
michael@0 172 const SkPoint& end, SkScalar endRadius,
michael@0 173 const Descriptor& desc)
michael@0 174 : SkGradientShaderBase(desc),
michael@0 175 fCenter1(start),
michael@0 176 fCenter2(end),
michael@0 177 fRadius1(startRadius),
michael@0 178 fRadius2(endRadius) {
michael@0 179 init();
michael@0 180 }
michael@0 181
michael@0 182 SkShader::BitmapType SkTwoPointRadialGradient::asABitmap(
michael@0 183 SkBitmap* bitmap,
michael@0 184 SkMatrix* matrix,
michael@0 185 SkShader::TileMode* xy) const {
michael@0 186 if (bitmap) {
michael@0 187 this->getGradientTableBitmap(bitmap);
michael@0 188 }
michael@0 189 SkScalar diffL = 0; // just to avoid gcc warning
michael@0 190 if (matrix) {
michael@0 191 diffL = SkScalarSqrt(SkScalarSquare(fDiff.fX) +
michael@0 192 SkScalarSquare(fDiff.fY));
michael@0 193 }
michael@0 194 if (matrix) {
michael@0 195 if (diffL) {
michael@0 196 SkScalar invDiffL = SkScalarInvert(diffL);
michael@0 197 matrix->setSinCos(-SkScalarMul(invDiffL, fDiff.fY),
michael@0 198 SkScalarMul(invDiffL, fDiff.fX));
michael@0 199 } else {
michael@0 200 matrix->reset();
michael@0 201 }
michael@0 202 matrix->preConcat(fPtsToUnit);
michael@0 203 }
michael@0 204 if (xy) {
michael@0 205 xy[0] = fTileMode;
michael@0 206 xy[1] = kClamp_TileMode;
michael@0 207 }
michael@0 208 return kTwoPointRadial_BitmapType;
michael@0 209 }
michael@0 210
michael@0 211 SkShader::GradientType SkTwoPointRadialGradient::asAGradient(
michael@0 212 SkShader::GradientInfo* info) const {
michael@0 213 if (info) {
michael@0 214 commonAsAGradient(info);
michael@0 215 info->fPoint[0] = fCenter1;
michael@0 216 info->fPoint[1] = fCenter2;
michael@0 217 info->fRadius[0] = fRadius1;
michael@0 218 info->fRadius[1] = fRadius2;
michael@0 219 }
michael@0 220 return kRadial2_GradientType;
michael@0 221 }
michael@0 222
michael@0 223 void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
michael@0 224 int count) {
michael@0 225 SkASSERT(count > 0);
michael@0 226
michael@0 227 SkPMColor* SK_RESTRICT dstC = dstCParam;
michael@0 228
michael@0 229 // Zero difference between radii: fill with transparent black.
michael@0 230 if (fDiffRadius == 0) {
michael@0 231 sk_bzero(dstC, count * sizeof(*dstC));
michael@0 232 return;
michael@0 233 }
michael@0 234 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
michael@0 235 TileProc proc = fTileProc;
michael@0 236 const SkPMColor* SK_RESTRICT cache = this->getCache32();
michael@0 237
michael@0 238 SkScalar foura = fA * 4;
michael@0 239 bool posRoot = fDiffRadius < 0;
michael@0 240 if (fDstToIndexClass != kPerspective_MatrixClass) {
michael@0 241 SkPoint srcPt;
michael@0 242 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
michael@0 243 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
michael@0 244 SkScalar dx, fx = srcPt.fX;
michael@0 245 SkScalar dy, fy = srcPt.fY;
michael@0 246
michael@0 247 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
michael@0 248 SkFixed fixedX, fixedY;
michael@0 249 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
michael@0 250 dx = SkFixedToScalar(fixedX);
michael@0 251 dy = SkFixedToScalar(fixedY);
michael@0 252 } else {
michael@0 253 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
michael@0 254 dx = fDstToIndex.getScaleX();
michael@0 255 dy = fDstToIndex.getSkewY();
michael@0 256 }
michael@0 257 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
michael@0 258 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
michael@0 259 SkScalar db = (SkScalarMul(fDiff.fX, dx) +
michael@0 260 SkScalarMul(fDiff.fY, dy)) * 2;
michael@0 261
michael@0 262 TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
michael@0 263 if (SkShader::kClamp_TileMode == fTileMode) {
michael@0 264 shadeProc = shadeSpan_twopoint_clamp;
michael@0 265 } else if (SkShader::kMirror_TileMode == fTileMode) {
michael@0 266 shadeProc = shadeSpan_twopoint_mirror;
michael@0 267 } else {
michael@0 268 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
michael@0 269 }
michael@0 270 (*shadeProc)(fx, dx, fy, dy, b, db,
michael@0 271 fSr2D2, foura, fOneOverTwoA, posRoot,
michael@0 272 dstC, cache, count);
michael@0 273 } else { // perspective case
michael@0 274 SkScalar dstX = SkIntToScalar(x);
michael@0 275 SkScalar dstY = SkIntToScalar(y);
michael@0 276 for (; count > 0; --count) {
michael@0 277 SkPoint srcPt;
michael@0 278 dstProc(fDstToIndex, dstX, dstY, &srcPt);
michael@0 279 SkScalar fx = srcPt.fX;
michael@0 280 SkScalar fy = srcPt.fY;
michael@0 281 SkScalar b = (SkScalarMul(fDiff.fX, fx) +
michael@0 282 SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
michael@0 283 SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
michael@0 284 fOneOverTwoA, posRoot);
michael@0 285 SkFixed index = proc(t);
michael@0 286 SkASSERT(index <= 0xFFFF);
michael@0 287 *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
michael@0 288 dstX += SK_Scalar1;
michael@0 289 }
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
michael@0 294 const SkPaint& paint,
michael@0 295 const SkMatrix& matrix){
michael@0 296 // For now, we might have divided by zero, so detect that
michael@0 297 if (0 == fDiffRadius) {
michael@0 298 return false;
michael@0 299 }
michael@0 300
michael@0 301 if (!this->INHERITED::setContext(device, paint, matrix)) {
michael@0 302 return false;
michael@0 303 }
michael@0 304
michael@0 305 // we don't have a span16 proc
michael@0 306 fFlags &= ~kHasSpan16_Flag;
michael@0 307 return true;
michael@0 308 }
michael@0 309
michael@0 310 #ifndef SK_IGNORE_TO_STRING
michael@0 311 void SkTwoPointRadialGradient::toString(SkString* str) const {
michael@0 312 str->append("SkTwoPointRadialGradient: (");
michael@0 313
michael@0 314 str->append("center1: (");
michael@0 315 str->appendScalar(fCenter1.fX);
michael@0 316 str->append(", ");
michael@0 317 str->appendScalar(fCenter1.fY);
michael@0 318 str->append(") radius1: ");
michael@0 319 str->appendScalar(fRadius1);
michael@0 320 str->append(" ");
michael@0 321
michael@0 322 str->append("center2: (");
michael@0 323 str->appendScalar(fCenter2.fX);
michael@0 324 str->append(", ");
michael@0 325 str->appendScalar(fCenter2.fY);
michael@0 326 str->append(") radius2: ");
michael@0 327 str->appendScalar(fRadius2);
michael@0 328 str->append(" ");
michael@0 329
michael@0 330 this->INHERITED::toString(str);
michael@0 331
michael@0 332 str->append(")");
michael@0 333 }
michael@0 334 #endif
michael@0 335
michael@0 336 SkTwoPointRadialGradient::SkTwoPointRadialGradient(
michael@0 337 SkReadBuffer& buffer)
michael@0 338 : INHERITED(buffer),
michael@0 339 fCenter1(buffer.readPoint()),
michael@0 340 fCenter2(buffer.readPoint()),
michael@0 341 fRadius1(buffer.readScalar()),
michael@0 342 fRadius2(buffer.readScalar()) {
michael@0 343 init();
michael@0 344 };
michael@0 345
michael@0 346 void SkTwoPointRadialGradient::flatten(
michael@0 347 SkWriteBuffer& buffer) const {
michael@0 348 this->INHERITED::flatten(buffer);
michael@0 349 buffer.writePoint(fCenter1);
michael@0 350 buffer.writePoint(fCenter2);
michael@0 351 buffer.writeScalar(fRadius1);
michael@0 352 buffer.writeScalar(fRadius2);
michael@0 353 }
michael@0 354
michael@0 355 void SkTwoPointRadialGradient::init() {
michael@0 356 fDiff = fCenter1 - fCenter2;
michael@0 357 fDiffRadius = fRadius2 - fRadius1;
michael@0 358 // hack to avoid zero-divide for now
michael@0 359 SkScalar inv = fDiffRadius ? SkScalarInvert(fDiffRadius) : 0;
michael@0 360 fDiff.fX = SkScalarMul(fDiff.fX, inv);
michael@0 361 fDiff.fY = SkScalarMul(fDiff.fY, inv);
michael@0 362 fStartRadius = SkScalarMul(fRadius1, inv);
michael@0 363 fSr2D2 = SkScalarSquare(fStartRadius);
michael@0 364 fA = SkScalarSquare(fDiff.fX) + SkScalarSquare(fDiff.fY) - SK_Scalar1;
michael@0 365 fOneOverTwoA = fA ? SkScalarInvert(fA * 2) : 0;
michael@0 366
michael@0 367 fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
michael@0 368 fPtsToUnit.postScale(inv, inv);
michael@0 369 }
michael@0 370
michael@0 371 /////////////////////////////////////////////////////////////////////
michael@0 372
michael@0 373 #if SK_SUPPORT_GPU
michael@0 374
michael@0 375 #include "GrTBackendEffectFactory.h"
michael@0 376
michael@0 377 // For brevity
michael@0 378 typedef GrGLUniformManager::UniformHandle UniformHandle;
michael@0 379
michael@0 380 class GrGLRadial2Gradient : public GrGLGradientEffect {
michael@0 381
michael@0 382 public:
michael@0 383
michael@0 384 GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
michael@0 385 virtual ~GrGLRadial2Gradient() { }
michael@0 386
michael@0 387 virtual void emitCode(GrGLShaderBuilder*,
michael@0 388 const GrDrawEffect&,
michael@0 389 EffectKey,
michael@0 390 const char* outputColor,
michael@0 391 const char* inputColor,
michael@0 392 const TransformedCoordsArray&,
michael@0 393 const TextureSamplerArray&) SK_OVERRIDE;
michael@0 394 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
michael@0 395
michael@0 396 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
michael@0 397
michael@0 398 protected:
michael@0 399
michael@0 400 UniformHandle fParamUni;
michael@0 401
michael@0 402 const char* fVSVaryingName;
michael@0 403 const char* fFSVaryingName;
michael@0 404
michael@0 405 bool fIsDegenerate;
michael@0 406
michael@0 407 // @{
michael@0 408 /// Values last uploaded as uniforms
michael@0 409
michael@0 410 SkScalar fCachedCenter;
michael@0 411 SkScalar fCachedRadius;
michael@0 412 bool fCachedPosRoot;
michael@0 413
michael@0 414 // @}
michael@0 415
michael@0 416 private:
michael@0 417
michael@0 418 typedef GrGLGradientEffect INHERITED;
michael@0 419
michael@0 420 };
michael@0 421
michael@0 422 /////////////////////////////////////////////////////////////////////
michael@0 423
michael@0 424 class GrRadial2Gradient : public GrGradientEffect {
michael@0 425 public:
michael@0 426 static GrEffectRef* Create(GrContext* ctx,
michael@0 427 const SkTwoPointRadialGradient& shader,
michael@0 428 const SkMatrix& matrix,
michael@0 429 SkShader::TileMode tm) {
michael@0 430 AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
michael@0 431 return CreateEffectRef(effect);
michael@0 432 }
michael@0 433
michael@0 434 virtual ~GrRadial2Gradient() { }
michael@0 435
michael@0 436 static const char* Name() { return "Two-Point Radial Gradient"; }
michael@0 437 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
michael@0 438 return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
michael@0 439 }
michael@0 440
michael@0 441 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
michael@0 442 bool isDegenerate() const { return SK_Scalar1 == fCenterX1; }
michael@0 443 SkScalar center() const { return fCenterX1; }
michael@0 444 SkScalar radius() const { return fRadius0; }
michael@0 445 bool isPosRoot() const { return SkToBool(fPosRoot); }
michael@0 446
michael@0 447 typedef GrGLRadial2Gradient GLEffect;
michael@0 448
michael@0 449 private:
michael@0 450 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
michael@0 451 const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
michael@0 452 return (INHERITED::onIsEqual(sBase) &&
michael@0 453 this->fCenterX1 == s.fCenterX1 &&
michael@0 454 this->fRadius0 == s.fRadius0 &&
michael@0 455 this->fPosRoot == s.fPosRoot);
michael@0 456 }
michael@0 457
michael@0 458 GrRadial2Gradient(GrContext* ctx,
michael@0 459 const SkTwoPointRadialGradient& shader,
michael@0 460 const SkMatrix& matrix,
michael@0 461 SkShader::TileMode tm)
michael@0 462 : INHERITED(ctx, shader, matrix, tm)
michael@0 463 , fCenterX1(shader.getCenterX1())
michael@0 464 , fRadius0(shader.getStartRadius())
michael@0 465 , fPosRoot(shader.getDiffRadius() < 0) {
michael@0 466 // We pass the linear part of the quadratic as a varying.
michael@0 467 // float b = 2.0 * (fCenterX1 * x - fRadius0 * z)
michael@0 468 fBTransform = this->getCoordTransform();
michael@0 469 SkMatrix& bMatrix = *fBTransform.accessMatrix();
michael@0 470 bMatrix[SkMatrix::kMScaleX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) -
michael@0 471 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp0]));
michael@0 472 bMatrix[SkMatrix::kMSkewX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) -
michael@0 473 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp1]));
michael@0 474 bMatrix[SkMatrix::kMTransX] = 2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) -
michael@0 475 SkScalarMul(fRadius0, bMatrix[SkMatrix::kMPersp2]));
michael@0 476 this->addCoordTransform(&fBTransform);
michael@0 477 }
michael@0 478
michael@0 479 GR_DECLARE_EFFECT_TEST;
michael@0 480
michael@0 481 // @{
michael@0 482 // Cache of values - these can change arbitrarily, EXCEPT
michael@0 483 // we shouldn't change between degenerate and non-degenerate?!
michael@0 484
michael@0 485 GrCoordTransform fBTransform;
michael@0 486 SkScalar fCenterX1;
michael@0 487 SkScalar fRadius0;
michael@0 488 SkBool8 fPosRoot;
michael@0 489
michael@0 490 // @}
michael@0 491
michael@0 492 typedef GrGradientEffect INHERITED;
michael@0 493 };
michael@0 494
michael@0 495 /////////////////////////////////////////////////////////////////////
michael@0 496
michael@0 497 GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
michael@0 498
michael@0 499 GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
michael@0 500 GrContext* context,
michael@0 501 const GrDrawTargetCaps&,
michael@0 502 GrTexture**) {
michael@0 503 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
michael@0 504 SkScalar radius1 = random->nextUScalar1();
michael@0 505 SkPoint center2;
michael@0 506 SkScalar radius2;
michael@0 507 do {
michael@0 508 center2.set(random->nextUScalar1(), random->nextUScalar1());
michael@0 509 radius2 = random->nextUScalar1 ();
michael@0 510 // There is a bug in two point radial gradients with identical radii
michael@0 511 } while (radius1 == radius2);
michael@0 512
michael@0 513 SkColor colors[kMaxRandomGradientColors];
michael@0 514 SkScalar stopsArray[kMaxRandomGradientColors];
michael@0 515 SkScalar* stops = stopsArray;
michael@0 516 SkShader::TileMode tm;
michael@0 517 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
michael@0 518 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointRadial(center1, radius1,
michael@0 519 center2, radius2,
michael@0 520 colors, stops, colorCount,
michael@0 521 tm));
michael@0 522 SkPaint paint;
michael@0 523 return shader->asNewEffect(context, paint);
michael@0 524 }
michael@0 525
michael@0 526 /////////////////////////////////////////////////////////////////////
michael@0 527
michael@0 528 GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
michael@0 529 const GrDrawEffect& drawEffect)
michael@0 530 : INHERITED(factory)
michael@0 531 , fVSVaryingName(NULL)
michael@0 532 , fFSVaryingName(NULL)
michael@0 533 , fCachedCenter(SK_ScalarMax)
michael@0 534 , fCachedRadius(-SK_ScalarMax)
michael@0 535 , fCachedPosRoot(0) {
michael@0 536
michael@0 537 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
michael@0 538 fIsDegenerate = data.isDegenerate();
michael@0 539 }
michael@0 540
michael@0 541 void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
michael@0 542 const GrDrawEffect& drawEffect,
michael@0 543 EffectKey key,
michael@0 544 const char* outputColor,
michael@0 545 const char* inputColor,
michael@0 546 const TransformedCoordsArray& coords,
michael@0 547 const TextureSamplerArray& samplers) {
michael@0 548
michael@0 549 this->emitUniforms(builder, key);
michael@0 550 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
michael@0 551 kFloat_GrSLType, "Radial2FSParams", 6);
michael@0 552
michael@0 553 SkString cName("c");
michael@0 554 SkString ac4Name("ac4");
michael@0 555 SkString rootName("root");
michael@0 556 SkString t;
michael@0 557 SkString p0;
michael@0 558 SkString p1;
michael@0 559 SkString p2;
michael@0 560 SkString p3;
michael@0 561 SkString p4;
michael@0 562 SkString p5;
michael@0 563 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
michael@0 564 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
michael@0 565 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
michael@0 566 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
michael@0 567 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
michael@0 568 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
michael@0 569
michael@0 570 // We interpolate the linear component in coords[1].
michael@0 571 SkASSERT(coords[0].type() == coords[1].type());
michael@0 572 const char* coords2D;
michael@0 573 SkString bVar;
michael@0 574 if (kVec3f_GrSLType == coords[0].type()) {
michael@0 575 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
michael@0 576 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
michael@0 577 coords2D = "interpolants.xy";
michael@0 578 bVar = "interpolants.z";
michael@0 579 } else {
michael@0 580 coords2D = coords[0].c_str();
michael@0 581 bVar.printf("%s.x", coords[1].c_str());
michael@0 582 }
michael@0 583
michael@0 584 // c = (x^2)+(y^2) - params[4]
michael@0 585 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
michael@0 586 cName.c_str(), coords2D, coords2D, p4.c_str());
michael@0 587
michael@0 588 // If we aren't degenerate, emit some extra code, and accept a slightly
michael@0 589 // more complex coord.
michael@0 590 if (!fIsDegenerate) {
michael@0 591
michael@0 592 // ac4 = 4.0 * params[0] * c
michael@0 593 builder->fsCodeAppendf("\tfloat %s = %s * 4.0 * %s;\n",
michael@0 594 ac4Name.c_str(), p0.c_str(),
michael@0 595 cName.c_str());
michael@0 596
michael@0 597 // root = sqrt(b^2-4ac)
michael@0 598 // (abs to avoid exception due to fp precision)
michael@0 599 builder->fsCodeAppendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
michael@0 600 rootName.c_str(), bVar.c_str(), bVar.c_str(),
michael@0 601 ac4Name.c_str());
michael@0 602
michael@0 603 // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
michael@0 604 t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(),
michael@0 605 rootName.c_str(), p1.c_str());
michael@0 606 } else {
michael@0 607 // t is: -c/b
michael@0 608 t.printf("-%s / %s", cName.c_str(), bVar.c_str());
michael@0 609 }
michael@0 610
michael@0 611 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers);
michael@0 612 }
michael@0 613
michael@0 614 void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman,
michael@0 615 const GrDrawEffect& drawEffect) {
michael@0 616 INHERITED::setData(uman, drawEffect);
michael@0 617 const GrRadial2Gradient& data = drawEffect.castEffect<GrRadial2Gradient>();
michael@0 618 SkASSERT(data.isDegenerate() == fIsDegenerate);
michael@0 619 SkScalar centerX1 = data.center();
michael@0 620 SkScalar radius0 = data.radius();
michael@0 621 if (fCachedCenter != centerX1 ||
michael@0 622 fCachedRadius != radius0 ||
michael@0 623 fCachedPosRoot != data.isPosRoot()) {
michael@0 624
michael@0 625 SkScalar a = SkScalarMul(centerX1, centerX1) - SK_Scalar1;
michael@0 626
michael@0 627 // When we're in the degenerate (linear) case, the second
michael@0 628 // value will be INF but the program doesn't read it. (We
michael@0 629 // use the same 6 uniforms even though we don't need them
michael@0 630 // all in the linear case just to keep the code complexity
michael@0 631 // down).
michael@0 632 float values[6] = {
michael@0 633 SkScalarToFloat(a),
michael@0 634 1 / (2.f * SkScalarToFloat(a)),
michael@0 635 SkScalarToFloat(centerX1),
michael@0 636 SkScalarToFloat(radius0),
michael@0 637 SkScalarToFloat(SkScalarMul(radius0, radius0)),
michael@0 638 data.isPosRoot() ? 1.f : -1.f
michael@0 639 };
michael@0 640
michael@0 641 uman.set1fv(fParamUni, 6, values);
michael@0 642 fCachedCenter = centerX1;
michael@0 643 fCachedRadius = radius0;
michael@0 644 fCachedPosRoot = data.isPosRoot();
michael@0 645 }
michael@0 646 }
michael@0 647
michael@0 648 GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrDrawEffect& drawEffect,
michael@0 649 const GrGLCaps&) {
michael@0 650 enum {
michael@0 651 kIsDegenerate = 1 << kBaseKeyBitCnt,
michael@0 652 };
michael@0 653
michael@0 654 EffectKey key = GenBaseGradientKey(drawEffect);
michael@0 655 if (drawEffect.castEffect<GrRadial2Gradient>().isDegenerate()) {
michael@0 656 key |= kIsDegenerate;
michael@0 657 }
michael@0 658 return key;
michael@0 659 }
michael@0 660
michael@0 661 /////////////////////////////////////////////////////////////////////
michael@0 662
michael@0 663 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
michael@0 664 SkASSERT(NULL != context);
michael@0 665 // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
michael@0 666 SkMatrix matrix;
michael@0 667 if (!this->getLocalMatrix().invert(&matrix)) {
michael@0 668 return NULL;
michael@0 669 }
michael@0 670 matrix.postConcat(fPtsToUnit);
michael@0 671
michael@0 672 SkScalar diffLen = fDiff.length();
michael@0 673 if (0 != diffLen) {
michael@0 674 SkScalar invDiffLen = SkScalarInvert(diffLen);
michael@0 675 SkMatrix rot;
michael@0 676 rot.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
michael@0 677 SkScalarMul(invDiffLen, fDiff.fX));
michael@0 678 matrix.postConcat(rot);
michael@0 679 }
michael@0 680
michael@0 681 return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
michael@0 682 }
michael@0 683
michael@0 684 #else
michael@0 685
michael@0 686 GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
michael@0 687 SkDEBUGFAIL("Should not call in GPU-less build");
michael@0 688 return NULL;
michael@0 689 }
michael@0 690
michael@0 691 #endif

mercurial