From: commit-bot@chromium.org Date: Thu, 3 Apr 2014 14:59:19 +0000 (+0000) Subject: Pull Gpu shader out of SkTwoPointConicalGradient into own file X-Git-Tag: submit/tizen/20180928.044319~8417 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=aa64fbfd349789f27b3cc35c1968d9dc0612cf8e;p=platform%2Fupstream%2FlibSkiaSharp.git Pull Gpu shader out of SkTwoPointConicalGradient into own file BUG=skia: R=bsalomon@google.com Author: egdaniel@google.com Review URL: https://codereview.chromium.org/222943002 git-svn-id: http://skia.googlecode.com/svn/trunk@14044 2bbb7eff-a529-9590-31e7-b0007b416f81 --- diff --git a/gyp/effects.gypi b/gyp/effects.gypi index 0bf31a6b38..3a6a010b11 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -77,6 +77,8 @@ '<(skia_src_path)/effects/gradients/SkTwoPointRadialGradient.h', '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.cpp', '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient.h', + '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient_gpu.cpp', + '<(skia_src_path)/effects/gradients/SkTwoPointConicalGradient_gpu.h', '<(skia_src_path)/effects/gradients/SkSweepGradient.cpp', '<(skia_src_path)/effects/gradients/SkSweepGradient.h', diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp index 434339cde7..74a581703c 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient.cpp +++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp @@ -7,6 +7,8 @@ #include "SkTwoPointConicalGradient.h" +#include "SkTwoPointConicalGradient_gpu.h" + static int valid_divide(float numer, float denom, float* ratio) { SkASSERT(ratio); if (0 == denom) { @@ -328,356 +330,8 @@ void SkTwoPointConicalGradient::flatten( buffer.writeScalar(fRadius2); } -///////////////////////////////////////////////////////////////////// - #if SK_SUPPORT_GPU -#include "GrTBackendEffectFactory.h" - -// For brevity -typedef GrGLUniformManager::UniformHandle UniformHandle; - -class GrGLConical2Gradient : public GrGLGradientEffect { -public: - - GrGLConical2Gradient(const GrBackendEffectFactory& factory, const GrDrawEffect&); - virtual ~GrGLConical2Gradient() { } - - 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 fIsDegenerate; - - // @{ - /// Values last uploaded as uniforms - - SkScalar fCachedCenter; - SkScalar fCachedRadius; - SkScalar fCachedDiffRadius; - - // @} - -private: - - typedef GrGLGradientEffect INHERITED; - -}; - -///////////////////////////////////////////////////////////////////// - -class GrConical2Gradient : public GrGradientEffect { -public: - - static GrEffectRef* Create(GrContext* ctx, - const SkTwoPointConicalGradient& shader, - const SkMatrix& matrix, - SkShader::TileMode tm) { - AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm))); - return CreateEffectRef(effect); - } - - virtual ~GrConical2Gradient() { } - - static const char* Name() { return "Two-Point Conical Gradient"; } - virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { - return GrTBackendEffectFactory::getInstance(); - } - - // The radial gradient parameters can collapse to a linear (instead of quadratic) equation. - bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); } - SkScalar center() const { return fCenterX1; } - SkScalar diffRadius() const { return fDiffRadius; } - SkScalar radius() const { return fRadius0; } - - typedef GrGLConical2Gradient GLEffect; - -private: - virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { - const GrConical2Gradient& s = CastEffect(sBase); - return (INHERITED::onIsEqual(sBase) && - this->fCenterX1 == s.fCenterX1 && - this->fRadius0 == s.fRadius0 && - this->fDiffRadius == s.fDiffRadius); - } - - GrConical2Gradient(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()) { - // We pass the linear part of the quadratic as a varying. - // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) - fBTransform = this->getCoordTransform(); - SkMatrix& bMatrix = *fBTransform.accessMatrix(); - SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); - bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) + - SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0])); - bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) + - SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1])); - bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) + - SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2])); - this->addCoordTransform(&fBTransform); - } - - GR_DECLARE_EFFECT_TEST; - - // @{ - // Cache of values - these can change arbitrarily, EXCEPT - // we shouldn't change between degenerate and non-degenerate?! - - GrCoordTransform fBTransform; - SkScalar fCenterX1; - SkScalar fRadius0; - SkScalar fDiffRadius; - - // @} - - typedef GrGradientEffect INHERITED; -}; - -GR_DEFINE_EFFECT_TEST(GrConical2Gradient); - -GrEffectRef* GrConical2Gradient::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 (); - // 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 shader(SkGradientShader::CreateTwoPointConical(center1, radius1, - center2, radius2, - colors, stops, colorCount, - tm)); - SkPaint paint; - return shader->asNewEffect(context, paint); -} - - -///////////////////////////////////////////////////////////////////// - -GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory, - const GrDrawEffect& drawEffect) - : INHERITED(factory) - , fVSVaryingName(NULL) - , fFSVaryingName(NULL) - , fCachedCenter(SK_ScalarMax) - , fCachedRadius(-SK_ScalarMax) - , fCachedDiffRadius(-SK_ScalarMax) { - - const GrConical2Gradient& data = drawEffect.castEffect(); - fIsDegenerate = data.isDegenerate(); -} - -void GrGLConical2Gradient::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); - - 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) - - 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()) { - builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", - coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); - coords2D = "interpolants.xy"; - bVar = "interpolants.z"; - } else { - coords2D = coords[0].c_str(); - bVar.printf("%s.x", coords[1].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); - - // c = (x^2)+(y^2) - params[4] - 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: - builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), - r0Name.c_str(), r1Name.c_str()); - - // 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()); - - builder->fsCodeAppend("\t\t"); - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); - - // otherwise, if r(t) for the larger root was <= 0, try the other root - builder->fsCodeAppend("\t\t} else {\n"); - builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), - r0Name.c_str(), r1Name.c_str()); - - // 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()); - - builder->fsCodeAppend("\t\t\t"); - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); - - // 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"); - } else { - - // 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(), - p5.c_str(), p3.c_str()); - builder->fsCodeAppend("\t"); - this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); - builder->fsCodeAppend("\t}\n"); - } -} - -void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, - const GrDrawEffect& drawEffect) { - INHERITED::setData(uman, drawEffect); - const GrConical2Gradient& data = drawEffect.castEffect(); - SkASSERT(data.isDegenerate() == fIsDegenerate); - SkScalar centerX1 = data.center(); - SkScalar radius0 = data.radius(); - SkScalar diffRadius = data.diffRadius(); - - if (fCachedCenter != centerX1 || - fCachedRadius != radius0 || - fCachedDiffRadius != diffRadius) { - - 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) - }; - - uman.set1fv(fParamUni, 6, values); - fCachedCenter = centerX1; - fCachedRadius = radius0; - fCachedDiffRadius = diffRadius; - } -} - -GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrDrawEffect& drawEffect, - const GrGLCaps&) { - enum { - kIsDegenerate = 1 << kBaseKeyBitCnt, - }; - - EffectKey key = GenBaseGradientKey(drawEffect); - if (drawEffect.castEffect().isDegenerate()) { - key |= kIsDegenerate; - } - return key; -} - -///////////////////////////////////////////////////////////////////// - GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const { SkASSERT(NULL != context); SkASSERT(fPtsToUnit.isIdentity()); @@ -698,7 +352,7 @@ GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const Sk matrix.postConcat(rot); } - return GrConical2Gradient::Create(context, *this, matrix, fTileMode); + return Gr2PtConicalGradientEffect::Create(context, *this, matrix, fTileMode); } #else diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h index dd77299c7d..71ba57f6d1 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient.h +++ b/src/effects/gradients/SkTwoPointConicalGradient.h @@ -6,8 +6,8 @@ * found in the LICENSE file. */ - #ifndef SkTwoPointConicalGradient_DEFINED - #define SkTwoPointConicalGradient_DEFINED +#ifndef SkTwoPointConicalGradient_DEFINED +#define SkTwoPointConicalGradient_DEFINED #include "SkGradientShaderPriv.h" diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp new file mode 100644 index 0000000000..e98d3baac6 --- /dev/null +++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp @@ -0,0 +1,306 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#if SK_SUPPORT_GPU +#include "SkTwoPointConicalGradient_gpu.h" +#include "GrTBackendEffectFactory.h" + +#include "SkTwoPointConicalGradient.h" + +// For brevity +typedef GrGLUniformManager::UniformHandle UniformHandle; + +class GrGL2PtConicalGradientEffect : public GrGLGradientEffect { +public: + + GrGL2PtConicalGradientEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&); + virtual ~GrGL2PtConicalGradientEffect() { } + + 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 fIsDegenerate; + + // @{ + /// Values last uploaded as uniforms + + SkScalar fCachedCenter; + SkScalar fCachedRadius; + SkScalar fCachedDiffRadius; + + // @} + +private: + + typedef GrGLGradientEffect INHERITED; + +}; + +const GrBackendEffectFactory& Gr2PtConicalGradientEffect::getFactory() const { + return GrTBackendEffectFactory::getInstance(); +} + +Gr2PtConicalGradientEffect::Gr2PtConicalGradientEffect(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()) { + // We pass the linear part of the quadratic as a varying. + // float b = -2.0 * (fCenterX1 * x + fRadius0 * fDiffRadius * z) + fBTransform = this->getCoordTransform(); + SkMatrix& bMatrix = *fBTransform.accessMatrix(); + SkScalar r0dr = SkScalarMul(fRadius0, fDiffRadius); + bMatrix[SkMatrix::kMScaleX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMScaleX]) + + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp0])); + bMatrix[SkMatrix::kMSkewX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMSkewX]) + + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp1])); + bMatrix[SkMatrix::kMTransX] = -2 * (SkScalarMul(fCenterX1, bMatrix[SkMatrix::kMTransX]) + + SkScalarMul(r0dr, bMatrix[SkMatrix::kMPersp2])); + this->addCoordTransform(&fBTransform); +} + +GR_DEFINE_EFFECT_TEST(Gr2PtConicalGradientEffect); + +GrEffectRef* Gr2PtConicalGradientEffect::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 (); + // 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 shader(SkGradientShader::CreateTwoPointConical(center1, radius1, + center2, radius2, + colors, stops, colorCount, + tm)); + SkPaint paint; + return shader->asNewEffect(context, paint); +} + + +///////////////////////////////////////////////////////////////////// + +GrGL2PtConicalGradientEffect::GrGL2PtConicalGradientEffect(const GrBackendEffectFactory& factory, + const GrDrawEffect& drawEffect) + : INHERITED(factory) + , fVSVaryingName(NULL) + , fFSVaryingName(NULL) + , fCachedCenter(SK_ScalarMax) + , fCachedRadius(-SK_ScalarMax) + , fCachedDiffRadius(-SK_ScalarMax) { + + const Gr2PtConicalGradientEffect& data = drawEffect.castEffect(); + fIsDegenerate = data.isDegenerate(); +} + +void GrGL2PtConicalGradientEffect::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); + + 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) + + 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()) { + builder->fsCodeAppendf("\tvec3 interpolants = vec3(%s.xy, %s.x) / %s.z;\n", + coords[0].c_str(), coords[1].c_str(), coords[0].c_str()); + coords2D = "interpolants.xy"; + bVar = "interpolants.z"; + } else { + coords2D = coords[0].c_str(); + bVar.printf("%s.x", coords[1].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); + + // c = (x^2)+(y^2) - params[4] + 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: + builder->fsCodeAppendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + + // 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()); + + builder->fsCodeAppend("\t\t"); + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); + + // otherwise, if r(t) for the larger root was <= 0, try the other root + builder->fsCodeAppend("\t\t} else {\n"); + builder->fsCodeAppendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(), + r0Name.c_str(), r1Name.c_str()); + + // 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()); + + builder->fsCodeAppend("\t\t\t"); + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); + + // 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"); + } else { + + // 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(), + p5.c_str(), p3.c_str()); + builder->fsCodeAppend("\t"); + this->emitColor(builder, tName.c_str(), key, outputColor, inputColor, samplers); + builder->fsCodeAppend("\t}\n"); + } +} + +void GrGL2PtConicalGradientEffect::setData(const GrGLUniformManager& uman, + const GrDrawEffect& drawEffect) { + INHERITED::setData(uman, drawEffect); + const Gr2PtConicalGradientEffect& data = drawEffect.castEffect(); + SkASSERT(data.isDegenerate() == fIsDegenerate); + SkScalar centerX1 = data.center(); + SkScalar radius0 = data.radius(); + SkScalar diffRadius = data.diffRadius(); + + if (fCachedCenter != centerX1 || + fCachedRadius != radius0 || + fCachedDiffRadius != diffRadius) { + + 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) + }; + + uman.set1fv(fParamUni, 6, values); + fCachedCenter = centerX1; + fCachedRadius = radius0; + fCachedDiffRadius = diffRadius; + } +} + +GrGLEffect::EffectKey GrGL2PtConicalGradientEffect::GenKey(const GrDrawEffect& drawEffect, + const GrGLCaps&) { + enum { + kIsDegenerate = 1 << kBaseKeyBitCnt, + }; + + EffectKey key = GenBaseGradientKey(drawEffect); + if (drawEffect.castEffect().isDegenerate()) { + key |= kIsDegenerate; + } + return key; +} +#endif + diff --git a/src/effects/gradients/SkTwoPointConicalGradient_gpu.h b/src/effects/gradients/SkTwoPointConicalGradient_gpu.h new file mode 100644 index 0000000000..2246f335a6 --- /dev/null +++ b/src/effects/gradients/SkTwoPointConicalGradient_gpu.h @@ -0,0 +1,73 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTwoPointConicalGradient_gpu_DEFINED +#define SkTwoPointconicalGradient_gpu_DEFINED + +#if SK_SUPPORT_GPU + +#include "SkGradientShaderPriv.h" + +class GrEffectRef; +class SkTwoPointConicalGradient; +class GrGL2PtConicalGradientEffect; + +class Gr2PtConicalGradientEffect : public GrGradientEffect { +public: + + static GrEffectRef* Create(GrContext* ctx, + const SkTwoPointConicalGradient& shader, + const SkMatrix& matrix, + SkShader::TileMode tm) { + AutoEffectUnref effect(SkNEW_ARGS(Gr2PtConicalGradientEffect, (ctx, shader, matrix, tm))); + return CreateEffectRef(effect); + } + + virtual ~Gr2PtConicalGradientEffect() { } + + static const char* Name() { return "Two-Point Conical Gradient"; } + 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); } + SkScalar center() const { return fCenterX1; } + SkScalar diffRadius() const { return fDiffRadius; } + SkScalar radius() const { return fRadius0; } + + typedef GrGL2PtConicalGradientEffect GLEffect; + +private: + virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE { + const Gr2PtConicalGradientEffect& s = CastEffect(sBase); + return (INHERITED::onIsEqual(sBase) && + this->fCenterX1 == s.fCenterX1 && + this->fRadius0 == s.fRadius0 && + this->fDiffRadius == s.fDiffRadius); + } + + Gr2PtConicalGradientEffect(GrContext* ctx, + const SkTwoPointConicalGradient& shader, + const SkMatrix& matrix, + SkShader::TileMode tm); + + GR_DECLARE_EFFECT_TEST; + + // @{ + // Cache of values - these can change arbitrarily, EXCEPT + // we shouldn't change between degenerate and non-degenerate?! + + GrCoordTransform fBTransform; + SkScalar fCenterX1; + SkScalar fRadius0; + SkScalar fDiffRadius; + + // @} + + typedef GrGradientEffect INHERITED; +}; +#endif +#endif