Chromium Code Reviews| Index: src/core/SkNormalBevelSource.cpp |
| diff --git a/src/core/SkNormalBevelSource.cpp b/src/core/SkNormalBevelSource.cpp |
| index a63e434c3fb1fe7f01162735d8ac7ebbd11dc330..a1a66ae75d4a94f716dab7b4cdd15a97c7250edd 100644 |
| --- a/src/core/SkNormalBevelSource.cpp |
| +++ b/src/core/SkNormalBevelSource.cpp |
| @@ -19,12 +19,20 @@ |
| #include "glsl/GrGLSLFragmentShaderBuilder.h" |
| #include "SkGr.h" |
| +/** \class NormalBevelFP |
| + * |
| + * Fragment processor for the SkNormalBevelSource. |
| + * |
| + * @param bevelType type of the bevel |
| + * @param bevelWidth width of the bevel in device space |
| + * @param bevelHeight height of the bevel in device space |
| + */ |
| class NormalBevelFP : public GrFragmentProcessor { |
| public: |
| - NormalBevelFP(SkNormalSource::BevelType type, SkScalar width, SkScalar height) |
| - : fType(type) |
| - , fWidth(width) |
| - , fHeight(height) { |
| + NormalBevelFP(SkNormalSource::BevelType bevelType, SkScalar bevelWidth, SkScalar bevelHeight) |
| + : fBevelType(bevelType) |
| + , fBevelWidth(bevelWidth) |
| + , fBevelHeight(bevelHeight) { |
| this->initClassID<NormalBevelFP>(); |
| fUsesDistanceVectorField = true; |
| @@ -39,23 +47,64 @@ public: |
| void onEmitCode(EmitArgs& args) override { |
| GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
| + const NormalBevelFP& fp = args.fFp.cast<NormalBevelFP>(); |
| GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
| - const char* widthUniName = nullptr; |
| - fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, |
| - kDefault_GrSLPrecision, "Width", &widthUniName); |
| + bool needWidth = true; |
| + bool needHeight = (fp.fBevelType == SkNormalSource::BevelType::kRoundedOut || |
| + fp.fBevelType == SkNormalSource::BevelType::kRoundedIn); |
| + bool needNormalized = (fp.fBevelType == SkNormalSource::BevelType::kLinear); |
| + |
| + const char *widthUniName = nullptr; |
| + if (needWidth) { |
| + fWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, |
| + kDefault_GrSLPrecision, "Width", |
| + &widthUniName); |
| + } |
| const char* heightUniName = nullptr; |
| - fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, |
| - kDefault_GrSLPrecision, "Height", &heightUniName); |
| + if (needHeight) { |
| + fHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, |
| + kDefault_GrSLPrecision, "Height", |
| + &heightUniName); |
| + } |
| + |
| + const char* normalizedWidthUniName = nullptr; |
| + const char* normalizedHeightUniName = nullptr; |
| + if (needNormalized) { |
| + fNormalizedWidthUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| + kFloat_GrSLType, |
| + kDefault_GrSLPrecision, |
| + "NormalizedWidth", |
| + &normalizedWidthUniName); |
| + fNormalizedHeightUni = uniformHandler->addUniform(kFragment_GrShaderFlag, |
| + kFloat_GrSLType, |
| + kDefault_GrSLPrecision, |
| + "NormalizedHeight", |
| + &normalizedHeightUniName); |
| + } |
| - fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 1.0, 0.0);", args.fOutputColor); |
| + // Here we are splitting the distance vector into length and normalized direction |
| + // TODO: Output these values from the geometry processor frag code instead of the vector |
| + fragBuilder->codeAppendf("float dv_length = length(%s);", |
| + fragBuilder->distanceVectorName()); |
| + fragBuilder->codeAppendf("vec2 dv_norm = normalize(%s);", |
| + fragBuilder->distanceVectorName()); |
| + |
| + fragBuilder->codeAppend( "vec3 normal;"); |
| + fragBuilder->codeAppendf("if (dv_length >= %s) {", widthUniName); |
| + fragBuilder->codeAppend( " normal = vec3(0.0, 0.0, 1.0);"); |
| + fragBuilder->codeAppend( "} else {"); |
| + this->emitMath(fragBuilder, fp.fBevelType, widthUniName, heightUniName, |
| + normalizedWidthUniName, normalizedHeightUniName); |
| + fragBuilder->codeAppend( "}"); |
| + fragBuilder->codeAppendf("%s = vec4(normal, 0.0);", args.fOutputColor); |
| } |
| static void GenKey(const GrProcessor& proc, const GrGLSLCaps&, |
| GrProcessorKeyBuilder* b) { |
| const NormalBevelFP& fp = proc.cast<NormalBevelFP>(); |
| - b->add32(static_cast<int>(fp.fType)); |
| + b->add32(static_cast<int>(fp.fBevelType)); |
| } |
| protected: |
| @@ -63,13 +112,84 @@ public: |
| const GrProcessor& proc) override { |
| const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>(); |
| - if (fPrevWidth != normalBevelFP.fWidth) { |
| - pdman.set1f(fWidthUni, normalBevelFP.fWidth); |
| - fPrevWidth = normalBevelFP.fWidth; |
| + // Updating uniform if bevel type requires it and data has changed |
| + |
| + bool needWidth = true; |
| + bool needHeight = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedOut || |
| + normalBevelFP.fBevelType == SkNormalSource::BevelType::kRoundedIn); |
| + bool needNormalized = (normalBevelFP.fBevelType == SkNormalSource::BevelType::kLinear); |
| + |
| + bool dirtyWidth = (fPrevWidth != normalBevelFP.fBevelWidth); |
| + bool dirtyHeight = (fPrevHeight != normalBevelFP.fBevelHeight); |
| + bool dirtyNormalized = (dirtyHeight || dirtyWidth); |
| + |
| + |
| + if (needWidth && dirtyWidth) { |
| + pdman.set1f(fWidthUni, normalBevelFP.fBevelWidth); |
| + fPrevWidth = normalBevelFP.fBevelWidth; |
| } |
| - if (fPrevHeight != normalBevelFP.fHeight) { |
| - pdman.set1f(fHeightUni, normalBevelFP.fHeight); |
| - fPrevHeight = normalBevelFP.fHeight; |
| + if (needHeight && dirtyHeight) { |
| + pdman.set1f(fHeightUni, normalBevelFP.fBevelHeight); |
| + fPrevHeight = normalBevelFP.fBevelHeight; |
| + } |
| + if (needNormalized && dirtyNormalized) { |
| + SkScalar height = normalBevelFP.fBevelHeight; |
| + SkScalar width = normalBevelFP.fBevelWidth; |
| + |
| + SkScalar length = SkScalarSqrt(SkScalarSquare(height) + SkScalarSquare(width)); |
| + pdman.set1f(fNormalizedHeightUni, height/length); |
| + pdman.set1f(fNormalizedWidthUni, width/length); |
| + } |
| + } |
| + |
| + // This method emits the code that calculates the normal orthgonal to the simulated beveled |
| + // surface. In the comments inside the function, the math involved is described. For this |
| + // purpose, the d-axis is defined to be the axis co-linear to the distance vector, where the |
| + // origin is the end of the bevel inside the shape. |
| + void emitMath(GrGLSLFPFragmentBuilder* fb, SkNormalSource::BevelType type, |
| + const char* width, const char* height, const char* normalizedWidth, |
| + const char* normalizedHeight) { |
| + switch (type) { |
| + case SkNormalSource::BevelType::kLinear: |
| + // Because the slope of the bevel is -height/width, the vector |
| + // normalized(vec2(height, width)) is the d- and z-components of the normal |
| + // vector that is orthogonal to the linear bevel. Multiplying the d-component |
| + // to the normalized distance vector splits it into x- and y-components. |
| + fb->codeAppendf("normal = vec3(%s * dv_norm, %s);", |
|
egdaniel
2016/08/04 18:36:19
for sanity lets assert normailizedHeight and norma
|
| + normalizedHeight, normalizedWidth); |
| + break; |
| + case SkNormalSource::BevelType::kRoundedOut: |
| + // Fall through |
| + case SkNormalSource::BevelType::kRoundedIn: |
|
egdaniel
2016/08/04 18:36:20
assert the height in here
|
| + // Setting the current position in the d-axis to the distance from the end of |
| + // the bevel as opposed to the beginning if the bevel is rounded in, essentially |
| + // flipping the bevel calculations. |
| + if ( type == SkNormalSource::BevelType::kRoundedIn ) { |
| + fb->codeAppendf("float currentPos_d = %s - dv_length;", width); |
| + } else if (type == SkNormalSource::BevelType::kRoundedOut) { |
| + fb->codeAppendf("float currentPos_d = dv_length;"); |
| + } |
| + |
| + fb->codeAppendf("float rootTOverW = sqrt(t/%s);", width); |
| + |
| + // Calculating the d- and z-components of the normal, where 'd' is the axis |
| + // co-linear to the distance vector. Equation was derived from the formula for |
| + // a bezier curve by solving the parametric equation for d(t) and z(t), then |
| + // with those, calculate d'(t), z'(t) and t(d), and from these, d'(d) and z'(d). |
| + // z'(d)/d'(d) results in the slope of the bevel at d, so we construct an |
| + // orthogonal vector of slope -d'(d)/z'(d) and length 1. |
| + fb->codeAppendf("vec2 unnormalizedNormal_dz = vec2(%s*(1.0-rootTOverW), " |
| + "%s*rootTOverW);", |
| + height, width); |
| + fb->codeAppendf("vec2 normal_dz = normalize(unnormalizedNormal_dz);"); |
| + |
| + // Multiplying the d-component to the normalized distance vector splits it into |
| + // x- and y-components. |
| + fb->codeAppendf("normal = vec3(normal_dz.x*dv_norm, normal_dz.y);"); |
| + |
| + break; |
| + default: |
| + SkDEBUGFAIL("Invalid bevel type passed to emitMath"); |
| } |
| } |
| @@ -79,6 +199,11 @@ public: |
| SkScalar fPrevHeight; |
| GrGLSLProgramDataManager::UniformHandle fHeightUni; |
| + |
| + // width / length(<width,height>) |
| + GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni; |
| + // height / length(<width,height>) |
| + GrGLSLProgramDataManager::UniformHandle fNormalizedHeightUni; |
| }; |
| void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { |
| @@ -96,20 +221,23 @@ private: |
| bool onIsEqual(const GrFragmentProcessor& proc) const override { |
| const NormalBevelFP& normalBevelFP = proc.cast<NormalBevelFP>(); |
| - return fType == normalBevelFP.fType && |
| - fWidth == normalBevelFP.fWidth && |
| - fHeight == normalBevelFP.fHeight; |
| + return fBevelType == normalBevelFP.fBevelType && |
| + fBevelWidth == normalBevelFP.fBevelWidth && |
| + fBevelHeight == normalBevelFP.fBevelHeight; |
| } |
| - SkNormalSource::BevelType fType; |
| - SkScalar fWidth; |
| - SkScalar fHeight; |
| + SkNormalSource::BevelType fBevelType; |
| + SkScalar fBevelWidth; |
| + SkScalar fBevelHeight; |
| }; |
| sk_sp<GrFragmentProcessor> SkNormalBevelSourceImpl::asFragmentProcessor( |
| - const SkShader::AsFPArgs&) const { |
| + const SkShader::AsFPArgs& args) const { |
| + |
| + SkScalar maxScale = args.fViewMatrix->getMaxScale(); |
| - return sk_make_sp<NormalBevelFP>(fType, fWidth, fHeight); |
| + // Providing device-space width and height |
| + return sk_make_sp<NormalBevelFP>(fType, maxScale * fWidth, maxScale * fHeight); |
| } |
| #endif // SK_SUPPORT_GPU |