diff options
Diffstat (limited to 'src/effects/gradients/SkTwoPointRadialGradient.cpp')
-rw-r--r-- | src/effects/gradients/SkTwoPointRadialGradient.cpp | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp index 27eb5aae4f..d2612a4f78 100644 --- a/src/effects/gradients/SkTwoPointRadialGradient.cpp +++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp @@ -362,3 +362,259 @@ void SkTwoPointRadialGradient::init() { fPtsToUnit.postScale(inv, inv); } +///////////////////////////////////////////////////////////////////// + +// For brevity +typedef GrGLUniformManager::UniformHandle UniformHandle; +static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; + +class GrGLRadial2Gradient : public GrGLGradientStage { + +public: + + GrGLRadial2Gradient(const GrProgramStageFactory& factory, + const GrCustomStage&); + virtual ~GrGLRadial2Gradient() { } + + virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE; + virtual void emitVS(GrGLShaderBuilder* builder, + const char* vertexCoords) SK_OVERRIDE; + virtual void emitFS(GrGLShaderBuilder* builder, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + virtual void setData(const GrGLUniformManager&, + const GrCustomStage&, + const GrRenderTarget*, + int stageNum) SK_OVERRIDE; + + static StageKey GenKey(const GrCustomStage& s) { + return (static_cast<const GrRadial2Gradient&>(s).isDegenerate()); + } + +protected: + + UniformHandle fVSParamUni; + UniformHandle fFSParamUni; + + const char* fVSVaryingName; + const char* fFSVaryingName; + + bool fIsDegenerate; + + // @{ + /// Values last uploaded as uniforms + + GrScalar fCachedCenter; + GrScalar fCachedRadius; + bool fCachedPosRoot; + + // @} + +private: + + typedef GrGLGradientStage INHERITED; + +}; + +GrGLRadial2Gradient::GrGLRadial2Gradient( + const GrProgramStageFactory& factory, + const GrCustomStage& baseData) + : INHERITED(factory) + , fVSParamUni(kInvalidUniformHandle) + , fFSParamUni(kInvalidUniformHandle) + , fVSVaryingName(NULL) + , fFSVaryingName(NULL) + , fCachedCenter(GR_ScalarMax) + , fCachedRadius(-GR_ScalarMax) + , fCachedPosRoot(0) { + + const GrRadial2Gradient& data = + static_cast<const GrRadial2Gradient&>(baseData); + fIsDegenerate = data.isDegenerate(); +} + +void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) { + // 2 copies of uniform array, 1 for each of vertex & fragment shader, + // to work around Xoom bug. Doesn't seem to cause performance decrease + // in test apps, but need to keep an eye on it. + fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType, + kFloat_GrSLType, "Radial2VSParams", 6); + fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "Radial2FSParams", 6); + + // For radial gradients without perspective we can pass the linear + // part of the quadratic as a varying. + if (builder->fVaryingDims == builder->fCoordDims) { + builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", + &fVSVaryingName, &fFSVaryingName); + } +} + +void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* builder, + const char* vertexCoords) { + SkString* code = &builder->fVSCode; + SkString p2; + SkString p3; + builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2); + builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3); + + // For radial gradients without perspective we can pass the linear + // part of the quadratic as a varying. + if (builder->fVaryingDims == builder->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* builder, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + SkString* code = &builder->fFSCode; + SkString cName("c"); + SkString ac4Name("ac4"); + SkString rootName("root"); + SkString t; + SkString p0; + SkString p1; + SkString p2; + SkString p3; + SkString p4; + SkString p5; + builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0); + builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1); + builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2); + builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3); + builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4); + builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5); + + // If we we're able to interpolate the linear component, + // bVar is the varying; otherwise compute it + SkString bVar; + if (builder->fCoordDims == builder->fVaryingDims) { + bVar = fFSVaryingName; + GrAssert(2 == builder->fVaryingDims); + } else { + GrAssert(3 == builder->fVaryingDims); + bVar = "b"; + //bVar.appendS32(stageNum); + code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n", + bVar.c_str(), p2.c_str(), + builder->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(), builder->fSampleCoords.c_str(), + builder->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()); + + // t is: (-b + params[5] * sqrt(b^2-4ac)) * params[1] + t.printf("(-%s + %s * %s) * %s", bVar.c_str(), p5.c_str(), + rootName.c_str(), p1.c_str()); + } else { + // t is: -c/b + t.printf("-%s / %s", cName.c_str(), bVar.c_str()); + } + + this->emitColorLookup(builder, t.c_str(), outputColor, samplerName); +} + +void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, + const GrCustomStage& baseData, + const GrRenderTarget*, + int stageNum) { + const GrRadial2Gradient& data = + static_cast<const GrRadial2Gradient&>(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 + }; + + uman.set1fv(fVSParamUni, 0, 6, values); + uman.set1fv(fFSParamUni, 0, 6, values); + fCachedCenter = centerX1; + fCachedRadius = radius0; + fCachedPosRoot = data.isPosRoot(); + } +} + + +///////////////////////////////////////////////////////////////////// + +GrRadial2Gradient::GrRadial2Gradient(GrTexture* texture, + GrScalar center, + GrScalar radius, + bool posRoot) + : INHERITED(texture) + , fCenterX1 (center) + , fRadius0 (radius) + , fPosRoot (posRoot) { + +} + +GrRadial2Gradient::GrRadial2Gradient(GrContext* ctx, + const SkShader& shader, + GrSamplerState* sampler, + SkScalar center, + SkScalar startRadius, + SkScalar diffRadius) + : INHERITED(ctx, shader, sampler) + , fCenterX1(center) + , fRadius0(startRadius) + , fPosRoot(diffRadius < 0) { +} + +GrRadial2Gradient::~GrRadial2Gradient() { + +} + + +const GrProgramStageFactory& GrRadial2Gradient::getFactory() const { + return GrTProgramStageFactory<GrRadial2Gradient>::getInstance(); +} + +bool GrRadial2Gradient::isEqual(const GrCustomStage& sBase) const { + const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase); + return (INHERITED::isEqual(sBase) && + this->fCenterX1 == s.fCenterX1 && + this->fRadius0 == s.fRadius0 && + this->fPosRoot == s.fPosRoot); +} + |