diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 859454546..bd415b482 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -277,6 +277,8 @@ '../src/gpu/effects/Gr1DKernelEffect.h', '../src/gpu/effects/GrConvolutionEffect.cpp', '../src/gpu/effects/GrConvolutionEffect.h', + '../src/gpu/effects/GrGradientEffects.cpp', + '../src/gpu/effects/GrGradientEffects.h', '../src/gpu/effects/GrMorphologyEffect.cpp', '../src/gpu/effects/GrMorphologyEffect.h', diff --git a/src/gpu/effects/GrGradientEffects.cpp b/src/gpu/effects/GrGradientEffects.cpp new file mode 100644 index 000000000..3f498b332 --- /dev/null +++ b/src/gpu/effects/GrGradientEffects.cpp @@ -0,0 +1,365 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrGradientEffects.h" +#include "gl/GrGLProgramStage.h" +#include "GrProgramStageFactory.h" + +///////////////////////////////////////////////////////////////////// + +class GrGLRadialGradient : public GrGLProgramStage { + +public: + + GrGLRadialGradient(const GrProgramStageFactory& factory, + const GrCustomStage&) : INHERITED (factory) { } + virtual ~GrGLRadialGradient() { } + + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE { } + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + + static StageKey GenKey(const GrCustomStage& s) { return 0; } + +private: + + typedef GrGLProgramStage INHERITED; + +}; + +void GrGLRadialGradient::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + state->fSampleCoords.printf("vec2(length(%s.xy), 0.5)", + state->fSampleCoords.c_str()); + state->fComplexCoord = true; + + state->emitDefaultFetch(outputColor, samplerName); +} + + +///////////////////////////////////////////////////////////////////// + + +GrRadialGradient::GrRadialGradient() { + +} + +GrRadialGradient::~GrRadialGradient() { + +} + + +const GrProgramStageFactory& GrRadialGradient::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrRadialGradient::isEqual(const GrCustomStage& sBase) const { + return true; +} + +///////////////////////////////////////////////////////////////////// + +class GrGLRadial2Gradient : public GrGLProgramStage { + +public: + + GrGLRadial2Gradient(const GrProgramStageFactory& factory, + const GrCustomStage&); + virtual ~GrGLRadial2Gradient() { } + + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE; + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + GrCustomStage*, + int stageNum) SK_OVERRIDE; + + static StageKey GenKey(const GrCustomStage& s) { + return (static_cast(s).isDegenerate()); + } + +protected: + + const GrGLShaderVar* fParamVar; + GrGLint fParamLocation; + + const char* fVSVaryingName; + const char* fFSVaryingName; + + bool fIsDegenerate; + + // @{ + /// Values last uploaded as uniforms + + GrScalar fCachedCenter; + GrScalar fCachedRadius; + SkBool8 fCachedPosRoot; + + // @} + +private: + + typedef GrGLProgramStage INHERITED; + +}; + +GrGLRadial2Gradient::GrGLRadial2Gradient( + const GrProgramStageFactory& factory, + const GrCustomStage& baseData) + : INHERITED(factory) + , fParamVar(NULL) + , fVSVaryingName(NULL) + , fFSVaryingName(NULL) + , fCachedCenter(GR_ScalarMax) + , fCachedRadius(-GR_ScalarMax) + , fCachedPosRoot(0) { + + const GrRadial2Gradient& data = + static_cast(baseData); + fIsDegenerate = data.isDegenerate(); +} + +void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* state, int stage) { + fParamVar = &state->addUniform( + GrGLShaderBuilder::kBoth_VariableLifetime, + kFloat_GrSLType, "uRadial2Params", stage, 6); + + fParamLocation = GrGLProgramStage::kUseUniform; + + // For radial gradients without perspective we can pass the linear + // part of the quadratic as a varying. + if (state->fVaryingDims == state->fCoordDims) { + state->addVarying(kFloat_GrSLType, "Radial2BCoeff", stage, + &fVSVaryingName, &fFSVaryingName); + } +} + +void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) { + GrStringBuilder* code = &state->fVSCode; + GrStringBuilder p2; + GrStringBuilder p3; + fParamVar->appendArrayAccess(2, &p2); + fParamVar->appendArrayAccess(3, &p3); + + // For radial gradients without perspective we can pass the linear + // part of the quadratic as a varying. + if (state->fVaryingDims == state->fCoordDims) { + // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3]) + code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n", + fVSVaryingName, p2.c_str(), + vertexCoords, p3.c_str()); + } +} + +void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + GrStringBuilder* code = &state->fFSCode; + GrStringBuilder cName("c"); + GrStringBuilder ac4Name("ac4"); + GrStringBuilder rootName("root"); + GrStringBuilder p0; + GrStringBuilder p1; + GrStringBuilder p2; + GrStringBuilder p3; + GrStringBuilder p4; + GrStringBuilder p5; + fParamVar->appendArrayAccess(0, &p0); + fParamVar->appendArrayAccess(1, &p1); + fParamVar->appendArrayAccess(2, &p2); + fParamVar->appendArrayAccess(3, &p3); + fParamVar->appendArrayAccess(4, &p4); + fParamVar->appendArrayAccess(5, &p5); + + // If we we're able to interpolate the linear component, + // bVar is the varying; otherwise compute it + GrStringBuilder bVar; + if (state->fCoordDims == state->fVaryingDims) { + bVar = fFSVaryingName; + GrAssert(2 == state->fVaryingDims); + } else { + GrAssert(3 == state->fVaryingDims); + bVar = "b"; + //bVar.appendS32(stageNum); + code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n", + bVar.c_str(), p2.c_str(), + state->fSampleCoords.c_str(), p3.c_str()); + } + + // c = (x^2)+(y^2) - params[4] + code->appendf("\tfloat %s = dot(%s, %s) - %s;\n", + cName.c_str(), state->fSampleCoords.c_str(), + state->fSampleCoords.c_str(), + p4.c_str()); + + // If we aren't degenerate, emit some extra code, and accept a slightly + // more complex coord. + if (fIsDegenerate) { + + // ac4 = 4.0 * params[0] * c + code->appendf("\tfloat %s = %s * 4.0 * %s;\n", + ac4Name.c_str(), p0.c_str(), + cName.c_str()); + + // root = sqrt(b^2-4ac) + // (abs to avoid exception due to fp precision) + code->appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n", + rootName.c_str(), bVar.c_str(), bVar.c_str(), + ac4Name.c_str()); + + // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] + // y coord is 0.5 (texture is effectively 1D) + state->fSampleCoords.printf("vec2((-%s + %s * %s) * %s, 0.5)", + bVar.c_str(), p5.c_str(), + rootName.c_str(), p1.c_str()); + } else { + // x coord is: -c/b + // y coord is 0.5 (texture is effectively 1D) + state->fSampleCoords.printf("vec2((-%s / %s), 0.5)", + cName.c_str(), bVar.c_str()); + } + state->fComplexCoord = true; + + state->emitDefaultFetch(outputColor, samplerName); +} + +void GrGLRadial2Gradient::initUniforms(const GrGLInterface* gl, int programID) { + GR_GL_CALL_RET(gl, fParamLocation, + GetUniformLocation(programID, fParamVar->getName().c_str())); +} + +void GrGLRadial2Gradient::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + GrCustomStage* baseData, + int stageNum) { + const GrRadial2Gradient* data = + static_cast(baseData); + GrAssert(data->isDegenerate() == fIsDegenerate); + GrScalar centerX1 = data->center(); + GrScalar radius0 = data->radius(); + if (fCachedCenter != centerX1 || + fCachedRadius != radius0 || + fCachedPosRoot != data->isPosRoot()) { + + GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1; + + // 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] = { + GrScalarToFloat(a), + 1 / (2.f * GrScalarToFloat(a)), + GrScalarToFloat(centerX1), + GrScalarToFloat(radius0), + GrScalarToFloat(GrMul(radius0, radius0)), + data->isPosRoot() ? 1.f : -1.f + }; + + GR_GL_CALL(gl, Uniform1fv(fParamLocation, 6, values)); + fCachedCenter = centerX1; + fCachedRadius = radius0; + fCachedPosRoot = data->isPosRoot(); + } +} + + +///////////////////////////////////////////////////////////////////// + +GrRadial2Gradient::GrRadial2Gradient(GrScalar center, + GrScalar radius, + bool posRoot) + : fCenterX1 (center) + , fRadius0 (radius) + , fPosRoot (posRoot) { + +} + +GrRadial2Gradient::~GrRadial2Gradient() { + +} + + +const GrProgramStageFactory& GrRadial2Gradient::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrRadial2Gradient::isEqual(const GrCustomStage& sBase) const { + const GrRadial2Gradient& s = static_cast(sBase); + return (this->isDegenerate() == s.isDegenerate()); +} + +///////////////////////////////////////////////////////////////////// + +class GrGLSweepGradient : public GrGLProgramStage { + +public: + + GrGLSweepGradient(const GrProgramStageFactory& factory, + const GrCustomStage&) : INHERITED (factory) { } + virtual ~GrGLSweepGradient() { } + + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE { } + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + + static StageKey GenKey(const GrCustomStage& s) { return 0; } + +private: + + typedef GrGLProgramStage INHERITED; + +}; + +void GrGLSweepGradient::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + state->fSampleCoords.printf( + "vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", + state->fSampleCoords.c_str(), state->fSampleCoords.c_str()); + state->fComplexCoord = true; + + state->emitDefaultFetch(outputColor, samplerName); +} + +///////////////////////////////////////////////////////////////////// + +GrSweepGradient::GrSweepGradient() { + +} + +GrSweepGradient::~GrSweepGradient() { + +} + +const GrProgramStageFactory& GrSweepGradient::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrSweepGradient::isEqual(const GrCustomStage& sBase) const { + return true; +} + diff --git a/src/gpu/effects/GrGradientEffects.h b/src/gpu/effects/GrGradientEffects.h new file mode 100644 index 000000000..8d67c2526 --- /dev/null +++ b/src/gpu/effects/GrGradientEffects.h @@ -0,0 +1,93 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrGradientEffects_DEFINED +#define GrGradientEffects_DEFINED + +#include "GrCustomStage.h" +#include "GrTypes.h" +#include "GrScalar.h" + +class GrGLRadialGradient; + +class GrRadialGradient : public GrCustomStage { + +public: + + GrRadialGradient(); + virtual ~GrRadialGradient(); + + static const char* Name() { return "Radial Gradient"; } + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + typedef GrGLRadialGradient GLProgramStage; + +private: + + typedef GrCustomStage INHERITED; +}; + +class GrGLRadial2Gradient; + +class GrRadial2Gradient : public GrCustomStage { + +public: + + GrRadial2Gradient(GrScalar center, GrScalar radius, bool posRoot); + virtual ~GrRadial2Gradient(); + + static const char* Name() { return "Two-Point Radial Gradient"; } + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + // The radial gradient parameters can collapse to a linear (instead + // of quadratic) equation. + bool isDegenerate() const { return GR_Scalar1 == fCenterX1; } + GrScalar center() const { return fCenterX1; } + GrScalar radius() const { return fRadius0; } + bool isPosRoot() const { return SkToBool(fPosRoot); } + + typedef GrGLRadial2Gradient GLProgramStage; + +private: + + // @{ + // Cache of values - these can change arbitrarily, EXCEPT + // we shouldn't change between degenerate and non-degenerate?! + + GrScalar fCenterX1; + GrScalar fRadius0; + SkBool8 fPosRoot; + + // @} + + typedef GrCustomStage INHERITED; +}; + +class GrGLSweepGradient; + +class GrSweepGradient : public GrCustomStage { + +public: + + GrSweepGradient(); + virtual ~GrSweepGradient(); + + static const char* Name() { return "Sweep Gradient"; } + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + typedef GrGLSweepGradient GLProgramStage; + +protected: + + typedef GrCustomStage INHERITED; +}; + +#endif +