| 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
|
|
|