gfx/skia/trunk/src/effects/gradients/SkTwoPointConicalGradient.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 * Copyright 2012 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "SkTwoPointConicalGradient.h"
michael@0 9
michael@0 10 static int valid_divide(float numer, float denom, float* ratio) {
michael@0 11 SkASSERT(ratio);
michael@0 12 if (0 == denom) {
michael@0 13 return 0;
michael@0 14 }
michael@0 15 *ratio = numer / denom;
michael@0 16 return 1;
michael@0 17 }
michael@0 18
michael@0 19 // Return the number of distinct real roots, and write them into roots[] in
michael@0 20 // ascending order
michael@0 21 static int find_quad_roots(float A, float B, float C, float roots[2]) {
michael@0 22 SkASSERT(roots);
michael@0 23
michael@0 24 if (A == 0) {
michael@0 25 return valid_divide(-C, B, roots);
michael@0 26 }
michael@0 27
michael@0 28 float R = B*B - 4*A*C;
michael@0 29 if (R < 0) {
michael@0 30 return 0;
michael@0 31 }
michael@0 32 R = sk_float_sqrt(R);
michael@0 33
michael@0 34 #if 1
michael@0 35 float Q = B;
michael@0 36 if (Q < 0) {
michael@0 37 Q -= R;
michael@0 38 } else {
michael@0 39 Q += R;
michael@0 40 }
michael@0 41 #else
michael@0 42 // on 10.6 this was much slower than the above branch :(
michael@0 43 float Q = B + copysignf(R, B);
michael@0 44 #endif
michael@0 45 Q *= -0.5f;
michael@0 46 if (0 == Q) {
michael@0 47 roots[0] = 0;
michael@0 48 return 1;
michael@0 49 }
michael@0 50
michael@0 51 float r0 = Q / A;
michael@0 52 float r1 = C / Q;
michael@0 53 roots[0] = r0 < r1 ? r0 : r1;
michael@0 54 roots[1] = r0 > r1 ? r0 : r1;
michael@0 55 return 2;
michael@0 56 }
michael@0 57
michael@0 58 static float lerp(float x, float dx, float t) {
michael@0 59 return x + t * dx;
michael@0 60 }
michael@0 61
michael@0 62 static float sqr(float x) { return x * x; }
michael@0 63
michael@0 64 void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
michael@0 65 const SkPoint& center1, SkScalar rad1) {
michael@0 66 fCenterX = SkScalarToFloat(center0.fX);
michael@0 67 fCenterY = SkScalarToFloat(center0.fY);
michael@0 68 fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
michael@0 69 fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
michael@0 70 fRadius = SkScalarToFloat(rad0);
michael@0 71 fDRadius = SkScalarToFloat(rad1) - fRadius;
michael@0 72
michael@0 73 fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
michael@0 74 fRadius2 = sqr(fRadius);
michael@0 75 fRDR = fRadius * fDRadius;
michael@0 76 }
michael@0 77
michael@0 78 void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
michael@0 79 fRelX = SkScalarToFloat(fx) - fCenterX;
michael@0 80 fRelY = SkScalarToFloat(fy) - fCenterY;
michael@0 81 fIncX = SkScalarToFloat(dfx);
michael@0 82 fIncY = SkScalarToFloat(dfy);
michael@0 83 fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
michael@0 84 fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
michael@0 85 }
michael@0 86
michael@0 87 SkFixed TwoPtRadial::nextT() {
michael@0 88 float roots[2];
michael@0 89
michael@0 90 float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
michael@0 91 int countRoots = find_quad_roots(fA, fB, C, roots);
michael@0 92
michael@0 93 fRelX += fIncX;
michael@0 94 fRelY += fIncY;
michael@0 95 fB += fDB;
michael@0 96
michael@0 97 if (0 == countRoots) {
michael@0 98 return kDontDrawT;
michael@0 99 }
michael@0 100
michael@0 101 // Prefer the bigger t value if both give a radius(t) > 0
michael@0 102 // find_quad_roots returns the values sorted, so we start with the last
michael@0 103 float t = roots[countRoots - 1];
michael@0 104 float r = lerp(fRadius, fDRadius, t);
michael@0 105 if (r <= 0) {
michael@0 106 t = roots[0]; // might be the same as roots[countRoots-1]
michael@0 107 r = lerp(fRadius, fDRadius, t);
michael@0 108 if (r <= 0) {
michael@0 109 return kDontDrawT;
michael@0 110 }
michael@0 111 }
michael@0 112 return SkFloatToFixed(t);
michael@0 113 }
michael@0 114
michael@0 115 typedef void (*TwoPointConicalProc)(TwoPtRadial* rec, SkPMColor* dstC,
michael@0 116 const SkPMColor* cache, int toggle, int count);
michael@0 117
michael@0 118 static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
michael@0 119 const SkPMColor* SK_RESTRICT cache, int toggle,
michael@0 120 int count) {
michael@0 121 for (; count > 0; --count) {
michael@0 122 SkFixed t = rec->nextT();
michael@0 123 if (TwoPtRadial::DontDrawT(t)) {
michael@0 124 *dstC++ = 0;
michael@0 125 } else {
michael@0 126 SkFixed index = SkClampMax(t, 0xFFFF);
michael@0 127 SkASSERT(index <= 0xFFFF);
michael@0 128 *dstC++ = cache[toggle +
michael@0 129 (index >> SkGradientShaderBase::kCache32Shift)];
michael@0 130 }
michael@0 131 toggle = next_dither_toggle(toggle);
michael@0 132 }
michael@0 133 }
michael@0 134
michael@0 135 static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
michael@0 136 const SkPMColor* SK_RESTRICT cache, int toggle,
michael@0 137 int count) {
michael@0 138 for (; count > 0; --count) {
michael@0 139 SkFixed t = rec->nextT();
michael@0 140 if (TwoPtRadial::DontDrawT(t)) {
michael@0 141 *dstC++ = 0;
michael@0 142 } else {
michael@0 143 SkFixed index = repeat_tileproc(t);
michael@0 144 SkASSERT(index <= 0xFFFF);
michael@0 145 *dstC++ = cache[toggle +
michael@0 146 (index >> SkGradientShaderBase::kCache32Shift)];
michael@0 147 }
michael@0 148 toggle = next_dither_toggle(toggle);
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
michael@0 153 const SkPMColor* SK_RESTRICT cache, int toggle,
michael@0 154 int count) {
michael@0 155 for (; count > 0; --count) {
michael@0 156 SkFixed t = rec->nextT();
michael@0 157 if (TwoPtRadial::DontDrawT(t)) {
michael@0 158 *dstC++ = 0;
michael@0 159 } else {
michael@0 160 SkFixed index = mirror_tileproc(t);
michael@0 161 SkASSERT(index <= 0xFFFF);
michael@0 162 *dstC++ = cache[toggle +
michael@0 163 (index >> SkGradientShaderBase::kCache32Shift)];
michael@0 164 }
michael@0 165 toggle = next_dither_toggle(toggle);
michael@0 166 }
michael@0 167 }
michael@0 168
michael@0 169 void SkTwoPointConicalGradient::init() {
michael@0 170 fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
michael@0 171 fPtsToUnit.reset();
michael@0 172 }
michael@0 173
michael@0 174 /////////////////////////////////////////////////////////////////////
michael@0 175
michael@0 176 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
michael@0 177 const SkPoint& start, SkScalar startRadius,
michael@0 178 const SkPoint& end, SkScalar endRadius,
michael@0 179 const Descriptor& desc)
michael@0 180 : SkGradientShaderBase(desc),
michael@0 181 fCenter1(start),
michael@0 182 fCenter2(end),
michael@0 183 fRadius1(startRadius),
michael@0 184 fRadius2(endRadius) {
michael@0 185 // this is degenerate, and should be caught by our caller
michael@0 186 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
michael@0 187 this->init();
michael@0 188 }
michael@0 189
michael@0 190 bool SkTwoPointConicalGradient::isOpaque() const {
michael@0 191 // Because areas outside the cone are left untouched, we cannot treat the
michael@0 192 // shader as opaque even if the gradient itself is opaque.
michael@0 193 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
michael@0 194 return false;
michael@0 195 }
michael@0 196
michael@0 197 void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
michael@0 198 int count) {
michael@0 199 int toggle = init_dither_toggle(x, y);
michael@0 200
michael@0 201 SkASSERT(count > 0);
michael@0 202
michael@0 203 SkPMColor* SK_RESTRICT dstC = dstCParam;
michael@0 204
michael@0 205 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
michael@0 206
michael@0 207 const SkPMColor* SK_RESTRICT cache = this->getCache32();
michael@0 208
michael@0 209 TwoPointConicalProc shadeProc = twopoint_repeat;
michael@0 210 if (SkShader::kClamp_TileMode == fTileMode) {
michael@0 211 shadeProc = twopoint_clamp;
michael@0 212 } else if (SkShader::kMirror_TileMode == fTileMode) {
michael@0 213 shadeProc = twopoint_mirror;
michael@0 214 } else {
michael@0 215 SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
michael@0 216 }
michael@0 217
michael@0 218 if (fDstToIndexClass != kPerspective_MatrixClass) {
michael@0 219 SkPoint srcPt;
michael@0 220 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
michael@0 221 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
michael@0 222 SkScalar dx, fx = srcPt.fX;
michael@0 223 SkScalar dy, fy = srcPt.fY;
michael@0 224
michael@0 225 if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
michael@0 226 SkFixed fixedX, fixedY;
michael@0 227 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), &fixedX, &fixedY);
michael@0 228 dx = SkFixedToScalar(fixedX);
michael@0 229 dy = SkFixedToScalar(fixedY);
michael@0 230 } else {
michael@0 231 SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
michael@0 232 dx = fDstToIndex.getScaleX();
michael@0 233 dy = fDstToIndex.getSkewY();
michael@0 234 }
michael@0 235
michael@0 236 fRec.setup(fx, fy, dx, dy);
michael@0 237 (*shadeProc)(&fRec, dstC, cache, toggle, count);
michael@0 238 } else { // perspective case
michael@0 239 SkScalar dstX = SkIntToScalar(x) + SK_ScalarHalf;
michael@0 240 SkScalar dstY = SkIntToScalar(y) + SK_ScalarHalf;
michael@0 241 for (; count > 0; --count) {
michael@0 242 SkPoint srcPt;
michael@0 243 dstProc(fDstToIndex, dstX, dstY, &srcPt);
michael@0 244 fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
michael@0 245 (*shadeProc)(&fRec, dstC, cache, toggle, 1);
michael@0 246
michael@0 247 dstX += SK_Scalar1;
michael@0 248 toggle = next_dither_toggle(toggle);
michael@0 249 dstC += 1;
michael@0 250 }
michael@0 251 }
michael@0 252 }
michael@0 253
michael@0 254 bool SkTwoPointConicalGradient::setContext(const SkBitmap& device,
michael@0 255 const SkPaint& paint,
michael@0 256 const SkMatrix& matrix) {
michael@0 257 if (!this->INHERITED::setContext(device, paint, matrix)) {
michael@0 258 return false;
michael@0 259 }
michael@0 260
michael@0 261 // we don't have a span16 proc
michael@0 262 fFlags &= ~kHasSpan16_Flag;
michael@0 263
michael@0 264 // in general, we might discard based on computed-radius, so clear
michael@0 265 // this flag (todo: sometimes we can detect that we never discard...)
michael@0 266 fFlags &= ~kOpaqueAlpha_Flag;
michael@0 267
michael@0 268 return true;
michael@0 269 }
michael@0 270
michael@0 271 SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
michael@0 272 SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
michael@0 273 SkPoint diff = fCenter2 - fCenter1;
michael@0 274 SkScalar diffLen = 0;
michael@0 275
michael@0 276 if (bitmap) {
michael@0 277 this->getGradientTableBitmap(bitmap);
michael@0 278 }
michael@0 279 if (matrix) {
michael@0 280 diffLen = diff.length();
michael@0 281 }
michael@0 282 if (matrix) {
michael@0 283 if (diffLen) {
michael@0 284 SkScalar invDiffLen = SkScalarInvert(diffLen);
michael@0 285 // rotate to align circle centers with the x-axis
michael@0 286 matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
michael@0 287 SkScalarMul(invDiffLen, diff.fX));
michael@0 288 } else {
michael@0 289 matrix->reset();
michael@0 290 }
michael@0 291 matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
michael@0 292 }
michael@0 293 if (xy) {
michael@0 294 xy[0] = fTileMode;
michael@0 295 xy[1] = kClamp_TileMode;
michael@0 296 }
michael@0 297 return kTwoPointConical_BitmapType;
michael@0 298 }
michael@0 299
michael@0 300 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
michael@0 301 GradientInfo* info) const {
michael@0 302 if (info) {
michael@0 303 commonAsAGradient(info);
michael@0 304 info->fPoint[0] = fCenter1;
michael@0 305 info->fPoint[1] = fCenter2;
michael@0 306 info->fRadius[0] = fRadius1;
michael@0 307 info->fRadius[1] = fRadius2;
michael@0 308 }
michael@0 309 return kConical_GradientType;
michael@0 310 }
michael@0 311
michael@0 312 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
michael@0 313 SkReadBuffer& buffer)
michael@0 314 : INHERITED(buffer),
michael@0 315 fCenter1(buffer.readPoint()),
michael@0 316 fCenter2(buffer.readPoint()),
michael@0 317 fRadius1(buffer.readScalar()),
michael@0 318 fRadius2(buffer.readScalar()) {
michael@0 319 this->init();
michael@0 320 };
michael@0 321
michael@0 322 void SkTwoPointConicalGradient::flatten(
michael@0 323 SkWriteBuffer& buffer) const {
michael@0 324 this->INHERITED::flatten(buffer);
michael@0 325 buffer.writePoint(fCenter1);
michael@0 326 buffer.writePoint(fCenter2);
michael@0 327 buffer.writeScalar(fRadius1);
michael@0 328 buffer.writeScalar(fRadius2);
michael@0 329 }
michael@0 330
michael@0 331 /////////////////////////////////////////////////////////////////////
michael@0 332
michael@0 333 #if SK_SUPPORT_GPU
michael@0 334
michael@0 335 #include "GrTBackendEffectFactory.h"
michael@0 336
michael@0 337 // For brevity
michael@0 338 typedef GrGLUniformManager::UniformHandle UniformHandle;
michael@0 339
michael@0 340 class GrGLConical2Gradient : public GrGLGradientEffect {
michael@0 341 public:
michael@0 342
michael@0 343 GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&);
michael@0 344 virtual ~GrGLConical2Gradient() { }
michael@0 345
michael@0 346 virtual void emitCode(GrGLShaderBuilder*,
michael@0 347 const GrDrawEffect&,
michael@0 348 EffectKey,
michael@0 349 const char* outputColor,
michael@0 350 const char* inputColor,
michael@0 351 const TransformedCoordsArray&,
michael@0 352 const TextureSamplerArray&) SK_OVERRIDE;
michael@0 353 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
michael@0 354
michael@0 355 static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps);
michael@0 356
michael@0 357 protected:
michael@0 358
michael@0 359 UniformHandle fParamUni;
michael@0 360
michael@0 361 const char* fVSVaryingName;
michael@0 362 const char* fFSVaryingName;
michael@0 363
michael@0 364 bool fIsDegenerate;
michael@0 365
michael@0 366 // @{
michael@0 367 /// Values last uploaded as uniforms
michael@0 368
michael@0 369 SkScalar fCachedCenter;
michael@0 370 SkScalar fCachedRadius;
michael@0 371 SkScalar fCachedDiffRadius;
michael@0 372
michael@0 373 // @}
michael@0 374
michael@0 375 private:
michael@0 376
michael@0 377 typedef GrGLGradientEffect INHERITED;
michael@0 378
michael@0 379 };
michael@0 380
michael@0 381 /////////////////////////////////////////////////////////////////////
michael@0 382
michael@0 383 class GrConical2Gradient : public GrGradientEffect {
michael@0 384 public:
michael@0 385
michael@0 386 static GrEffectRef* Create(GrContext* ctx,
michael@0 387 const SkTwoPointConicalGradient& shader,
michael@0 388 const SkMatrix& matrix,
michael@0 389 SkShader::TileMode tm) {
michael@0 390 AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
michael@0 391 return CreateEffectRef(effect);
michael@0 392 }
michael@0 393
michael@0 394 virtual ~GrConical2Gradient() { }
michael@0 395
michael@0 396 static const char* Name() { return "Two-Point Conical Gradient"; }
michael@0 397 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
michael@0 398 return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
michael@0 399 }
michael@0 400
michael@0 401 // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
michael@0 402 bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
michael@0 403 SkScalar center() const { return fCenterX1; }
michael@0 404 SkScalar diffRadius() const { return fDiffRadius; }
michael@0 405 SkScalar radius() const { return fRadius0; }
michael@0 406
michael@0 407 typedef GrGLConical2Gradient GLEffect;
michael@0 408
michael@0 409 private:
michael@0 410 virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
michael@0 411 const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
michael@0 412 return (INHERITED::onIsEqual(sBase) &&
michael@0 413 this->fCenterX1 == s.fCenterX1 &&
michael@0 414 this->fRadius0 == s.fRadius0 &&
michael@0 415 this->fDiffRadius == s.fDiffRadius);
michael@0 416 }
michael@0 417
michael@0 418 GrConical2Gradient(GrContext* ctx,
michael@0 419 const SkTwoPointConicalGradient& shader,
michael@0 420 const SkMatrix& matrix,
michael@0 421 SkShader::TileMode tm)
michael@0 422 : INHERITED(ctx, shader, matrix, tm)
michael@0 423 , fCenterX1(shader.getCenterX1())
michael@0 424 , fRadius0(shader.getStartRadius())
michael@0 425 , fDiffRadius(shader.getDiffRadius()) {
michael@0 426 // We pass the linear part of the quadratic as a varying.
michael@0 427 // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z)
michael@0 428 fBTransform = this->getCoordTransform();
michael@0 429 SkMatrix& bMatrix = *fBTransform.accessMatrix();
michael@0 430 SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius);
michael@0 431 bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) +
michael@0 432 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0]));
michael@0 433 bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) +
michael@0 434 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1]));
michael@0 435 bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) +
michael@0 436 SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2]));
michael@0 437 this->addCoordTransform(&fBTransform);
michael@0 438 }
michael@0 439
michael@0 440 GR_DECLARE_EFFECT_TEST;
michael@0 441
michael@0 442 // @{
michael@0 443 // Cache of values - these can change arbitrarily, EXCEPT
michael@0 444 // we shouldn't change between degenerate and non-degenerate?!
michael@0 445
michael@0 446 GrCoordTransform fBTransform;
michael@0 447 SkScalar fCenterX1;
michael@0 448 SkScalar fRadius0;
michael@0 449 SkScalar fDiffRadius;
michael@0 450
michael@0 451 // @}
michael@0 452
michael@0 453 typedef GrGradientEffect INHERITED;
michael@0 454 };
michael@0 455
michael@0 456 GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
michael@0 457
michael@0 458 GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random,
michael@0 459 GrContext* context,
michael@0 460 const GrDrawTargetCaps&,
michael@0 461 GrTexture**) {
michael@0 462 SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
michael@0 463 SkScalar radius1 = random->nextUScalar1();
michael@0 464 SkPoint center2;
michael@0 465 SkScalar radius2;
michael@0 466 do {
michael@0 467 center2.set(random->nextUScalar1(), random->nextUScalar1());
michael@0 468 radius2 = random->nextUScalar1 ();
michael@0 469 // If the circles are identical the factory will give us an empty shader.
michael@0 470 } while (radius1 == radius2 && center1 == center2);
michael@0 471
michael@0 472 SkColor colors[kMaxRandomGradientColors];
michael@0 473 SkScalar stopsArray[kMaxRandomGradientColors];
michael@0 474 SkScalar* stops = stopsArray;
michael@0 475 SkShader::TileMode tm;
michael@0 476 int colorCount = RandomGradientParams(random, colors, &stops, &tm);
michael@0 477 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
michael@0 478 center2, radius2,
michael@0 479 colors, stops, colorCount,
michael@0 480 tm));
michael@0 481 SkPaint paint;
michael@0 482 return shader->asNewEffect(context, paint);
michael@0 483 }
michael@0 484
michael@0 485
michael@0 486 /////////////////////////////////////////////////////////////////////
michael@0 487
michael@0 488 GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
michael@0 489 const GrDrawEffect& drawEffect)
michael@0 490 : INHERITED(factory)
michael@0 491 , fVSVaryingName(NULL)
michael@0 492 , fFSVaryingName(NULL)
michael@0 493 , fCachedCenter(SK_ScalarMax)
michael@0 494 , fCachedRadius(-SK_ScalarMax)
michael@0 495 , fCachedDiffRadius(-SK_ScalarMax) {
michael@0 496
michael@0 497 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
michael@0 498 fIsDegenerate = data.isDegenerate();
michael@0 499 }
michael@0 500
michael@0 501 void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
michael@0 502 const GrDrawEffect&,
michael@0 503 EffectKey key,
michael@0 504 const char* outputColor,
michael@0 505 const char* inputColor,
michael@0 506 const TransformedCoordsArray& coords,
michael@0 507 const TextureSamplerArray& samplers) {
michael@0 508 this->emitUniforms(builder, key);
michael@0 509 fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
michael@0 510 kFloat_GrSLType, "Conical2FSParams", 6);
michael@0 511
michael@0 512 SkString cName("c");
michael@0 513 SkString ac4Name("ac4");
michael@0 514 SkString dName("d");
michael@0 515 SkString qName("q");
michael@0 516 SkString r0Name("r0");
michael@0 517 SkString r1Name("r1");
michael@0 518 SkString tName("t");
michael@0 519 SkString p0; // 4a
michael@0 520 SkString p1; // 1/a
michael@0 521 SkString p2; // distance between centers
michael@0 522 SkString p3; // start radius
michael@0 523 SkString p4; // start radius squared
michael@0 524 SkString p5; // difference in radii (r1 - r0)
michael@0 525
michael@0 526 builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0);
michael@0 527 builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1);
michael@0 528 builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2);
michael@0 529 builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3);
michael@0 530 builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4);
michael@0 531 builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5);
michael@0 532
michael@0 533 // We interpolate the linear component in coords[1].
michael@0 534 SkASSERT(coords[0].type() == coords[1].type());
michael@0 535 const char* coords2D;
michael@0 536 SkString bVar;
michael@0 537 if (kVec3f_GrSLType == coords[0].type()) {
michael@0 538 builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n",
michael@0 539 coords[0].c_str(), coords[1].c_str(), coords[0].c_str());
michael@0 540 coords2D = "interpolants.xy";
michael@0 541 bVar = "interpolants.z";
michael@0 542 } else {
michael@0 543 coords2D = coords[0].c_str();
michael@0 544 bVar.printf("%s.x", coords[1].c_str());
michael@0 545 }
michael@0 546
michael@0 547 // output will default to transparent black (we simply won't write anything
michael@0 548 // else to it if invalid, instead of discarding or returning prematurely)
michael@0 549 builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
michael@0 550
michael@0 551 // c = (x^2)+(y^2) - params[4]
michael@0 552 builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n",
michael@0 553 cName.c_str(), coords2D, coords2D, p4.c_str());
michael@0 554
michael@0 555 // Non-degenerate case (quadratic)
michael@0 556 if (!fIsDegenerate) {
michael@0 557
michael@0 558 // ac4 = params[0] * c
michael@0 559 builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
michael@0 560 cName.c_str());
michael@0 561
michael@0 562 // d = b^2 - ac4
michael@0 563 builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
michael@0 564 bVar.c_str(), bVar.c_str(), ac4Name.c_str());
michael@0 565
michael@0 566 // only proceed if discriminant is >= 0
michael@0 567 builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str());
michael@0 568
michael@0 569 // intermediate value we'll use to compute the roots
michael@0 570 // q = -0.5 * (b +/- sqrt(d))
michael@0 571 builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
michael@0 572 " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
michael@0 573 bVar.c_str(), dName.c_str());
michael@0 574
michael@0 575 // compute both roots
michael@0 576 // r0 = q * params[1]
michael@0 577 builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
michael@0 578 qName.c_str(), p1.c_str());
michael@0 579 // r1 = c / q
michael@0 580 builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
michael@0 581 cName.c_str(), qName.c_str());
michael@0 582
michael@0 583 // Note: If there are two roots that both generate radius(t) > 0, the
michael@0 584 // Canvas spec says to choose the larger t.
michael@0 585
michael@0 586 // so we'll look at the larger one first:
michael@0 587 builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
michael@0 588 r0Name.c_str(), r1Name.c_str());
michael@0 589
michael@0 590 // if r(t) > 0, then we're done; t will be our x coordinate
michael@0 591 builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
michael@0 592 p5.c_str(), p3.c_str());
michael@0 593
michael@0 594 builder->fsCodeAppend("\t\t");
michael@0 595 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
michael@0 596
michael@0 597 // otherwise, if r(t) for the larger root was <= 0, try the other root
michael@0 598 builder->fsCodeAppend("\t\t} else {\n");
michael@0 599 builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
michael@0 600 r0Name.c_str(), r1Name.c_str());
michael@0 601
michael@0 602 // if r(t) > 0 for the smaller root, then t will be our x coordinate
michael@0 603 builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
michael@0 604 tName.c_str(), p5.c_str(), p3.c_str());
michael@0 605
michael@0 606 builder->fsCodeAppend("\t\t\t");
michael@0 607 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
michael@0 608
michael@0 609 // end if (r(t) > 0) for smaller root
michael@0 610 builder->fsCodeAppend("\t\t\t}\n");
michael@0 611 // end if (r(t) > 0), else, for larger root
michael@0 612 builder->fsCodeAppend("\t\t}\n");
michael@0 613 // end if (discriminant >= 0)
michael@0 614 builder->fsCodeAppend("\t}\n");
michael@0 615 } else {
michael@0 616
michael@0 617 // linear case: t = -c/b
michael@0 618 builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
michael@0 619 cName.c_str(), bVar.c_str());
michael@0 620
michael@0 621 // if r(t) > 0, then t will be the x coordinate
michael@0 622 builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
michael@0 623 p5.c_str(), p3.c_str());
michael@0 624 builder->fsCodeAppend("\t");
michael@0 625 this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers);
michael@0 626 builder->fsCodeAppend("\t}\n");
michael@0 627 }
michael@0 628 }
michael@0 629
michael@0 630 void GrGLConical2Gradient::setData(const GrGLUniformManager& uman,
michael@0 631 const GrDrawEffect& drawEffect) {
michael@0 632 INHERITED::setData(uman, drawEffect);
michael@0 633 const GrConical2Gradient& data = drawEffect.castEffect<GrConical2Gradient>();
michael@0 634 SkASSERT(data.isDegenerate() == fIsDegenerate);
michael@0 635 SkScalar centerX1 = data.center();
michael@0 636 SkScalar radius0 = data.radius();
michael@0 637 SkScalar diffRadius = data.diffRadius();
michael@0 638
michael@0 639 if (fCachedCenter != centerX1 ||
michael@0 640 fCachedRadius != radius0 ||
michael@0 641 fCachedDiffRadius != diffRadius) {
michael@0 642
michael@0 643 SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
michael@0 644
michael@0 645 // When we're in the degenerate (linear) case, the second
michael@0 646 // value will be INF but the program doesn't read it. (We
michael@0 647 // use the same 6 uniforms even though we don't need them
michael@0 648 // all in the linear case just to keep the code complexity
michael@0 649 // down).
michael@0 650 float values[6] = {
michael@0 651 SkScalarToFloat(a * 4),
michael@0 652 1.f / (SkScalarToFloat(a)),
michael@0 653 SkScalarToFloat(centerX1),
michael@0 654 SkScalarToFloat(radius0),
michael@0 655 SkScalarToFloat(SkScalarMul(radius0, radius0)),
michael@0 656 SkScalarToFloat(diffRadius)
michael@0 657 };
michael@0 658
michael@0 659 uman.set1fv(fParamUni, 6, values);
michael@0 660 fCachedCenter = centerX1;
michael@0 661 fCachedRadius = radius0;
michael@0 662 fCachedDiffRadius = diffRadius;
michael@0 663 }
michael@0 664 }
michael@0 665
michael@0 666 GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect,
michael@0 667 const GrGLCaps&) {
michael@0 668 enum {
michael@0 669 kIsDegenerate = 1 << kBaseKeyBitCnt,
michael@0 670 };
michael@0 671
michael@0 672 EffectKey key = GenBaseGradientKey(drawEffect);
michael@0 673 if (drawEffect.castEffect<GrConical2Gradient>().isDegenerate()) {
michael@0 674 key |= kIsDegenerate;
michael@0 675 }
michael@0 676 return key;
michael@0 677 }
michael@0 678
michael@0 679 /////////////////////////////////////////////////////////////////////
michael@0 680
michael@0 681 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
michael@0 682 SkASSERT(NULL != context);
michael@0 683 SkASSERT(fPtsToUnit.isIdentity());
michael@0 684 // invert the localM, translate to center1, rotate so center2 is on x axis.
michael@0 685 SkMatrix matrix;
michael@0 686 if (!this->getLocalMatrix().invert(&matrix)) {
michael@0 687 return NULL;
michael@0 688 }
michael@0 689 matrix.postTranslate(-fCenter1.fX, -fCenter1.fY);
michael@0 690
michael@0 691 SkPoint diff = fCenter2 - fCenter1;
michael@0 692 SkScalar diffLen = diff.length();
michael@0 693 if (0 != diffLen) {
michael@0 694 SkScalar invDiffLen = SkScalarInvert(diffLen);
michael@0 695 SkMatrix rot;
michael@0 696 rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
michael@0 697 SkScalarMul(invDiffLen, diff.fX));
michael@0 698 matrix.postConcat(rot);
michael@0 699 }
michael@0 700
michael@0 701 return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
michael@0 702 }
michael@0 703
michael@0 704 #else
michael@0 705
michael@0 706 GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const {
michael@0 707 SkDEBUGFAIL("Should not call in GPU-less build");
michael@0 708 return NULL;
michael@0 709 }
michael@0 710
michael@0 711 #endif
michael@0 712
michael@0 713 #ifndef SK_IGNORE_TO_STRING
michael@0 714 void SkTwoPointConicalGradient::toString(SkString* str) const {
michael@0 715 str->append("SkTwoPointConicalGradient: (");
michael@0 716
michael@0 717 str->append("center1: (");
michael@0 718 str->appendScalar(fCenter1.fX);
michael@0 719 str->append(", ");
michael@0 720 str->appendScalar(fCenter1.fY);
michael@0 721 str->append(") radius1: ");
michael@0 722 str->appendScalar(fRadius1);
michael@0 723 str->append(" ");
michael@0 724
michael@0 725 str->append("center2: (");
michael@0 726 str->appendScalar(fCenter2.fX);
michael@0 727 str->append(", ");
michael@0 728 str->appendScalar(fCenter2.fY);
michael@0 729 str->append(") radius2: ");
michael@0 730 str->appendScalar(fRadius2);
michael@0 731 str->append(" ");
michael@0 732
michael@0 733 this->INHERITED::toString(str);
michael@0 734
michael@0 735 str->append(")");
michael@0 736 }
michael@0 737 #endif

mercurial