summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/core/SkGaussFilter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/core/SkGaussFilter.cpp')
-rw-r--r--gfx/skia/skia/src/core/SkGaussFilter.cpp109
1 files changed, 109 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/core/SkGaussFilter.cpp b/gfx/skia/skia/src/core/SkGaussFilter.cpp
new file mode 100644
index 0000000000..5cbc93705c
--- /dev/null
+++ b/gfx/skia/skia/src/core/SkGaussFilter.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "include/core/SkTypes.h"
+#include "include/private/base/SkFloatingPoint.h"
+#include "src/core/SkGaussFilter.h"
+#include <cmath>
+
+// The value when we can stop expanding the filter. The spec implies that 3% is acceptable, but
+// we just use 1%.
+static constexpr double kGoodEnough = 1.0 / 100.0;
+
+// Normalize the values of gauss to 1.0, and make sure they add to one.
+// NB if n == 1, then this will force gauss[0] == 1.
+static void normalize(int n, double* gauss) {
+ // Carefully add from smallest to largest to calculate the normalizing sum.
+ double sum = 0;
+ for (int i = n-1; i >= 1; i--) {
+ sum += 2 * gauss[i];
+ }
+ sum += gauss[0];
+
+ // Normalize gauss.
+ for (int i = 0; i < n; i++) {
+ gauss[i] /= sum;
+ }
+
+ // The factors should sum to 1. Take any remaining slop, and add it to gauss[0]. Add the
+ // values in such a way to maintain the most accuracy.
+ sum = 0;
+ for (int i = n - 1; i >= 1; i--) {
+ sum += 2 * gauss[i];
+ }
+
+ gauss[0] = 1 - sum;
+}
+
+static int calculate_bessel_factors(double sigma, double *gauss) {
+ auto var = sigma * sigma;
+
+ // The two functions below come from the equations in "Handbook of Mathematical Functions"
+ // by Abramowitz and Stegun. Specifically, equation 9.6.10 on page 375. Bessel0 is given
+ // explicitly as 9.6.12
+ // BesselI_0 for 0 <= sigma < 2.
+ // NB the k = 0 factor is just sum = 1.0.
+ auto besselI_0 = [](double t) -> double {
+ auto tSquaredOver4 = t * t / 4.0;
+ auto sum = 1.0;
+ auto factor = 1.0;
+ auto k = 1;
+ // Use a variable number of loops. When sigma is small, this only requires 3-4 loops, but
+ // when sigma is near 2, it could require 10 loops. The same holds for BesselI_1.
+ while(factor > 1.0/1000000.0) {
+ factor *= tSquaredOver4 / (k * k);
+ sum += factor;
+ k += 1;
+ }
+ return sum;
+ };
+ // BesselI_1 for 0 <= sigma < 2.
+ auto besselI_1 = [](double t) -> double {
+ auto tSquaredOver4 = t * t / 4.0;
+ auto sum = t / 2.0;
+ auto factor = sum;
+ auto k = 1;
+ while (factor > 1.0/1000000.0) {
+ factor *= tSquaredOver4 / (k * (k + 1));
+ sum += factor;
+ k += 1;
+ }
+ return sum;
+ };
+
+ // The following formula for calculating the Gaussian kernel is from
+ // "Scale-Space for Discrete Signals" by Tony Lindeberg.
+ // gauss(n; var) = besselI_n(var) / (e^var)
+ auto d = std::exp(var);
+ double b[SkGaussFilter::kGaussArrayMax] = {besselI_0(var), besselI_1(var)};
+ gauss[0] = b[0]/d;
+ gauss[1] = b[1]/d;
+
+ // The code below is tricky, and written to mirror the recursive equations from the book.
+ // The maximum spread for sigma == 2 is guass[4], but in order to know to stop guass[5]
+ // is calculated. At this point n == 5 meaning that gauss[0..4] are the factors, but a 6th
+ // element was used to calculate them.
+ int n = 1;
+ // The recurrence relation below is from "Numerical Recipes" 3rd Edition.
+ // Equation 6.5.16 p.282
+ while (gauss[n] > kGoodEnough) {
+ b[n+1] = -(2*n/var) * b[n] + b[n-1];
+ gauss[n+1] = b[n+1] / d;
+ n += 1;
+ }
+
+ normalize(n, gauss);
+
+ return n;
+}
+
+SkGaussFilter::SkGaussFilter(double sigma) {
+ SkASSERT(0 <= sigma && sigma < 2);
+
+ fN = calculate_bessel_factors(sigma, fBasis);
+}