diff --git a/gyp/effects.gyp b/gyp/effects.gyp index fbccb401b..cb15a2ac2 100644 --- a/gyp/effects.gyp +++ b/gyp/effects.gyp @@ -7,6 +7,8 @@ '../include/config', '../include/core', '../include/effects', + '../include/gpu', + '../src/gpu', ], 'sources': [ '../include/effects/Sk1DPathEffect.h', diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index 91e7385fd..2874054bd 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -163,6 +163,9 @@ 'include_dirs': [ '../include/core', '../include/config', + '../include/effects', # This is only to get Sk effects into + # GrGpuGL_unittest.cpp, not for general + # consumption in src/gpu. '../include/gpu', '../src/core', # SkRasterClip.h '../src/gpu' diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h index c27f69820..b7bf8bcfe 100644 --- a/include/core/SkImageFilter.h +++ b/include/core/SkImageFilter.h @@ -14,6 +14,7 @@ class SkBitmap; class SkDevice; class SkMatrix; struct SkPoint; +class GrCustomStage; /** * Experimental. @@ -76,6 +77,17 @@ public: */ bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst); + /** + * Returns true if the filter can be expressed a single-pass + * GrCustomStage, used to process this filter on the GPU, or false if + * not. + * + * If stage is non-NULL, a new GrCustomStage instance is stored + * in it. The caller assumes ownership of the stage, and it is up to the + * caller to unref it. + */ + virtual bool asNewCustomStage(GrCustomStage** stage) const; + /** * Experimental. * diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h index 5406e7e62..c1ef234ea 100644 --- a/include/effects/SkLightingImageFilter.h +++ b/include/effects/SkLightingImageFilter.h @@ -39,6 +39,9 @@ public: SkPoint3 operator-(const SkPoint3& other) const { return SkPoint3(fX - other.fX, fY - other.fY, fZ - other.fZ); } + bool operator==(const SkPoint3& other) const { + return fX == other.fX && fY == other.fY && fZ == other.fZ; + } SkScalar fX, fY, fZ; }; diff --git a/include/gpu/GrProgramStageFactory.h b/include/gpu/GrProgramStageFactory.h index d133beb9f..f2b4f5fc9 100644 --- a/include/gpu/GrProgramStageFactory.h +++ b/include/gpu/GrProgramStageFactory.h @@ -10,6 +10,7 @@ #include "GrTypes.h" #include "SkTemplates.h" +#include "GrNoncopyable.h" /** Given a GrCustomStage of a particular type, creates the corresponding graphics-backend-specific GrProgramStage. Also tracks equivalence diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index cba7930af..08e642cfb 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -2193,6 +2193,10 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, return true; } +bool SkImageFilter::asNewCustomStage(GrCustomStage**) const { + return false; +} + bool SkImageFilter::asABlur(SkSize* sigma) const { return false; } diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp index 5625d0363..ef45968f5 100644 --- a/src/effects/SkLightingImageFilter.cpp +++ b/src/effects/SkLightingImageFilter.cpp @@ -8,6 +8,14 @@ #include "SkLightingImageFilter.h" #include "SkBitmap.h" #include "SkColorPriv.h" +#include "GrProgramStageFactory.h" +#include "gl/GrGLProgramStage.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLTexture.h" +#include "GrCustomStage.h" + +class GrGLDiffuseLightingEffect; +class GrGLSpecularLightingEffect; // FIXME: Eventually, this should be implemented properly, and put in // SkScalar.h. @@ -19,6 +27,13 @@ const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3)); const SkScalar gOneHalf = SkFloatToScalar(0.5f); const SkScalar gOneQuarter = SkFloatToScalar(0.25f); +void setUniformPoint3(const GrGLInterface* gl, GrGLint location, const SkPoint3& point) { + float x = SkScalarToFloat(point.fX); + float y = SkScalarToFloat(point.fY); + float z = SkScalarToFloat(point.fZ); + GR_GL_CALL(gl, Uniform3f(location, x, y, z)); +} + // Shift matrix components to the left, as we advance pixels to the right. inline void shiftMatrixLeft(int m[9]) { m[0] = m[1]; @@ -236,6 +251,7 @@ public: SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter) + virtual bool asNewCustomStage(GrCustomStage** stage) const SK_OVERRIDE; SkScalar kd() const { return fKD; } protected: @@ -255,6 +271,10 @@ public: SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess); SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter) + virtual bool asNewCustomStage(GrCustomStage** stage) const SK_OVERRIDE; + SkScalar ks() const { return fKS; } + SkScalar shininess() const { return fShininess; } + protected: explicit SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer); virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE; @@ -267,8 +287,144 @@ private: SkScalar fShininess; }; + +class GrLightingEffect : public GrCustomStage { +public: + GrLightingEffect(const SkLight* light, SkScalar surfaceScale); + virtual ~GrLightingEffect(); + + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + const SkLight* light() const { return fLight; } + SkScalar surfaceScale() const { return fSurfaceScale; } +private: + typedef GrCustomStage INHERITED; + const SkLight* fLight; + SkScalar fSurfaceScale; }; +class GrDiffuseLightingEffect : public GrLightingEffect { +public: + GrDiffuseLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar kd); + + static const char* Name() { return "DiffuseLighting"; } + + typedef GrGLDiffuseLightingEffect GLProgramStage; + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + SkScalar kd() const { return fKD; } +private: + typedef GrLightingEffect INHERITED; + SkScalar fKD; +}; + +class GrSpecularLightingEffect : public GrLightingEffect { +public: + GrSpecularLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess); + + static const char* Name() { return "SpecularLighting"; } + + typedef GrGLSpecularLightingEffect GLProgramStage; + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + SkScalar ks() const { return fKS; } + SkScalar shininess() const { return fShininess; } + +private: + typedef GrLightingEffect INHERITED; + SkScalar fKS; + SkScalar fShininess; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void emitVS(SkString* builder) const {} + virtual void emitFuncs(SkString* builder) const {} + virtual void emitSurfaceToLight(SkString* builder, const char* z) const = 0; + virtual void emitLightColor(SkString* builder, const char *surfaceToLight) const; + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface*, const SkLight* light) const; + +private: + typedef SkRefCnt INHERITED; + +protected: + const GrGLShaderVar* fColorVar; + int fColorVarLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLDistantLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface* gl, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + +private: + typedef GrGLLight INHERITED; + const GrGLShaderVar* fDirectionVar; + int fDirectionLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLPointLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitVS(SkString* builder) const; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + +private: + typedef GrGLLight INHERITED; + SkPoint3 fLocation; + const GrGLShaderVar* fLocationVar; + int fLocationLocation; + const char* fHeightVaryingName; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLSpotLight : public GrGLLight { +public: + virtual void setupVariables(GrGLShaderBuilder* state, int stage); + virtual void initUniforms(const GrGLInterface* gl, int programID); + virtual void setData(const GrGLInterface* gl, const SkLight* light) const SK_OVERRIDE; + virtual void emitVS(SkString* builder) const; + virtual void emitFuncs(SkString* builder) const; + virtual void emitSurfaceToLight(SkString* builder, const char* z) const SK_OVERRIDE; + virtual void emitLightColor(SkString* builder, const char *surfaceToLight) const; + +private: + typedef GrGLLight INHERITED; + + const GrGLShaderVar* fLocationVar; + int fLocationLocation; + const GrGLShaderVar* fExponentVar; + int fExponentLocation; + const GrGLShaderVar* fCosOuterConeAngleVar; + int fCosOuterConeAngleLocation; + const GrGLShaderVar* fCosInnerConeAngleVar; + int fCosInnerConeAngleLocation; + const GrGLShaderVar* fConeScaleVar; + int fConeScaleLocation; + const GrGLShaderVar* fSVar; + int fSLocation; + const char* fHeightVaryingName; +}; + +}; + +/////////////////////////////////////////////////////////////////////////////// + class SkLight : public SkFlattenable { public: SK_DECLARE_INST_COUNT(SkLight) @@ -280,6 +436,10 @@ public: }; virtual LightType type() const = 0; const SkPoint3& color() const { return fColor; } + virtual GrGLLight* createGLLight() const = 0; + virtual bool isEqual(const SkLight& other) const { + return fColor == other.fColor; + } protected: SkLight(SkColor color) @@ -302,6 +462,8 @@ private: SK_DEFINE_INST_COUNT(SkLight) +/////////////////////////////////////////////////////////////////////////////// + class SkDistantLight : public SkLight { public: SkDistantLight(const SkPoint3& direction, SkColor color) @@ -313,7 +475,8 @@ public: }; SkPoint3 lightColor(const SkPoint3&) const { return color(); } virtual LightType type() const { return kDistant_LightType; } - SkPoint3 direction() const { return fDirection; } + const SkPoint3& direction() const { return fDirection; } + virtual GrGLLight* createGLLight() const SK_OVERRIDE; SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDistantLight) @@ -331,6 +494,8 @@ private: SkPoint3 fDirection; }; +/////////////////////////////////////////////////////////////////////////////// + class SkPointLight : public SkLight { public: SkPointLight(const SkPoint3& location, SkColor color) @@ -345,6 +510,18 @@ public: }; SkPoint3 lightColor(const SkPoint3&) const { return color(); } virtual LightType type() const { return kPoint_LightType; } + const SkPoint3& location() const { return fLocation; } + virtual GrGLLight* createGLLight() const SK_OVERRIDE { + return new GrGLPointLight(); + } + bool isEqual(const SkLight& other) SK_OVERRIDE { + if (other.type() != kPoint_LightType) { + return false; + } + const SkPointLight& o = static_cast(other); + return INHERITED::isEqual(other) && + fLocation == o.fLocation; + } SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPointLight) @@ -362,6 +539,8 @@ private: SkPoint3 fLocation; }; +/////////////////////////////////////////////////////////////////////////////// + class SkSpotLight : public SkLight { public: SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor color) @@ -397,8 +576,17 @@ public: } return color() * scale; } - + virtual GrGLLight* createGLLight() const SK_OVERRIDE { + return new GrGLSpotLight(); + } virtual LightType type() const { return kSpot_LightType; } + const SkPoint3& location() const { return fLocation; } + const SkPoint3& target() const { return fTarget; } + SkScalar specularExponent() const { return fSpecularExponent; } + SkScalar cosInnerConeAngle() const { return fCosInnerConeAngle; } + SkScalar cosOuterConeAngle() const { return fCosOuterConeAngle; } + SkScalar coneScale() const { return fConeScale; } + SkPoint3 s() const { return fS; } SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotLight) @@ -423,6 +611,19 @@ protected: writePoint3(fS, buffer); } + virtual bool isEqual(const SkLight& other) SK_OVERRIDE { + if (other.type() != kSpot_LightType) { + return false; + } + + const SkSpotLight& o = static_cast(other); + return INHERITED::isEqual(other) && + fLocation == o.fLocation && + fTarget == o.fTarget && + fSpecularExponent == o.fSpecularExponent && + fCosOuterConeAngle == o.fCosOuterConeAngle; + } + private: typedef SkLight INHERITED; SkPoint3 fLocation; @@ -434,6 +635,8 @@ private: SkPoint3 fS; }; +/////////////////////////////////////////////////////////////////////////////// + SkLightingImageFilter::SkLightingImageFilter(SkLight* light, SkScalar surfaceScale) : fLight(light), fSurfaceScale(SkScalarDiv(surfaceScale, SkIntToScalar(255))) @@ -507,6 +710,8 @@ void SkLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeScalar(fSurfaceScale); } +/////////////////////////////////////////////////////////////////////////////// + SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd) : SkLightingImageFilter(light, surfaceScale), fKD(kd) @@ -557,6 +762,16 @@ bool SkDiffuseLightingImageFilter::onFilterImage(Proxy*, return true; } +bool SkDiffuseLightingImageFilter::asNewCustomStage(GrCustomStage** stage) const { + if (stage) { + SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255)); + *stage = new GrDiffuseLightingEffect(light(), scale, kd()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess) : SkLightingImageFilter(light, surfaceScale), fKS(ks), @@ -610,6 +825,517 @@ bool SkSpecularLightingImageFilter::onFilterImage(Proxy*, return true; } +bool SkSpecularLightingImageFilter::asNewCustomStage(GrCustomStage** stage) const { + if (stage) { + SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255)); + *stage = new GrSpecularLightingEffect(light(), scale, ks(), shininess()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLLightingEffect : public GrGLProgramStage { +public: + GrGLLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual ~GrGLLightingEffect(); + + 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 emitLightFunc(SkString* funcs) = 0; + + static inline StageKey GenKey(const GrCustomStage& s); + + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLProgramStage INHERITED; + + const GrGLShaderVar* fImageIncrementVar; + GrGLint fImageIncrementLocation; + const GrGLShaderVar* fSurfaceScaleVar; + GrGLint fSurfaceScaleLocation; + GrGLLight* fLight; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLDiffuseLightingEffect : public GrGLLightingEffect { +public: + GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitLightFunc(SkString* funcs) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLLightingEffect INHERITED; + + const GrGLShaderVar* fKDVar; + GrGLint fKDLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLSpecularLightingEffect : public GrGLLightingEffect { +public: + GrGLSpecularLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + virtual void setupVariables(GrGLShaderBuilder* state, + int stage) SK_OVERRIDE; + virtual void emitLightFunc(SkString* funcs) SK_OVERRIDE; + virtual void initUniforms(const GrGLInterface*, int programID) SK_OVERRIDE; + virtual void setData(const GrGLInterface*, + const GrGLTexture&, + const GrCustomStage&, + int stageNum) SK_OVERRIDE; + +private: + typedef GrGLLightingEffect INHERITED; + + const GrGLShaderVar* fKSVar; + GrGLint fKSLocation; + const GrGLShaderVar* fShininessVar; + GrGLint fShininessLocation; +}; + +/////////////////////////////////////////////////////////////////////////////// + +GrLightingEffect::GrLightingEffect(const SkLight* light, SkScalar surfaceScale) + : fLight(light) + , fSurfaceScale(surfaceScale) { + fLight->ref(); +} + +GrLightingEffect::~GrLightingEffect() { + fLight->unref(); +} + +bool GrLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrLightingEffect& s = + static_cast(sBase); + return fLight->isEqual(*s.fLight) && + fSurfaceScale == s.fSurfaceScale; +} + +/////////////////////////////////////////////////////////////////////////////// + +GrDiffuseLightingEffect::GrDiffuseLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar kd) + : INHERITED(light, surfaceScale), fKD(kd) { +} + +const GrProgramStageFactory& GrDiffuseLightingEffect::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrDiffuseLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrDiffuseLightingEffect& s = + static_cast(sBase); + return INHERITED::isEqual(sBase) && + this->kd() == s.kd(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrGLLightingEffect::GrGLLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLProgramStage(factory) + , fImageIncrementVar(NULL) + , fImageIncrementLocation(0) + , fSurfaceScaleVar(NULL) + , fSurfaceScaleLocation(0) { + const GrLightingEffect& m = static_cast(stage); + fLight = m.light()->createGLLight(); +} + +GrGLLightingEffect::~GrGLLightingEffect() { + delete fLight; +} + +void GrGLLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + fImageIncrementVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec2f_GrSLType, "uImageIncrement", stage); + fSurfaceScaleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uSurfaceScale", stage); + fLight->setupVariables(state, stage); +} + +void GrGLLightingEffect::emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) { + fLight->emitVS(&state->fVSCode); +} + +void GrGLLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + GR_GL_CALL_RET(gl, fSurfaceScaleLocation, + GetUniformLocation(programID, + fSurfaceScaleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fImageIncrementLocation, + GetUniformLocation(programID, + fImageIncrementVar->getName().c_str())); + fLight->initUniforms(gl, programID); +} + +void GrGLLightingEffect::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + SkString* code = &state->fFSCode; + SkString* funcs = &state->fFSFunctions; + fLight->emitFuncs(funcs); + emitLightFunc(funcs); + funcs->appendf("float sobel(float a, float b, float c, float d, float e, float f, float scale) {\n"); + funcs->appendf("\treturn (-a + b - 2 * c + 2 * d -e + f) * scale;\n"); + funcs->appendf("}\n"); + funcs->appendf("vec3 pointToNormal(float x, float y, float scale) {\n"); + funcs->appendf("\treturn normalize(vec3(-x * scale, -y * scale, 1));\n"); + funcs->appendf("}\n"); + funcs->append("\n\ +vec3 interiorNormal(float m[9], float surfaceScale) {\n\ + return pointToNormal(sobel(m[0], m[2], m[3], m[5], m[6], m[8], 0.25),\n\ + sobel(m[0], m[6], m[1], m[7], m[2], m[8], 0.25),\n\ + surfaceScale);\n}\n"); + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tfloat m[9];\n"); + int index = 0; + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + SkString texCoords; + texCoords.appendf("coord + vec2(%d, %d) * %s", dx, dy, fImageIncrementVar->getName().c_str()); + code->appendf("\t\tm[%d] = ", index++); + state->emitTextureLookup(samplerName, texCoords.c_str()); + code->appendf(".a;\n"); + } + } + code->appendf("\t\tvec3 surfaceToLight = "); + SkString arg; + arg.appendf("%s * m[4]", fSurfaceScaleVar->getName().c_str()); + fLight->emitSurfaceToLight(code, arg.c_str()); + code->appendf(";\n"); + code->appendf("\t\t%s = light(interiorNormal(m, %s), surfaceToLight, ", outputColor, fSurfaceScaleVar->getName().c_str()); + fLight->emitLightColor(code, "surfaceToLight"); + code->appendf(")%s;\n", state->fModulate.c_str()); +} + +GrGLProgramStage::StageKey GrGLLightingEffect::GenKey( + const GrCustomStage& s) { + return static_cast(s).light()->type(); +} + +void GrGLLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + const GrLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform2f(fImageIncrementLocation, 1.0f / texture.width(), 1.0f / texture.height())); + GR_GL_CALL(gl, Uniform1f(fSurfaceScaleLocation, effect.surfaceScale())); + fLight->setData(gl, effect.light()); +} + +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// + +GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : INHERITED(factory, stage) + , fKDVar(NULL) + , fKDLocation(0) { +} + +void GrGLDiffuseLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fKDVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uKD", stage); +} + +void GrGLDiffuseLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fKDLocation, + GetUniformLocation(programID, + fKDVar->getName().c_str())); +} + +void GrGLDiffuseLightingEffect::emitLightFunc(SkString* funcs) { + funcs->appendf("vec4 light(vec3 normal, vec3 surfaceToLight, vec3 lightColor) {\n"); + funcs->appendf("\tfloat colorScale = %s * dot(normal, surfaceToLight);\n", fKDVar->getName().c_str()); + funcs->appendf("\treturn vec4(lightColor * clamp(colorScale, 0, 1), 1);\n"); + funcs->appendf("}\n"); +} + +void GrGLDiffuseLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + INHERITED::setData(gl, texture, data, stageNum); + const GrDiffuseLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform1f(fKDLocation, effect.kd())); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrSpecularLightingEffect::GrSpecularLightingEffect(const SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess) + : INHERITED(light, surfaceScale), + fKS(ks), + fShininess(shininess) { +} + +const GrProgramStageFactory& GrSpecularLightingEffect::getFactory() const { + return GrTProgramStageFactory::getInstance(); +} + +bool GrSpecularLightingEffect::isEqual(const GrCustomStage& sBase) const { + const GrSpecularLightingEffect& s = + static_cast(sBase); + return INHERITED::isEqual(sBase) && + this->ks() == s.ks() && + this->shininess() == s.shininess(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLLightingEffect(factory, stage) + , fKSVar(NULL) + , fKSLocation(0) + , fShininessVar(NULL) + , fShininessLocation(0) { +} + +void GrGLSpecularLightingEffect::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fKSVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uKS", stage); + fShininessVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uShininess", stage); +} + +void GrGLSpecularLightingEffect::initUniforms(const GrGLInterface* gl, + int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fKSLocation, + GetUniformLocation(programID, fKSVar->getName().c_str())); + GR_GL_CALL_RET(gl, fShininessLocation, + GetUniformLocation(programID, fShininessVar->getName().c_str())); +} + +void GrGLSpecularLightingEffect::emitLightFunc(SkString* funcs) { + funcs->appendf("vec4 light(vec3 normal, vec3 surfaceToLight, vec3 lightColor) {\n"); + funcs->appendf("\tvec3 halfDir = vec3(normalize(surfaceToLight + vec3(0, 0, 1)));\n"); + + funcs->appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n", + fKSVar->getName().c_str(), fShininessVar->getName().c_str()); + funcs->appendf("\treturn vec4(lightColor * clamp(colorScale, 0, 1), 1);\n"); + funcs->appendf("}\n"); +} + +void GrGLSpecularLightingEffect::setData(const GrGLInterface* gl, + const GrGLTexture& texture, + const GrCustomStage& data, + int stageNum) { + INHERITED::setData(gl, texture, data, stageNum); + const GrSpecularLightingEffect& effect = + static_cast(data); + GR_GL_CALL(gl, Uniform1f(fKSLocation, effect.ks())); + GR_GL_CALL(gl, Uniform1f(fShininessLocation, effect.shininess())); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLLight::emitLightColor(SkString* builder, const char *surfaceToLight) const { + builder->append(fColorVar->getName().c_str()); +} + +void GrGLLight::setupVariables(GrGLShaderBuilder* state, int stage) { + fColorVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uLightColor", stage); +} + +void GrGLLight::initUniforms(const GrGLInterface* gl, int programID) { + GR_GL_CALL_RET(gl, fColorVarLocation, + GetUniformLocation(programID, fColorVar->getName().c_str())); +} + +void GrGLLight::setData(const GrGLInterface* gl, const SkLight* light) const { + setUniformPoint3(gl, fColorVarLocation, light->color() * SkScalarInvert(SkIntToScalar(255))); +} + +GrGLLight* SkDistantLight::createGLLight() const { + return new GrGLDistantLight(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLDistantLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fDirectionVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType, + "uLightDirection", stage); +} + +void GrGLDistantLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fDirectionLocation, + GetUniformLocation(programID, fDirectionVar->getName().c_str())); +} + +void GrGLDistantLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kDistant_LightType); + const SkDistantLight* distantLight = static_cast(light); + setUniformPoint3(gl, fDirectionLocation, distantLight->direction()); +} + +void GrGLDistantLight::emitSurfaceToLight(SkString* builder, + const char* z) const { + builder->append(fDirectionVar->getName().c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLPointLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fLocationVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType, + "uLightLocation", stage); + state->addVarying(kFloat_GrSLType, "Height", stage, &fHeightVaryingName); +} + +void GrGLPointLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fLocationLocation, + GetUniformLocation(programID, fLocationVar->getName().c_str())); +} + +void GrGLPointLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kPoint_LightType); + const SkPointLight* pointLight = static_cast(light); + setUniformPoint3(gl, fLocationLocation, pointLight->location()); +} + +void GrGLPointLight::emitVS(SkString* builder) const { + // Compute viewport height from the Y scale of the matrix. + builder->appendf("\t\t%s = -2.0 / uViewM[1][1];\n", fHeightVaryingName); +} + +void GrGLPointLight::emitSurfaceToLight(SkString* builder, + const char* z) const { + builder->appendf( + "normalize(%s - vec3(gl_FragCoord.x, %s - gl_FragCoord.y, %s))", + fLocationVar->getName().c_str(), fHeightVaryingName, z); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrGLSpotLight::setupVariables(GrGLShaderBuilder* state, int stage) { + INHERITED::setupVariables(state, stage); + fLocationVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uLightLocation", stage); + fExponentVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uExponent", stage); + fCosInnerConeAngleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uCosInnerConeAngle", stage); + fCosOuterConeAngleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uCosOuterConeAngle", stage); + fConeScaleVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "uConeScale", stage); + fSVar = &state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType, + kVec3f_GrSLType, "uS", stage); + state->addVarying(kFloat_GrSLType, "Height", stage, &fHeightVaryingName); +} + +void GrGLSpotLight::initUniforms(const GrGLInterface* gl, int programID) { + INHERITED::initUniforms(gl, programID); + GR_GL_CALL_RET(gl, fLocationLocation, + GetUniformLocation(programID, fLocationVar->getName().c_str())); + GR_GL_CALL_RET(gl, fExponentLocation, + GetUniformLocation(programID, fExponentVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosInnerConeAngleLocation, + GetUniformLocation(programID, fCosInnerConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosOuterConeAngleLocation, + GetUniformLocation(programID, fCosOuterConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fCosOuterConeAngleLocation, + GetUniformLocation(programID, fCosOuterConeAngleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fConeScaleLocation, + GetUniformLocation(programID, fConeScaleVar->getName().c_str())); + GR_GL_CALL_RET(gl, fSLocation, + GetUniformLocation(programID, fSVar->getName().c_str())); +} + +void GrGLSpotLight::setData(const GrGLInterface* gl, const SkLight* light) const { + INHERITED::setData(gl, light); + SkASSERT(light->type() == SkLight::kSpot_LightType); + const SkSpotLight* spotLight = static_cast(light); + setUniformPoint3(gl, fLocationLocation, spotLight->location()); + GR_GL_CALL(gl, Uniform1f(fExponentLocation, spotLight->specularExponent())); + GR_GL_CALL(gl, Uniform1f(fCosInnerConeAngleLocation, spotLight->cosInnerConeAngle())); + GR_GL_CALL(gl, Uniform1f(fCosOuterConeAngleLocation, spotLight->cosOuterConeAngle())); + GR_GL_CALL(gl, Uniform1f(fConeScaleLocation, spotLight->coneScale())); + setUniformPoint3(gl, fSLocation, spotLight->s()); +} + +void GrGLSpotLight::emitVS(SkString* builder) const { + // Compute viewport height from the Y scale of the matrix. + builder->appendf("\t\t%s = -2.0 / uViewM[1][1];\n", fHeightVaryingName); +} + +void GrGLSpotLight::emitFuncs(SkString* builder) const { + builder->appendf("vec3 lightColor(vec3 surfaceToLight) {\n"); + builder->appendf("\tfloat cosAngle = -dot(surfaceToLight, %s);\n", fSVar->getName().c_str()); + builder->appendf("\tif (cosAngle < %s) {\n", fCosOuterConeAngleVar->getName().c_str()); + builder->appendf("\t\treturn vec3(0);\n"); + builder->appendf("\t}\n"); + builder->appendf("\tfloat scale = pow(cosAngle, %s);\n", fExponentVar->getName().c_str()); + builder->appendf("\tif (cosAngle < %s) {\n", fCosInnerConeAngleVar->getName().c_str()); + builder->appendf("\t\treturn %s * scale * (cosAngle - %s) * %s;\n", fColorVar->getName().c_str(), fCosOuterConeAngleVar->getName().c_str(), fConeScaleVar->getName().c_str()); + builder->appendf("\t}\n"); + builder->appendf("\treturn %s;\n", fColorVar->getName().c_str()); + builder->appendf("}\n"); +} + +void GrGLSpotLight::emitSurfaceToLight(SkString* builder, const char* z) const { + builder->appendf("normalize(%s - vec3(gl_FragCoord.x, %s - gl_FragCoord.y, %s))", fLocationVar->getName().c_str(), fHeightVaryingName, z); +} + +void GrGLSpotLight::emitLightColor(SkString* builder, const char *surfaceToLight) const { + builder->appendf("lightColor(%s)", surfaceToLight); +} + SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiffuseLightingImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpecularLightingImageFilter) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index fc73f0d02..e5227a275 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1424,6 +1424,33 @@ void SkGpuDevice::internalDrawBitmap(const SkDraw& draw, fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m); } +namespace { + +void apply_custom_stage(GrContext* context, + GrTexture* srcTexture, + GrTexture* dstTexture, + const GrRect& rect, + GrCustomStage* stage) { + SkASSERT(srcTexture && srcTexture->getContext() == context); + GrAutoMatrix avm(context, GrMatrix::I()); + GrContext::AutoRenderTarget art(context, dstTexture->asRenderTarget()); + GrClip oldClip = context->getClip(); + context->setClip(rect); + + GrMatrix sampleM; + sampleM.setIDiv(srcTexture->width(), srcTexture->height()); + GrPaint paint; + paint.reset(); + paint.textureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter); + paint.textureSampler(0)->reset(sampleM); + paint.textureSampler(0)->setCustomStage(stage); + paint.setTexture(0, srcTexture); + context->drawRect(paint, rect); + context->setClip(oldClip); +} + +}; + static GrTexture* filter_texture(GrContext* context, GrTexture* texture, SkImageFilter* filter, const GrRect& rect) { GrAssert(filter); @@ -1436,8 +1463,14 @@ static GrTexture* filter_texture(GrContext* context, GrTexture* texture, desc.fWidth = SkScalarCeilToInt(rect.width()); desc.fHeight = SkScalarCeilToInt(rect.height()); desc.fConfig = kRGBA_8888_PM_GrPixelConfig; + GrCustomStage* stage; - if (filter->asABlur(&blurSize)) { + if (filter->asNewCustomStage(&stage)) { + GrAutoScratchTexture dst(context, desc); + apply_custom_stage(context, texture, dst.texture(), rect, stage); + texture = dst.detach(); + stage->unref(); + } else if (filter->asABlur(&blurSize)) { GrAutoScratchTexture temp1, temp2; texture = context->gaussianBlur(texture, &temp1, &temp2, rect, blurSize.width(), @@ -1564,7 +1597,11 @@ void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device, bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) { SkSize size; SkISize radius; - if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) { + + if (!filter->asNewCustomStage(NULL) && + !filter->asABlur(&size) && + !filter->asADilate(&radius) && + !filter->asAnErode(&radius)) { return false; } return true; diff --git a/src/gpu/gl/GrGpuGL_unittest.cpp b/src/gpu/gl/GrGpuGL_unittest.cpp index 7f71f4820..82804450b 100644 --- a/src/gpu/gl/GrGpuGL_unittest.cpp +++ b/src/gpu/gl/GrGpuGL_unittest.cpp @@ -3,6 +3,7 @@ #include "effects/GrConvolutionEffect.h" #include "effects/GrGradientEffects.h" #include "effects/GrMorphologyEffect.h" +#include "SkLightingImageFilter.h" #include "GrProgramStageFactory.h" #include "GrRandom.h" @@ -23,6 +24,10 @@ bool random_bool(GrRandom* r) { return r->nextF() > .5f; } +SkPoint3 random_point3(GrRandom* r) { + return SkPoint3(r->nextF(), r->nextF(), r->nextF()); +} + typedef GrGLProgram::StageDesc StageDesc; // TODO: Effects should be able to register themselves for inclusion in the // randomly generated shaders. They should be able to configure themselves @@ -35,6 +40,12 @@ GrCustomStage* create_random_effect(StageDesc* stageDesc, kDilate_EffectType, kRadialGradient_EffectType, kRadial2Gradient_EffectType, + kDiffuseDistant_EffectType, + kDiffusePoint_EffectType, + kDiffuseSpot_EffectType, + kSpecularDistant_EffectType, + kSpecularPoint_EffectType, + kSpecularSpot_EffectType, kSweepGradient_EffectType, kEffectCount @@ -107,6 +118,86 @@ GrCustomStage* create_random_effect(StageDesc* stageDesc, case kSweepGradient_EffectType: { return SkNEW(GrSweepGradient); } + case kDiffuseDistant_EffectType: { + SkPoint3 direction = random_point3(random); + direction.normalize(); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar kd = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateDistantLitDiffuse(direction, lightColor, surfaceScale, kd)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kDiffusePoint_EffectType: { + SkPoint3 location = random_point3(random); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar kd = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreatePointLitDiffuse(location, lightColor, surfaceScale, kd)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kDiffuseSpot_EffectType: { + SkPoint3 location = random_point3(random); + SkPoint3 target = random_point3(random); + SkScalar cutoffAngle = SkFloatToScalar(random->nextF()); + SkScalar specularExponent = SkFloatToScalar(random->nextF()); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateSpotLitSpecular( + location, target, specularExponent, cutoffAngle, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularDistant_EffectType: { + SkPoint3 direction = random_point3(random); + direction.normalize(); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateDistantLitSpecular(direction, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularPoint_EffectType: { + SkPoint3 location = random_point3(random); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreatePointLitSpecular(location, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } + case kSpecularSpot_EffectType: { + SkPoint3 location = random_point3(random); + SkPoint3 target = random_point3(random); + SkScalar cutoffAngle = SkFloatToScalar(random->nextF()); + SkScalar specularExponent = SkFloatToScalar(random->nextF()); + SkColor lightColor = random->nextU(); + SkScalar surfaceScale = SkFloatToScalar(random->nextF()); + SkScalar ks = SkFloatToScalar(random->nextF()); + SkScalar shininess = SkFloatToScalar(random->nextF()); + SkAutoTUnref filter(SkLightingImageFilter::CreateSpotLitSpecular( + location, target, specularExponent, cutoffAngle, lightColor, surfaceScale, ks, shininess)); + // does not work with perspective or mul-by-alpha-mask + GrCustomStage* stage; + SkASSERT(filter->asNewCustomStage(&stage)); + return stage; + } default: GrCrash("Unexpected custom effect type"); }