This CL's base is the CL that sets up the distance vector field req. exposure: https...
authordvonbeck <dvonbeck@google.com>
Thu, 4 Aug 2016 19:27:26 +0000 (12:27 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 4 Aug 2016 19:27:26 +0000 (12:27 -0700)
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2151653002

Review-Url: https://codereview.chromium.org/2151653002

src/core/SkNormalBevelSource.cpp

index a63e434..04faa00 100644 (file)
 #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