Index: src/core/SkNormalBevelSource.cpp |
diff --git a/src/core/SkNormalBevelSource.cpp b/src/core/SkNormalBevelSource.cpp |
index a63e434c3fb1fe7f01162735d8ac7ebbd11dc330..04faa0006fa15d42d0d0e85ab490492dcc98792d 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,68 @@ 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); |
+ // Determining necessary uniforms and initializing them |
+ 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); |
+ } |
- fragBuilder->codeAppendf("%s = vec4(0.0, 0.0, 1.0, 0.0);", args.fOutputColor); |
+ 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); |
+ } |
+ |
+ // 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()); |
+ |
+ // Asserting presence of necessary uniforms |
+ SkASSERT(widthUniName); |
+ |
+ 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 +116,92 @@ 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: |
+ // Asserting presence of necessary uniforms |
+ SkASSERT(normalizedHeight); |
+ SkASSERT(normalizedWidth); |
+ |
+ // 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);", |
+ normalizedHeight, normalizedWidth); |
+ break; |
+ case SkNormalSource::BevelType::kRoundedOut: |
+ // Fall through |
+ case SkNormalSource::BevelType::kRoundedIn: |
+ // Asserting presence of necessary uniforms |
+ SkASSERT(height); |
+ SkASSERT(width); |
+ |
+ // 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 rootDOverW = sqrt(currentPos_d/%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-rootDOverW), " |
+ "%s*rootDOverW);", |
+ 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 +211,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 +233,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 |