Fix degenerate case handling in fill_in_2D_gaussian_kernel

Bug: 849016
Change-Id: Icc46451acd83140cb86593195947efea7f83828d
Reviewed-on: https://skia-review.googlesource.com/135142
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Herb Derby <herb@google.com>
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index ec2126d..c2c1f7c 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -213,19 +213,58 @@
            fDomain == s.domain();
 }
 
+static void fill_in_1D_gaussian_kernel_with_stride(float* kernel, int size, int stride,
+                                                   float twoSigmaSqrd) {
+    SkASSERT(!SkScalarNearlyZero(twoSigmaSqrd, SK_ScalarNearlyZero));
+
+    const float sigmaDenom = 1.0f / twoSigmaSqrd;
+    const int radius = size / 2;
+
+    float sum = 0.0f;
+    for (int i = 0; i < size; ++i) {
+        float term = static_cast<float>(i - radius);
+        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
+        // is dropped here, since we renormalize the kernel below.
+        kernel[i * stride] = sk_float_exp(-term * term * sigmaDenom);
+        sum += kernel[i * stride];
+    }
+    // Normalize the kernel
+    float scale = 1.0f / sum;
+    for (int i = 0; i < size; ++i) {
+        kernel[i * stride] *= scale;
+    }
+}
+
 static void fill_in_2D_gaussian_kernel(float* kernel, int width, int height,
                                        SkScalar sigmaX, SkScalar sigmaY) {
     SkASSERT(width * height <= MAX_KERNEL_SIZE);
     const float twoSigmaSqrdX = 2.0f * SkScalarToFloat(SkScalarSquare(sigmaX));
     const float twoSigmaSqrdY = 2.0f * SkScalarToFloat(SkScalarSquare(sigmaY));
 
+    // TODO: in all of these degenerate cases we're uploading (and using) a whole lot of zeros.
     if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero) ||
         SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero)) {
-        SkASSERT(3 == width && 3 == height);
-        for (int i = 0; i < width * height; ++i) {
-            kernel[i] = 0.0f;
+        // In this case the 2D Gaussian degenerates to a 1D Gaussian (in X or Y) or a point
+        SkASSERT(3 == width || 3 == height);
+        memset(kernel, 0, width*height*sizeof(float));
+
+        if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero) &&
+            SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero)) {
+            // A point
+            SkASSERT(3 == width && 3 == height);
+            kernel[4] = 1.0f;
+        } else if (SkScalarNearlyZero(twoSigmaSqrdX, SK_ScalarNearlyZero)) {
+            // A 1D Gaussian in Y
+            SkASSERT(3 == width);
+            // Down the middle column of the kernel with a stride of width
+            fill_in_1D_gaussian_kernel_with_stride(&kernel[1], height, width, twoSigmaSqrdY);
+        } else {
+            // A 1D Gaussian in X
+            SkASSERT(SkScalarNearlyZero(twoSigmaSqrdY, SK_ScalarNearlyZero));
+            SkASSERT(3 == height);
+            // Down the middle row of the kernel with a stride of 1
+            fill_in_1D_gaussian_kernel_with_stride(&kernel[width], width, 1, twoSigmaSqrdX);
         }
-        kernel[4] = 1.0f;
         return;
     }