From 1b9e2fb49415d8dc41e449bee5f8ebec6f616d71 Mon Sep 17 00:00:00 2001 From: dvonbeck Date: Thu, 4 Aug 2016 12:27:26 -0700 Subject: [PATCH] This CL's base is the CL that sets up the distance vector field req. exposure: https://codereview.chromium.org/2114993002/ BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2151653002 Review-Url: https://codereview.chromium.org/2151653002 --- src/core/SkNormalBevelSource.cpp | 190 +++++++++++++++++++++++++++++++++------ 1 file changed, 165 insertions(+), 25 deletions(-) diff --git a/src/core/SkNormalBevelSource.cpp b/src/core/SkNormalBevelSource.cpp index a63e434..04faa00 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(); fUsesDistanceVectorField = true; @@ -39,23 +47,68 @@ public: void onEmitCode(EmitArgs& args) override { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + const NormalBevelFP& fp = args.fFp.cast(); 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(); - b->add32(static_cast(fp.fType)); + b->add32(static_cast(fp.fBevelType)); } protected: @@ -63,13 +116,92 @@ public: const GrProcessor& proc) override { const NormalBevelFP& normalBevelFP = proc.cast(); - 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() + GrGLSLProgramDataManager::UniformHandle fNormalizedWidthUni; + // height / length() + 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(); - 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 SkNormalBevelSourceImpl::asFragmentProcessor( - const SkShader::AsFPArgs&) const { + const SkShader::AsFPArgs& args) const { + + SkScalar maxScale = args.fViewMatrix->getMaxScale(); - return sk_make_sp(fType, fWidth, fHeight); + // Providing device-space width and height + return sk_make_sp(fType, maxScale * fWidth, maxScale * fHeight); } #endif // SK_SUPPORT_GPU -- 2.7.4