Chromium Code Reviews| Index: src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp |
| diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp |
| index 7cdb62dc44c516154b24dabacb83c4cddb77172f..6d478f5c41425ce9caea54df589657cd006e1e3f 100644 |
| --- a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp |
| +++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp |
| @@ -1,3 +1,4 @@ |
| + |
| /* |
| * Copyright 2014 Google Inc. |
| * |
| @@ -14,10 +15,18 @@ |
| // For brevity |
| typedef GrGLUniformManager::UniformHandle UniformHandle; |
| +static const SkScalar kErrorTol = 0.00001; |
| + |
| +enum ConicalType { |
|
bsalomon
2014/04/21 18:54:58
comment here... what are inside, outside, and edge
|
| + kInside_ConicalType, |
| + kOutside_ConicalType, |
| + kEdge_ConicalType, |
| +}; |
| + |
| ////////////////////////////////////////////////////////////////////////////// |
| -static void set_matrix_default_conical(const SkTwoPointConicalGradient& shader, |
| - SkMatrix* invLMatrix) { |
| +static void set_matrix_edge_conical(const SkTwoPointConicalGradient& shader, |
| + SkMatrix* invLMatrix) { |
| // Inverse of the current local matrix is passed in then, |
| // translate to center1, rotate so center2 is on x axis. |
| const SkPoint& center1 = shader.getStartCenter(); |
| @@ -36,52 +45,51 @@ static void set_matrix_default_conical(const SkTwoPointConicalGradient& shader, |
| } |
| } |
| -class GLDefault2PtConicalEffect; |
| +class GLEdge2PtConicalEffect; |
| -class Default2PtConicalEffect : public GrGradientEffect { |
| +class Edge2PtConicalEffect : public GrGradientEffect { |
| public: |
| static GrEffectRef* Create(GrContext* ctx, |
| const SkTwoPointConicalGradient& shader, |
| const SkMatrix& matrix, |
| SkShader::TileMode tm) { |
| - AutoEffectUnref effect(SkNEW_ARGS(Default2PtConicalEffect, (ctx, shader, matrix, tm))); |
| + AutoEffectUnref effect(SkNEW_ARGS(Edge2PtConicalEffect, (ctx, shader, matrix, tm))); |
| return CreateEffectRef(effect); |
| } |
| - virtual ~Default2PtConicalEffect() { } |
| + virtual ~Edge2PtConicalEffect() {} |
| - static const char* Name() { return "Two-Point Conical Gradient"; } |
| + static const char* Name() { return "Two-Point Conical Gradient Edge Touching"; } |
| virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. |
| - bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); } |
| - bool isFlipped() const { return fIsFlipped; } |
| SkScalar center() const { return fCenterX1; } |
| SkScalar diffRadius() const { return fDiffRadius; } |
| SkScalar radius() const { return fRadius0; } |
| - typedef GLDefault2PtConicalEffect GLEffect; |
| + typedef GLEdge2PtConicalEffect GLEffect; |
| private: |
| virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { |
| - const Default2PtConicalEffect& s = CastEffect<Default2PtConicalEffect>(sBase); |
| + const Edge2PtConicalEffect& s = CastEffect<Edge2PtConicalEffect>(sBase); |
| return (INHERITED::onIsEqual(sBase) && |
| this->fCenterX1 == s.fCenterX1 && |
| this->fRadius0 == s.fRadius0 && |
| - this->fDiffRadius == s.fDiffRadius && |
| - this->fIsFlipped == s.fIsFlipped); |
| + this->fDiffRadius == s.fDiffRadius); |
| } |
| - Default2PtConicalEffect(GrContext* ctx, |
| - const SkTwoPointConicalGradient& shader, |
| - const SkMatrix& matrix, |
| - SkShader::TileMode tm) |
| + Edge2PtConicalEffect(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm) |
| : INHERITED(ctx, shader, matrix, tm), |
| fCenterX1(shader.getCenterX1()), |
| fRadius0(shader.getStartRadius()), |
| - fDiffRadius(shader.getDiffRadius()), |
| - fIsFlipped(shader.isFlippedGrad()) { |
| + fDiffRadius(shader.getDiffRadius()){ |
| + // We should only be calling this shader if we are degenerate case with touching circles |
| + SkASSERT(SkScalarAbs(fDiffRadius) - SkScalarAbs(fCenterX1) < kErrorTol) ; |
| + |
| // We pass the linear part of the quadratic as a varying. |
| // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) |
| fBTransform = this->getCoordTransform(); |
| @@ -106,17 +114,16 @@ private: |
| SkScalar fCenterX1; |
| SkScalar fRadius0; |
| SkScalar fDiffRadius; |
| - bool fIsFlipped; |
| // @} |
| typedef GrGradientEffect INHERITED; |
| }; |
| -class GLDefault2PtConicalEffect : public GrGLGradientEffect { |
| +class GLEdge2PtConicalEffect : public GrGLGradientEffect { |
| public: |
| - GLDefault2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| - virtual ~GLDefault2PtConicalEffect() { } |
| + GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| + virtual ~GLEdge2PtConicalEffect() { } |
| virtual void emitCode(GrGLShaderBuilder*, |
| const GrDrawEffect&, |
| @@ -135,13 +142,9 @@ protected: |
| const char* fVSVaryingName; |
| const char* fFSVaryingName; |
| - bool fIsDegenerate; |
| - bool fIsFlipped; |
| - |
| // @{ |
| /// Values last uploaded as uniforms |
| - SkScalar fCachedCenter; |
| SkScalar fCachedRadius; |
| SkScalar fCachedDiffRadius; |
| @@ -152,23 +155,27 @@ private: |
| }; |
| -const GrBackendEffectFactory& Default2PtConicalEffect::getFactory() const { |
| - return GrTBackendEffectFactory<Default2PtConicalEffect>::getInstance(); |
| +const GrBackendEffectFactory& Edge2PtConicalEffect::getFactory() const { |
| + return GrTBackendEffectFactory<Edge2PtConicalEffect>::getInstance(); |
| } |
| -GR_DEFINE_EFFECT_TEST(Default2PtConicalEffect); |
| +GR_DEFINE_EFFECT_TEST(Edge2PtConicalEffect); |
| -GrEffectRef* Default2PtConicalEffect::TestCreate(SkRandom* random, |
| - GrContext* context, |
| - const GrDrawTargetCaps&, |
| - GrTexture**) { |
| +GrEffectRef* Edge2PtConicalEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps&, |
| + GrTexture**) { |
| SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| SkScalar radius1 = random->nextUScalar1(); |
| SkPoint center2; |
| SkScalar radius2; |
| do { |
| center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| - radius2 = random->nextUScalar1 (); |
| + // Below makes sure that circle one is contained within circle two |
| + // and both circles are touching on an edge |
| + SkPoint diff = center2 - center1; |
| + SkScalar diffLen = diff.length(); |
| + radius2 = radius1 + diffLen; |
| // If the circles are identical the factory will give us an empty shader. |
|
bsalomon
2014/04/21 18:54:58
maybe take this comment out since it isn't the pri
|
| } while (radius1 == radius2 && center1 == center2); |
| @@ -185,62 +192,42 @@ GrEffectRef* Default2PtConicalEffect::TestCreate(SkRandom* random, |
| return shader->asNewEffect(context, paint); |
| } |
| - |
| -///////////////////////////////////////////////////////////////////// |
| - |
| -GLDefault2PtConicalEffect::GLDefault2PtConicalEffect(const GrBackendEffectFactory& factory, |
| - const GrDrawEffect& drawEffect) |
| +GLEdge2PtConicalEffect::GLEdge2PtConicalEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| : INHERITED(factory) |
| , fVSVaryingName(NULL) |
| , fFSVaryingName(NULL) |
| - , fCachedCenter(SK_ScalarMax) |
| , fCachedRadius(-SK_ScalarMax) |
| - , fCachedDiffRadius(-SK_ScalarMax) { |
| - |
| - const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>(); |
| - fIsDegenerate = data.isDegenerate(); |
| - fIsFlipped = data.isFlipped(); |
| -} |
| - |
| -void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| - const GrDrawEffect&, |
| - EffectKey key, |
| - const char* outputColor, |
| - const char* inputColor, |
| - const TransformedCoordsArray& coords, |
| - const TextureSamplerArray& samplers) { |
| + , fCachedDiffRadius(-SK_ScalarMax) {} |
| + |
| +void GLEdge2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| this->emitUniforms(builder, key); |
| fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, |
| - kFloat_GrSLType, "Conical2FSParams", 6); |
| + kFloat_GrSLType, "Conical2FSParams", 3); |
| SkString cName("c"); |
| - SkString ac4Name("ac4"); |
| - SkString dName("d"); |
| - SkString qName("q"); |
| - SkString r0Name("r0"); |
| - SkString r1Name("r1"); |
| SkString tName("t"); |
| - SkString p0; // 4a |
| - SkString p1; // 1/a |
| - SkString p2; // distance between centers |
| - SkString p3; // start radius |
| - SkString p4; // start radius squared |
| - SkString p5; // difference in radii (r1 - r0) |
| + SkString p0; // start radius |
| + SkString p1; // start radius squared |
| + SkString p2; // difference in radii (r1 - r0) |
| builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); |
| builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); |
| builder->getUniformVariable(fParamUni).appendArrayAccess(2, &p2); |
| - builder->getUniformVariable(fParamUni).appendArrayAccess(3, &p3); |
| - builder->getUniformVariable(fParamUni).appendArrayAccess(4, &p4); |
| - builder->getUniformVariable(fParamUni).appendArrayAccess(5, &p5); |
| // We interpolate the linear component in coords[1]. |
| SkASSERT(coords[0].type() == coords[1].type()); |
| const char* coords2D; |
| SkString bVar; |
| if (kVec3f_GrSLType == coords[0].type()) { |
|
bsalomon
2014/04/21 18:54:58
There's a helper on GrGLShaderBuilder called ensur
|
| - builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", |
| - coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); |
| + builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy / %s.z, %s.x / %s.z);\n", |
| + coords[0].c_str(), coords[0].c_str(), coords[1].c_str(), coords[1].c_str()); |
| coords2D = "interpolants.xy"; |
| bVar = "interpolants.z"; |
| } else { |
| @@ -252,144 +239,327 @@ void GLDefault2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| // else to it if invalid, instead of discarding or returning prematurely) |
| builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
| - // c = (x^2)+(y^2) - params[4] |
| + // c = (x^2)+(y^2) - params[1] |
| builder->fsCodeAppendf("\tfloat %s = dot(%s, %s) - %s;\n", |
| - cName.c_str(), coords2D, coords2D, p4.c_str()); |
| - |
| - // Non-degenerate case (quadratic) |
| - if (!fIsDegenerate) { |
| - |
| - // ac4 = params[0] * c |
| - builder->fsCodeAppendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(), |
| - cName.c_str()); |
| - |
| - // d = b^2 - ac4 |
| - builder->fsCodeAppendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(), |
| - bVar.c_str(), bVar.c_str(), ac4Name.c_str()); |
| - |
| - // only proceed if discriminant is >= 0 |
| - builder->fsCodeAppendf("\tif (%s >= 0.0) {\n", dName.c_str()); |
| - |
| - // intermediate value we'll use to compute the roots |
| - // q = -0.5 * (b +/- sqrt(d)) |
| - builder->fsCodeAppendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)" |
| - " * sqrt(%s));\n", qName.c_str(), bVar.c_str(), |
| - bVar.c_str(), dName.c_str()); |
| - |
| - // compute both roots |
| - // r0 = q * params[1] |
| - builder->fsCodeAppendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(), |
| - qName.c_str(), p1.c_str()); |
| - // r1 = c / q |
| - builder->fsCodeAppendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(), |
| - cName.c_str(), qName.c_str()); |
| - |
| - // Note: If there are two roots that both generate radius(t) > 0, the |
| - // Canvas spec says to choose the larger t. |
| - |
| - // so we'll look at the larger one first (or smaller if flipped): |
| - if (!fIsFlipped) { |
| - builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), |
| - r0Name.c_str(), r1Name.c_str()); |
| - } else { |
| - builder->fsCodeAppendf("\t\tfloat %s = min(%s, %s);\n", tName.c_str(), |
| - r0Name.c_str(), r1Name.c_str()); |
| - } |
| + cName.c_str(), coords2D, coords2D, p1.c_str()); |
| + |
| + // linear case: t = -c/b |
| + builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), |
| + cName.c_str(), bVar.c_str()); |
| + |
| + // if r(t) > 0, then t will be the x coordinate |
| + builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), |
| + p2.c_str(), p0.c_str()); |
| + builder->fsCodeAppend("\t"); |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->fsCodeAppend("\t}\n"); |
| +} |
| - // if r(t) > 0, then we're done; t will be our x coordinate |
| - builder->fsCodeAppendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), |
| - p5.c_str(), p3.c_str()); |
| +void GLEdge2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| + INHERITED::setData(uman, drawEffect); |
| + const Edge2PtConicalEffect& data = drawEffect.castEffect<Edge2PtConicalEffect>(); |
| + SkScalar radius0 = data.radius(); |
| + SkScalar diffRadius = data.diffRadius(); |
| - builder->fsCodeAppend("\t\t"); |
| - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + if (fCachedRadius != radius0 || |
| + fCachedDiffRadius != diffRadius) { |
| - // otherwise, if r(t) for the larger root was <= 0, try the other root |
| - builder->fsCodeAppend("\t\t} else {\n"); |
| - if (!fIsFlipped) { |
| - builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), |
| - r0Name.c_str(), r1Name.c_str()); |
| - } else { |
| - builder->fsCodeAppendf("\t\t\t%s = max(%s, %s);\n", tName.c_str(), |
| - r0Name.c_str(), r1Name.c_str()); |
| - } |
| + float values[3] = { |
| + SkScalarToFloat(radius0), |
| + SkScalarToFloat(SkScalarMul(radius0, radius0)), |
| + SkScalarToFloat(diffRadius) |
| + }; |
| + |
| + uman.set1fv(fParamUni, 3, values); |
| + fCachedRadius = radius0; |
| + fCachedDiffRadius = diffRadius; |
| + } |
| +} |
| + |
| +GrGLEffect::EffectKey GLEdge2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| + const GrGLCaps&) { |
| + return GenBaseGradientKey(drawEffect); |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| +// Focal Conical Gradients |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +static ConicalType set_matrix_focal_conical(const SkTwoPointConicalGradient& shader, |
| + SkMatrix* invLMatrix, SkScalar* focalX) { |
| + // Inverse of the current local matrix is passed in then, |
| + // translate, scale, and rotate such that endCircle is unit circle on x-axis, |
| + // and focal point is at the origin. |
| + ConicalType conicalType; |
| + const SkPoint& focal = shader.getStartCenter(); |
| + const SkPoint& centerEnd = shader.getEndCenter(); |
| + SkScalar radius = shader.getEndRadius(); |
| + SkScalar invRadius = 1.0 / radius; |
| + |
| + SkMatrix matrix; |
| + |
| + matrix.setTranslate(-centerEnd.fX, -centerEnd.fY); |
| + matrix.postScale(invRadius, invRadius); |
| + |
| + SkPoint focalTrans; |
| + matrix.mapPoints(&focalTrans, &focal, 1); |
| + *focalX = focalTrans.length(); |
| + |
| + if (0.0 != *focalX) { |
| + SkScalar invFocalX = SkScalarInvert(*focalX); |
| + SkMatrix rot; |
| + rot.setSinCos(-SkScalarMul(invFocalX, focalTrans.fY), |
| + SkScalarMul(invFocalX, focalTrans.fX)); |
| + matrix.postConcat(rot); |
| + } |
| + |
| + matrix.postTranslate(-(*focalX), 0.0); |
| + |
| + // If the focal point is touch the edge of the circle it will |
|
bsalomon
2014/04/21 18:54:58
touch->touching
|
| + // cause a degenerate case that must be handled separately |
| + if (SkScalarAbs(1.0 - (*focalX)) < kErrorTol) { |
| + return kEdge_ConicalType; |
| + } |
| + |
| + // Scale factor 1 / (1 - focalX * focalX) |
| + SkScalar oneMinusF2 = 1.0 - SkScalarMul(*focalX, *focalX); |
| + SkScalar s = SkScalarDiv(1.0, oneMinusF2); |
| + |
| + |
| + if (s >= 0.0) { |
| + conicalType = kInside_ConicalType; |
| + matrix.postScale(s, s * SkScalarSqrt(oneMinusF2)); |
| + } else { |
| + conicalType = kOutside_ConicalType; |
| + matrix.postScale(s, s); |
| + } |
| + |
| + invLMatrix->postConcat(matrix); |
| + |
| + return conicalType; |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +class GLFocalOutside2PtConicalEffect; |
| + |
| +class FocalOutside2PtConicalEffect : public GrGradientEffect { |
| +public: |
| + |
| + static GrEffectRef* Create(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + SkScalar focalX) { |
| + AutoEffectUnref effect(SkNEW_ARGS(FocalOutside2PtConicalEffect, (ctx, shader, matrix, tm, focalX))); |
| + return CreateEffectRef(effect); |
| + } |
| + |
| + virtual ~FocalOutside2PtConicalEffect() { } |
| + |
| + static const char* Name() { return "Two-Point Conical Gradient Focal Outside"; } |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| + bool isFlipped() const { return fIsFlipped; } |
| + SkScalar focal() const { return fFocalX; } |
| + |
| + typedef GLFocalOutside2PtConicalEffect GLEffect; |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { |
| + const FocalOutside2PtConicalEffect& s = CastEffect<FocalOutside2PtConicalEffect>(sBase); |
| + return (INHERITED::onIsEqual(sBase) && |
| + this->fFocalX == s.fFocalX && |
| + this->fIsFlipped == s.fIsFlipped); |
| + } |
| + |
| + FocalOutside2PtConicalEffect(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + SkScalar focalX) |
| + : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX), fIsFlipped(shader.isFlippedGrad()) {} |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + SkScalar fFocalX; |
| + bool fIsFlipped; |
| + |
| + typedef GrGradientEffect INHERITED; |
| +}; |
| + |
| +class GLFocalOutside2PtConicalEffect : public GrGLGradientEffect { |
| +public: |
| + GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| + virtual ~GLFocalOutside2PtConicalEffect() { } |
| + |
| + virtual void emitCode(GrGLShaderBuilder*, |
| + const GrDrawEffect&, |
| + EffectKey, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); |
| + |
| +protected: |
| + UniformHandle fParamUni; |
| + |
| + const char* fVSVaryingName; |
| + const char* fFSVaryingName; |
| + |
| + bool fIsFlipped; |
| + |
| + // @{ |
| + /// Values last uploaded as uniforms |
| + |
| + SkScalar fCachedFocal; |
| + |
| + // @} |
| + |
| +private: |
| + typedef GrGLGradientEffect INHERITED; |
| + |
| +}; |
| + |
| +const GrBackendEffectFactory& FocalOutside2PtConicalEffect::getFactory() const { |
| + return GrTBackendEffectFactory<FocalOutside2PtConicalEffect>::getInstance(); |
| +} |
| + |
| +GR_DEFINE_EFFECT_TEST(FocalOutside2PtConicalEffect); |
| + |
| +GrEffectRef* FocalOutside2PtConicalEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps&, |
| + GrTexture**) { |
| + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| + SkScalar radius1 = 0.0; |
| + SkPoint center2; |
| + SkScalar radius2; |
| + do { |
| + center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| + SkPoint diff = center2 - center1; |
| + SkScalar diffLen = diff.length(); |
| + // Below makes sure that the focal point is not contained within circle two |
| + radius2 = random->nextRangeF(0.0, diffLen); |
| + // If the circles are identical the factory will give us an empty shader. |
|
bsalomon
2014/04/21 18:54:58
same
|
| + } while (radius1 == radius2 && center1 == center2); |
| + |
| + SkColor colors[kMaxRandomGradientColors]; |
| + SkScalar stopsArray[kMaxRandomGradientColors]; |
| + SkScalar* stops = stopsArray; |
| + SkShader::TileMode tm; |
| + int colorCount = RandomGradientParams(random, colors, &stops, &tm); |
| + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, |
| + center2, radius2, |
| + colors, stops, colorCount, |
| + tm)); |
| + SkPaint paint; |
| + return shader->asNewEffect(context, paint); |
| +} |
| + |
| +GLFocalOutside2PtConicalEffect::GLFocalOutside2PtConicalEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED(factory) |
| + , fVSVaryingName(NULL) |
| + , fFSVaryingName(NULL) |
| + , fCachedFocal(SK_ScalarMax) { |
| + const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>(); |
| + fIsFlipped = data.isFlipped(); |
| +} |
| - // if r(t) > 0 for the smaller root, then t will be our x coordinate |
| - builder->fsCodeAppendf("\t\t\tif (%s * %s + %s > 0.0) {\n", |
| - tName.c_str(), p5.c_str(), p3.c_str()); |
| +void GLFocalOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| + this->emitUniforms(builder, key); |
| + fParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, "Conical2FSParams", 2); |
| + SkString tName("t"); |
| + SkString p0; // focalX |
| + SkString p1; // 1 - focalX * focalX |
| - builder->fsCodeAppend("\t\t\t"); |
| - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->getUniformVariable(fParamUni).appendArrayAccess(0, &p0); |
| + builder->getUniformVariable(fParamUni).appendArrayAccess(1, &p1); |
| - // end if (r(t) > 0) for smaller root |
| - builder->fsCodeAppend("\t\t\t}\n"); |
| - // end if (r(t) > 0), else, for larger root |
| - builder->fsCodeAppend("\t\t}\n"); |
| - // end if (discriminant >= 0) |
| - builder->fsCodeAppend("\t}\n"); |
| + // if we have a vec3 from being in perspective, convert it to a vec2 first |
| + const char* coords2D; |
| + if (kVec3f_GrSLType == coords[0].type()) { |
| + builder->fsCodeAppendf("\tvec2 interpolants = vec2(%s.xy) / %s.z;\n", |
| + coords[0].c_str(), coords[0].c_str()); |
| + coords2D = "interpolants"; |
| } else { |
| + coords2D = coords[0].c_str(); |
| + } |
| + |
| + // t = p.x * focal.x +/- sqrt(p.x^2 + (1 - focal.x^2) * p.y^2) |
| + |
| + // output will default to transparent black (we simply won't write anything |
| + // else to it if invalid, instead of discarding or returning prematurely) |
| + builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
| - // linear case: t = -c/b |
| - builder->fsCodeAppendf("\tfloat %s = -(%s / %s);\n", tName.c_str(), |
| - cName.c_str(), bVar.c_str()); |
| + builder->fsCodeAppendf("\tfloat xs = %s.x * %s.x;\n", coords2D, coords2D); |
| + builder->fsCodeAppendf("\tfloat ys = %s.y * %s.y;\n", coords2D, coords2D); |
| + builder->fsCodeAppendf("\tfloat d = xs + %s * ys;\n", p1.c_str()); |
| - // if r(t) > 0, then t will be the x coordinate |
| - builder->fsCodeAppendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(), |
| - p5.c_str(), p3.c_str()); |
| - builder->fsCodeAppend("\t"); |
| - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| - builder->fsCodeAppend("\t}\n"); |
| + // Must check to see if we flipped the circle order (to make sure start radius < end radius) |
| + // If so we must also flip sign on sqrt |
| + if (!fIsFlipped) { |
| + builder->fsCodeAppendf("\tfloat %s = %s.x * %s + sqrt(d);\n", tName.c_str(), |
| + coords2D, p0.c_str()); |
| + } else { |
| + builder->fsCodeAppendf("\tfloat %s = %s.x * %s - sqrt(d);\n", tName.c_str(), |
| + coords2D, p0.c_str()); |
| } |
| + /* |
| + builder->fsCodeAppendf("\tfloat temp = -1.0 * min(d, %s);\n", tName.c_str()); |
| + builder->fsCodeAppend("\ttemp = clamp(temp, 0.0, 1.0);\n"); |
| + builder->fsCodeAppend("\ttemp = ceil(temp);\n"); |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->fsCodeAppendf("\t%s = (1.0 - temp) * %s;\n", outputColor, outputColor); |
| + */ |
| + |
| + builder->fsCodeAppendf("\tif (%s >= 0.0 && d >= 0.0) {\n", tName.c_str()); |
| + builder->fsCodeAppend("\t\t"); |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->fsCodeAppend("\t}\n"); |
| } |
| -void GLDefault2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| - const GrDrawEffect& drawEffect) { |
| +void GLFocalOutside2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| INHERITED::setData(uman, drawEffect); |
| - const Default2PtConicalEffect& data = drawEffect.castEffect<Default2PtConicalEffect>(); |
| - SkASSERT(data.isDegenerate() == fIsDegenerate); |
| + const FocalOutside2PtConicalEffect& data = drawEffect.castEffect<FocalOutside2PtConicalEffect>(); |
| SkASSERT(data.isFlipped() == fIsFlipped); |
| - SkScalar centerX1 = data.center(); |
| - SkScalar radius0 = data.radius(); |
| - SkScalar diffRadius = data.diffRadius(); |
| + SkScalar focal = data.focal(); |
| - if (fCachedCenter != centerX1 || |
| - fCachedRadius != radius0 || |
| - fCachedDiffRadius != diffRadius) { |
| + if (fCachedFocal != focal) { |
| + SkScalar oneMinus2F = 1.0 - SkScalarMul(focal, focal); |
| - SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius; |
| - |
| - // When we're in the degenerate (linear) case, the second |
| - // value will be INF but the program doesn't read it. (We |
| - // use the same 6 uniforms even though we don't need them |
| - // all in the linear case just to keep the code complexity |
| - // down). |
| - float values[6] = { |
| - SkScalarToFloat(a * 4), |
| - 1.f / (SkScalarToFloat(a)), |
| - SkScalarToFloat(centerX1), |
| - SkScalarToFloat(radius0), |
| - SkScalarToFloat(SkScalarMul(radius0, radius0)), |
| - SkScalarToFloat(diffRadius) |
| + float values[2] = { |
| + SkScalarToFloat(focal), |
| + SkScalarToFloat(oneMinus2F), |
| }; |
| - uman.set1fv(fParamUni, 6, values); |
| - fCachedCenter = centerX1; |
| - fCachedRadius = radius0; |
| - fCachedDiffRadius = diffRadius; |
| + uman.set1fv(fParamUni, 2, values); |
| + fCachedFocal = focal; |
| } |
| } |
| -GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| - const GrGLCaps&) { |
| +GrGLEffect::EffectKey GLFocalOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| + const GrGLCaps&) { |
| enum { |
| - kIsDegenerate = 1 << kBaseKeyBitCnt, |
| - kIsFlipped = 1 << (kBaseKeyBitCnt + 1), |
| + kIsFlipped = 1 << kBaseKeyBitCnt, |
| }; |
| EffectKey key = GenBaseGradientKey(drawEffect); |
| - if (drawEffect.castEffect<Default2PtConicalEffect>().isDegenerate()) { |
| - key |= kIsDegenerate; |
| - } |
| - if (drawEffect.castEffect<Default2PtConicalEffect>().isFlipped()) { |
| + |
| + if (drawEffect.castEffect<FocalOutside2PtConicalEffect>().isFlipped()) { |
| key |= kIsFlipped; |
| } |
| return key; |
| @@ -397,17 +567,769 @@ GrGLEffect::EffectKey GLDefault2PtConicalEffect::GenKey(const GrDrawEffect& draw |
| ////////////////////////////////////////////////////////////////////////////// |
| -GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx, |
| - const SkTwoPointConicalGradient& shader, |
| - SkShader::TileMode tm) { |
| +class GLFocalInside2PtConicalEffect; |
| - SkMatrix matrix; |
| - if (!shader.getLocalMatrix().invert(&matrix)) { |
| - return NULL; |
| +class FocalInside2PtConicalEffect : public GrGradientEffect { |
| +public: |
| + |
| + static GrEffectRef* Create(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + SkScalar focalX) { |
| + AutoEffectUnref effect(SkNEW_ARGS(FocalInside2PtConicalEffect, (ctx, shader, matrix, tm, focalX))); |
| + return CreateEffectRef(effect); |
| + } |
| + |
| + virtual ~FocalInside2PtConicalEffect() {} |
| + |
| + static const char* Name() { return "Two-Point Conical Gradient Focal Inside"; } |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| + SkScalar focal() const { return fFocalX; } |
| + |
| + typedef GLFocalInside2PtConicalEffect GLEffect; |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { |
| + const FocalInside2PtConicalEffect& s = CastEffect<FocalInside2PtConicalEffect>(sBase); |
| + return (INHERITED::onIsEqual(sBase) && |
| + this->fFocalX == s.fFocalX); |
| } |
| - set_matrix_default_conical(shader, &matrix); |
| - return Default2PtConicalEffect::Create(ctx, shader, matrix, tm); |
| + FocalInside2PtConicalEffect(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + SkScalar focalX) |
| + : INHERITED(ctx, shader, matrix, tm), fFocalX(focalX) {} |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + SkScalar fFocalX; |
| + |
| + typedef GrGradientEffect INHERITED; |
| +}; |
| + |
| +class GLFocalInside2PtConicalEffect : public GrGLGradientEffect { |
| +public: |
| + GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| + virtual ~GLFocalInside2PtConicalEffect() {} |
| + |
| + virtual void emitCode(GrGLShaderBuilder*, |
| + const GrDrawEffect&, |
| + EffectKey, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); |
| + |
| +protected: |
| + UniformHandle fFocalUni; |
| + |
| + const char* fVSVaryingName; |
| + const char* fFSVaryingName; |
| + |
| + // @{ |
| + /// Values last uploaded as uniforms |
| + |
| + SkScalar fCachedFocal; |
| + |
| + // @} |
| + |
| +private: |
| + typedef GrGLGradientEffect INHERITED; |
| + |
| +}; |
| + |
| +const GrBackendEffectFactory& FocalInside2PtConicalEffect::getFactory() const { |
| + return GrTBackendEffectFactory<FocalInside2PtConicalEffect>::getInstance(); |
| +} |
| + |
| +GR_DEFINE_EFFECT_TEST(FocalInside2PtConicalEffect); |
| + |
| +GrEffectRef* FocalInside2PtConicalEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps&, |
| + GrTexture**) { |
| + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| + SkScalar radius1 = 0.0; |
| + SkPoint center2; |
| + SkScalar radius2; |
| + do { |
| + center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| + // Below makes sure radius2 is larger enouch such that the focal point |
| + // is inside the end circle |
| + SkScalar increase = random->nextUScalar1(); |
| + SkPoint diff = center2 - center1; |
| + SkScalar diffLen = diff.length(); |
| + radius2 = diffLen + increase; |
| + // If the circles are identical the factory will give us an empty shader. |
| + } while (radius1 == radius2 && center1 == center2); |
| + |
| + SkColor colors[kMaxRandomGradientColors]; |
| + SkScalar stopsArray[kMaxRandomGradientColors]; |
| + SkScalar* stops = stopsArray; |
| + SkShader::TileMode tm; |
| + int colorCount = RandomGradientParams(random, colors, &stops, &tm); |
| + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, |
| + center2, radius2, |
| + colors, stops, colorCount, |
| + tm)); |
| + SkPaint paint; |
| + return shader->asNewEffect(context, paint); |
| +} |
| + |
| +GLFocalInside2PtConicalEffect::GLFocalInside2PtConicalEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED(factory) |
| + , fVSVaryingName(NULL) |
| + , fFSVaryingName(NULL) |
| + , fCachedFocal(SK_ScalarMax) {} |
| + |
| +void GLFocalInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| + this->emitUniforms(builder, key); |
| + fFocalUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kFloat_GrSLType, "Conical2FSParams"); |
| + SkString tName("t"); |
| + |
| + // this is the distance along x-axis from the end center to focal point in |
| + // transformed coordinates |
| + GrGLShaderVar focal = builder->getUniformVariable(fFocalUni); |
| + |
| + // if we have a vec3 from being in perspective, convert it to a vec2 first |
| + const char* coords2D; |
| + if (kVec3f_GrSLType == coords[0].type()) { |
| + builder->fsCodeAppendf("\tvec2 interpolants = vec2(%s.xy) / %s.z;\n", |
| + coords[0].c_str(), coords[0].c_str()); |
| + coords2D = "interpolants"; |
| + } else { |
| + coords2D = coords[0].c_str(); |
| + } |
| + |
| + // output will default to transparent black (we simply won't write anything |
| + // else to it if invalid, instead of discarding or returning prematurely) |
| + builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
|
bsalomon
2014/04/21 18:54:58
I think this initialization only needs to happen i
|
| + |
| + // t = p.x * focalX + length(p) |
| + builder->fsCodeAppendf("\tfloat %s = %s.x * %s + length(%s);\n", tName.c_str(), |
| + coords2D, focal.c_str(), coords2D); |
| + |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| +} |
| + |
| +void GLFocalInside2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| + INHERITED::setData(uman, drawEffect); |
| + const FocalInside2PtConicalEffect& data = drawEffect.castEffect<FocalInside2PtConicalEffect>(); |
| + SkScalar focal = data.focal(); |
| + |
| + if (fCachedFocal != focal) { |
| + uman.set1f(fFocalUni, SkScalarToFloat(focal)); |
| + fCachedFocal = focal; |
| + } |
| +} |
| + |
| +GrGLEffect::EffectKey GLFocalInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| + const GrGLCaps&) { |
| + return GenBaseGradientKey(drawEffect); |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| +// Circle Conical Gradients |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +struct CircleConicalInfo { |
| + SkPoint fCenterEnd; |
| + SkScalar fA; |
| + SkScalar fB; |
| + SkScalar fC; |
| +}; |
| + |
| +// Returns focal distance along x-axis in transformed coords |
| +static ConicalType set_matrix_circle_conical(const SkTwoPointConicalGradient& shader, |
| + SkMatrix* invLMatrix, CircleConicalInfo* info) { |
| + // Inverse of the current local matrix is passed in then, |
| + // translate and scale such that start circle is on the origin and has radius 1 |
| + const SkPoint& centerStart = shader.getStartCenter(); |
| + const SkPoint& centerEnd = shader.getEndCenter(); |
| + SkScalar radiusStart = shader.getStartRadius(); |
| + SkScalar radiusEnd = shader.getEndRadius(); |
| + |
| + SkMatrix matrix; |
| + |
| + matrix.setTranslate(-centerStart.fX, -centerStart.fY); |
| + |
| + SkScalar invStartRad = 1.0 / radiusStart; |
| + matrix.postScale(invStartRad, invStartRad); |
| + |
| + radiusEnd /= radiusStart; |
| + |
| + SkPoint centerEndTrans; |
| + matrix.mapPoints(¢erEndTrans, ¢erEnd, 1); |
| + |
| + SkScalar A = centerEndTrans.fX * centerEndTrans.fX + centerEndTrans.fY * centerEndTrans.fY |
| + - radiusEnd * radiusEnd + 2 * radiusEnd - 1; |
| + |
| + // check to see if start circle is inside end circle with edges touching |
| + // if touching we return that it is of kEdge_ConicalType, and leave the matrix setting |
| + // to the edge shader. |
| + if (SkScalarAbs(A) < kErrorTol) { |
| + return kEdge_ConicalType; |
| + } |
| + |
| + SkScalar C = 1.0 / A; |
| + SkScalar B = (radiusEnd - 1.0) * C; |
| + |
| + matrix.postScale(C, C); |
| + |
| + invLMatrix->postConcat(matrix); |
| + |
| + info->fCenterEnd = centerEndTrans; |
| + info->fA = A; |
| + info->fB = B; |
| + info->fC = C; |
| + |
| + // if A ends up being negative, the start circle is contained completely inside the end cirlce |
| + if (A < 0.0) { |
| + return kInside_ConicalType; |
| + } |
| + return kOutside_ConicalType; |
| +} |
| + |
| +class GLCircleInside2PtConicalEffect; |
| + |
| +class CircleInside2PtConicalEffect : public GrGradientEffect { |
| +public: |
| + |
| + static GrEffectRef* Create(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + const CircleConicalInfo& info) { |
| + AutoEffectUnref effect(SkNEW_ARGS(CircleInside2PtConicalEffect, (ctx, shader, matrix, tm, info))); |
| + return CreateEffectRef(effect); |
| + } |
| + |
| + virtual ~CircleInside2PtConicalEffect() {} |
| + |
| + static const char* Name() { return "Two-Point Conical Gradient Inside"; } |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| + SkScalar centerX() const { return fInfo.fCenterEnd.fX; } |
| + SkScalar centerY() const { return fInfo.fCenterEnd.fY; } |
| + SkScalar A() const { return fInfo.fA; } |
| + SkScalar B() const { return fInfo.fB; } |
| + SkScalar C() const { return fInfo.fC; } |
| + |
| + typedef GLCircleInside2PtConicalEffect GLEffect; |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { |
| + const CircleInside2PtConicalEffect& s = CastEffect<CircleInside2PtConicalEffect>(sBase); |
| + return (INHERITED::onIsEqual(sBase) && |
| + this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && |
| + this->fInfo.fA == s.fInfo.fA && |
| + this->fInfo.fB == s.fInfo.fB && |
| + this->fInfo.fC == s.fInfo.fC); |
| + } |
| + |
| + CircleInside2PtConicalEffect(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + const CircleConicalInfo& info) |
| + : INHERITED(ctx, shader, matrix, tm), fInfo(info) {} |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + const CircleConicalInfo fInfo; |
| + |
| + typedef GrGradientEffect INHERITED; |
| +}; |
| + |
| +class GLCircleInside2PtConicalEffect : public GrGLGradientEffect { |
| +public: |
| + GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| + virtual ~GLCircleInside2PtConicalEffect() {} |
| + |
| + virtual void emitCode(GrGLShaderBuilder*, |
| + const GrDrawEffect&, |
| + EffectKey, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); |
| + |
| +protected: |
| + UniformHandle fCenterUni; |
| + UniformHandle fParamUni; |
| + |
| + const char* fVSVaryingName; |
| + const char* fFSVaryingName; |
| + |
| + // @{ |
| + /// Values last uploaded as uniforms |
| + |
| + SkScalar fCachedCenterX; |
| + SkScalar fCachedCenterY; |
| + SkScalar fCachedA; |
| + SkScalar fCachedB; |
| + SkScalar fCachedC; |
| + |
| + // @} |
| + |
| +private: |
| + typedef GrGLGradientEffect INHERITED; |
| + |
| +}; |
| + |
| +const GrBackendEffectFactory& CircleInside2PtConicalEffect::getFactory() const { |
| + return GrTBackendEffectFactory<CircleInside2PtConicalEffect>::getInstance(); |
| +} |
| + |
| +GR_DEFINE_EFFECT_TEST(CircleInside2PtConicalEffect); |
| + |
| +GrEffectRef* CircleInside2PtConicalEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps&, |
| + GrTexture**) { |
| + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| + SkScalar radius1 = random->nextUScalar1() + 0.0001; // make sure radius1 != 0 |
| + SkPoint center2; |
| + SkScalar radius2; |
| + do { |
| + center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| + // Below makes sure that circle one is contained within circle two |
| + SkScalar increase = random->nextUScalar1(); |
| + SkPoint diff = center2 - center1; |
| + SkScalar diffLen = diff.length(); |
| + radius2 = radius1 + diffLen + increase; |
| + // If the circles are identical the factory will give us an empty shader. |
| + } while (radius1 == radius2 && center1 == center2); |
| + |
| + SkColor colors[kMaxRandomGradientColors]; |
| + SkScalar stopsArray[kMaxRandomGradientColors]; |
| + SkScalar* stops = stopsArray; |
| + SkShader::TileMode tm; |
| + int colorCount = RandomGradientParams(random, colors, &stops, &tm); |
| + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, |
| + center2, radius2, |
| + colors, stops, colorCount, |
| + tm)); |
| + SkPaint paint; |
| + return shader->asNewEffect(context, paint); |
| +} |
| + |
| +GLCircleInside2PtConicalEffect::GLCircleInside2PtConicalEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED(factory) |
| + , fVSVaryingName(NULL) |
| + , fFSVaryingName(NULL) |
| + , fCachedCenterX(SK_ScalarMax) |
| + , fCachedCenterY(SK_ScalarMax) |
| + , fCachedA(SK_ScalarMax) |
| + , fCachedB(SK_ScalarMax) |
| + , fCachedC(SK_ScalarMax) {} |
| + |
| +void GLCircleInside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| + this->emitUniforms(builder, key); |
| + fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kVec2f_GrSLType, "Conical2FSCenter"); |
| + fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kVec3f_GrSLType, "Conical2FSParams"); |
| + SkString tName("t"); |
| + |
| + GrGLShaderVar center = builder->getUniformVariable(fCenterUni); |
| + // params.x = A |
| + // params.y = B |
| + // params.z = C |
| + GrGLShaderVar params = builder->getUniformVariable(fParamUni); |
| + |
| + // if we have a vec3 from being in perspective, convert it to a vec2 first |
| + const char* coords2D; |
| + if (kVec3f_GrSLType == coords[0].type()) { |
| + builder->fsCodeAppendf("\tvec2 interpolants = vec2(%s.xy) / %s.z;\n", |
| + coords[0].c_str(), coords[0].c_str()); |
| + coords2D = "interpolants"; |
| + } else { |
| + coords2D = coords[0].c_str(); |
| + } |
| + |
| + // output will default to transparent black (we simply won't write anything |
| + // else to it if invalid, instead of discarding or returning prematurely) |
| + builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
| + |
| + // p = coords2D |
| + // e = center end |
| + // r = radius end |
| + // A = dot(e, e) - r^2 + 2 * r - 1 |
| + // B = (r -1) / A |
| + // C = 1 / A |
| + // d = dot(e, p) + B |
| + // t = d +/- sqrt(d^2 - A * dot(p, p) + C) |
| + builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); |
| + builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str()); |
| + builder->fsCodeAppendf("\tfloat %s = d + sqrt(d * d - %s.x * pDotp + %s.z);\n", |
| + tName.c_str(), params.c_str(), params.c_str()); |
| + |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| +} |
| + |
| +void GLCircleInside2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| + INHERITED::setData(uman, drawEffect); |
| + const CircleInside2PtConicalEffect& data = drawEffect.castEffect<CircleInside2PtConicalEffect>(); |
| + SkScalar centerX = data.centerX(); |
| + SkScalar centerY = data.centerY(); |
| + SkScalar A = data.A(); |
| + SkScalar B = data.B(); |
| + SkScalar C = data.C(); |
| + |
| + if (fCachedCenterX != centerX || fCachedCenterY != centerY || |
| + fCachedA != A || fCachedB != B || fCachedC != C) { |
| + |
| + uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); |
| + uman.set3f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C)); |
| + |
| + fCachedCenterX = centerX; |
| + fCachedCenterY = centerY; |
| + fCachedA = A; |
| + fCachedB = B; |
| + fCachedC = C; |
| + } |
| +} |
| + |
| +GrGLEffect::EffectKey GLCircleInside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| + const GrGLCaps&) { |
| + EffectKey key = GenBaseGradientKey(drawEffect); |
| + return key; |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +class GLCircleOutside2PtConicalEffect; |
| + |
| +class CircleOutside2PtConicalEffect : public GrGradientEffect { |
| +public: |
| + |
| + static GrEffectRef* Create(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + const CircleConicalInfo& info) { |
| + AutoEffectUnref effect(SkNEW_ARGS(CircleOutside2PtConicalEffect, (ctx, shader, matrix, tm, info))); |
| + return CreateEffectRef(effect); |
| + } |
| + |
| + virtual ~CircleOutside2PtConicalEffect() {} |
| + |
| + static const char* Name() { return "Two-Point Conical Gradient Outside"; } |
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
| + |
| + SkScalar centerX() const { return fInfo.fCenterEnd.fX; } |
| + SkScalar centerY() const { return fInfo.fCenterEnd.fY; } |
| + SkScalar A() const { return fInfo.fA; } |
| + SkScalar B() const { return fInfo.fB; } |
| + SkScalar C() const { return fInfo.fC; } |
| + SkScalar tLimit() const { return fTLimit; } |
| + bool isFlipped() const { return fIsFlipped; } |
| + |
| + typedef GLCircleOutside2PtConicalEffect GLEffect; |
| + |
| +private: |
| + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { |
| + const CircleOutside2PtConicalEffect& s = CastEffect<CircleOutside2PtConicalEffect>(sBase); |
| + return (INHERITED::onIsEqual(sBase) && |
| + this->fInfo.fCenterEnd == s.fInfo.fCenterEnd && |
| + this->fInfo.fA == s.fInfo.fA && |
| + this->fInfo.fB == s.fInfo.fB && |
| + this->fInfo.fC == s.fInfo.fC && |
| + this->fTLimit == s.fTLimit && |
| + this->fIsFlipped == s.fIsFlipped); |
| + } |
| + |
| + CircleOutside2PtConicalEffect(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + const SkMatrix& matrix, |
| + SkShader::TileMode tm, |
| + const CircleConicalInfo& info) |
| + : INHERITED(ctx, shader, matrix, tm), fInfo(info) { |
| + if (shader.getStartRadius() != shader.getEndRadius()) { |
| + fTLimit = SkScalarDiv(shader.getStartRadius(), (shader.getStartRadius() - shader.getEndRadius())); |
| + } else { |
| + fTLimit = SK_ScalarMin; |
| + } |
| + |
| + fIsFlipped = shader.isFlippedGrad(); |
| + } |
| + |
| + GR_DECLARE_EFFECT_TEST; |
| + |
| + const CircleConicalInfo fInfo; |
| + SkScalar fTLimit; |
| + bool fIsFlipped; |
| + |
| + typedef GrGradientEffect INHERITED; |
| +}; |
| + |
| +class GLCircleOutside2PtConicalEffect : public GrGLGradientEffect { |
| +public: |
| + GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); |
| + virtual ~GLCircleOutside2PtConicalEffect() {} |
| + |
| + virtual void emitCode(GrGLShaderBuilder*, |
| + const GrDrawEffect&, |
| + EffectKey, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray&, |
| + const TextureSamplerArray&) SK_OVERRIDE; |
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; |
| + |
| + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps& caps); |
| + |
| +protected: |
| + UniformHandle fCenterUni; |
| + UniformHandle fParamUni; |
| + |
| + const char* fVSVaryingName; |
| + const char* fFSVaryingName; |
| + |
| + bool fIsFlipped; |
| + |
| + // @{ |
| + /// Values last uploaded as uniforms |
| + |
| + SkScalar fCachedCenterX; |
| + SkScalar fCachedCenterY; |
| + SkScalar fCachedA; |
| + SkScalar fCachedB; |
| + SkScalar fCachedC; |
| + SkScalar fCachedTLimit; |
| + |
| + // @} |
| + |
| +private: |
| + typedef GrGLGradientEffect INHERITED; |
| + |
| +}; |
| + |
| +const GrBackendEffectFactory& CircleOutside2PtConicalEffect::getFactory() const { |
| + return GrTBackendEffectFactory<CircleOutside2PtConicalEffect>::getInstance(); |
| +} |
| + |
| +GR_DEFINE_EFFECT_TEST(CircleOutside2PtConicalEffect); |
| + |
| +GrEffectRef* CircleOutside2PtConicalEffect::TestCreate(SkRandom* random, |
| + GrContext* context, |
| + const GrDrawTargetCaps&, |
| + GrTexture**) { |
| + SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()}; |
| + SkScalar radius1 = random->nextUScalar1() + 0.0001; // make sure radius1 != 0 |
| + SkPoint center2; |
| + SkScalar radius2; |
| + SkScalar diffLen; |
| + do { |
| + center2.set(random->nextUScalar1(), random->nextUScalar1()); |
| + SkPoint diff = center2 - center1; |
| + diffLen = diff.length(); |
| + // Below makes sure that circle one is not contained within circle two |
| + // and have radius2 >= radius to match sorting on cpu side |
| + radius2 = radius1 + random->nextRangeF(0.0, diffLen); |
| + // If the circles are identical the factory will give us an empty shader. |
| + } while (radius1 == radius2 && center1 == center2); |
| + |
| + SkColor colors[kMaxRandomGradientColors]; |
| + SkScalar stopsArray[kMaxRandomGradientColors]; |
| + SkScalar* stops = stopsArray; |
| + SkShader::TileMode tm; |
| + int colorCount = RandomGradientParams(random, colors, &stops, &tm); |
| + SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1, |
| + center2, radius2, |
| + colors, stops, colorCount, |
| + tm)); |
| + SkPaint paint; |
| + return shader->asNewEffect(context, paint); |
| +} |
| + |
| +GLCircleOutside2PtConicalEffect::GLCircleOutside2PtConicalEffect(const GrBackendEffectFactory& factory, |
| + const GrDrawEffect& drawEffect) |
| + : INHERITED(factory) |
| + , fVSVaryingName(NULL) |
| + , fFSVaryingName(NULL) |
| + , fCachedCenterX(SK_ScalarMax) |
| + , fCachedCenterY(SK_ScalarMax) |
| + , fCachedA(SK_ScalarMax) |
| + , fCachedB(SK_ScalarMax) |
| + , fCachedC(SK_ScalarMax) |
| + , fCachedTLimit(SK_ScalarMax) { |
| + const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>(); |
| + fIsFlipped = data.isFlipped(); |
| + } |
| + |
| +void GLCircleOutside2PtConicalEffect::emitCode(GrGLShaderBuilder* builder, |
| + const GrDrawEffect&, |
| + EffectKey key, |
| + const char* outputColor, |
| + const char* inputColor, |
| + const TransformedCoordsArray& coords, |
| + const TextureSamplerArray& samplers) { |
| + this->emitUniforms(builder, key); |
| + fCenterUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kVec2f_GrSLType, "Conical2FSCenter"); |
| + fParamUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
| + kVec4f_GrSLType, "Conical2FSParams"); |
| + SkString tName("t"); |
| + |
| + GrGLShaderVar center = builder->getUniformVariable(fCenterUni); |
| + // params.x = A |
| + // params.y = B |
| + // params.z = C |
| + GrGLShaderVar params = builder->getUniformVariable(fParamUni); |
| + |
| + // if we have a vec3 from being in perspective, convert it to a vec2 first |
| + const char* coords2D; |
| + if (kVec3f_GrSLType == coords[0].type()) { |
| + builder->fsCodeAppendf("\tvec2 interpolants = vec2(%s.xy) / %s.z;\n", |
| + coords[0].c_str(), coords[0].c_str()); |
| + coords2D = "interpolants"; |
| + } else { |
| + coords2D = coords[0].c_str(); |
| + } |
| + |
| + // output will default to transparent black (we simply won't write anything |
| + // else to it if invalid, instead of discarding or returning prematurely) |
| + builder->fsCodeAppendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor); |
| + |
| + // p = coords2D |
| + // e = center end |
| + // r = radius end |
| + // A = dot(e, e) - r^2 + 2 * r - 1 |
| + // B = (r -1) / A |
| + // C = 1 / A |
| + // d = dot(e, p) + B |
| + // t = d +/- sqrt(d^2 - A * dot(p, p) + C) |
| + |
| + builder->fsCodeAppendf("\tfloat pDotp = dot(%s, %s);\n", coords2D, coords2D); |
| + builder->fsCodeAppendf("\tfloat d = dot(%s, %s) + %s.y;\n", coords2D, center.c_str(), params.c_str()); |
| + builder->fsCodeAppendf("\tfloat deter = d * d - %s.x * pDotp + %s.z;\n", params.c_str(), params.c_str()); |
| + |
| + // Must check to see if we flipped the circle order (to make sure start radius < end radius) |
| + // If so we must also flip sign on sqrt |
| + if (!fIsFlipped) { |
| + builder->fsCodeAppendf("\tfloat %s = d + sqrt(deter);\n", tName.c_str()); |
| + } else { |
| + builder->fsCodeAppendf("\tfloat %s = d - sqrt(deter);\n", tName.c_str()); |
| + } |
| + |
| + /* |
| + builder->fsCodeAppendf("\tfloat temp = -1.0 * min(deter, %s - %s.w);\n", tName.c_str(), params.c_str()); |
| + builder->fsCodeAppend("\ttemp = clamp(temp, 0.0, 1.0);\n"); |
| + builder->fsCodeAppend("\ttemp = ceil(temp);\n"); |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->fsCodeAppendf("\t%s = (1.0 - temp) * %s;\n", outputColor, outputColor); |
| + */ |
| + builder->fsCodeAppendf("\tif (%s >= %s.w && deter >= 0.0) {\n", tName.c_str(), params.c_str()); |
| + builder->fsCodeAppend("\t\t"); |
| + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); |
| + builder->fsCodeAppend("\t}\n"); |
| +} |
| + |
| +void GLCircleOutside2PtConicalEffect::setData(const GrGLUniformManager& uman, |
| + const GrDrawEffect& drawEffect) { |
| + INHERITED::setData(uman, drawEffect); |
| + const CircleOutside2PtConicalEffect& data = drawEffect.castEffect<CircleOutside2PtConicalEffect>(); |
| + SkASSERT(data.isFlipped() == fIsFlipped); |
| + SkScalar centerX = data.centerX(); |
| + SkScalar centerY = data.centerY(); |
| + SkScalar A = data.A(); |
| + SkScalar B = data.B(); |
| + SkScalar C = data.C(); |
| + SkScalar tLimit = data.tLimit(); |
| + |
| + if (fCachedCenterX != centerX || fCachedCenterY != centerY || |
| + fCachedA != A || fCachedB != B || fCachedC != C || fCachedTLimit != tLimit) { |
| + |
| + uman.set2f(fCenterUni, SkScalarToFloat(centerX), SkScalarToFloat(centerY)); |
| + uman.set4f(fParamUni, SkScalarToFloat(A), SkScalarToFloat(B), SkScalarToFloat(C), |
| + SkScalarToFloat(tLimit)); |
| + |
| + fCachedCenterX = centerX; |
| + fCachedCenterY = centerY; |
| + fCachedA = A; |
| + fCachedB = B; |
| + fCachedC = C; |
| + fCachedTLimit = tLimit; |
| + } |
| +} |
| + |
| +GrGLEffect::EffectKey GLCircleOutside2PtConicalEffect::GenKey(const GrDrawEffect& drawEffect, |
| + const GrGLCaps&) { |
| + enum { |
| + kIsFlipped = 1 << kBaseKeyBitCnt, |
| + }; |
| + |
| + EffectKey key = GenBaseGradientKey(drawEffect); |
| + |
| + if (drawEffect.castEffect<CircleOutside2PtConicalEffect>().isFlipped()) { |
| + key |= kIsFlipped; |
| + } |
| + return key; |
| +} |
| + |
| +////////////////////////////////////////////////////////////////////////////// |
| + |
| +GrEffectRef* Gr2PtConicalGradientEffect::Create(GrContext* ctx, |
| + const SkTwoPointConicalGradient& shader, |
| + SkShader::TileMode tm) { |
| + SkMatrix matrix; |
| + if (!shader.getLocalMatrix().invert(&matrix)) { |
| + return NULL; |
| + } |
| + |
| + if (shader.getStartRadius() < kErrorTol) { |
| + SkScalar focalX; |
| + ConicalType type = set_matrix_focal_conical(shader, &matrix, &focalX); |
| + if (type == kInside_ConicalType) { |
| + return FocalInside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX); |
| + } else if(type == kEdge_ConicalType) { |
| + set_matrix_edge_conical(shader, &matrix); |
| + return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm); |
| + } else { |
| + return FocalOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, focalX); |
| + } |
| + } |
| + |
| + CircleConicalInfo info; |
| + ConicalType type = set_matrix_circle_conical(shader, &matrix, &info); |
| + |
| + if (type == kInside_ConicalType) { |
| + return CircleInside2PtConicalEffect::Create(ctx, shader, matrix, tm, info); |
| + } else if (type == kEdge_ConicalType) { |
| + set_matrix_edge_conical(shader, &matrix); |
|
bsalomon
2014/04/21 18:54:58
might be the browser extension but this looks doub
|
| + return Edge2PtConicalEffect::Create(ctx, shader, matrix, tm); |
| + } else { |
| + return CircleOutside2PtConicalEffect::Create(ctx, shader, matrix, tm, info); |
| + } |
| } |
| #endif |