1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 9 #include "SkGaussFilter.h" 10 11 #include <cmath> 12 #include "SkTypes.h" 13 14 static constexpr double kPi = 3.14159265358979323846264338327950288; 15 16 // The value when we can stop expanding the filter. The spec implies that 3% is acceptable, but 17 // we just use 1%. 18 static constexpr double kGoodEnough = 1.0 / 100.0; 19 20 // Normalize the values of gauss to 1.0, and make sure they add to one. 21 // NB if n == 1, then this will force gauss[0] == 1. 22 static void normalize(int n, double* gauss) { 23 // Carefully add from smallest to largest to calculate the normalizing sum. 24 double sum = 0; 25 for (int i = n-1; i >= 1; i--) { 26 sum += 2 * gauss[i]; 27 } 28 sum += gauss[0]; 29 30 // Normalize gauss. 31 for (int i = 0; i < n; i++) { 32 gauss[i] /= sum; 33 } 34 35 // The factors should sum to 1. Take any remaining slop, and add it to gauss[0]. Add the 36 // values in such a way to maintain the most accuracy. 37 sum = 0; 38 for (int i = n - 1; i >= 1; i--) { 39 sum += 2 * gauss[i]; 40 } 41 42 gauss[0] = 1 - sum; 43 } 44 45 static int calculate_bessel_factors(double sigma, double *gauss) { 46 auto var = sigma * sigma; 47 48 // The two functions below come from the equations in "Handbook of Mathematical Functions" 49 // by Abramowitz and Stegun. Specifically, equation 9.6.10 on page 375. Bessel0 is given 50 // explicitly as 9.6.12 51 // BesselI_0 for 0 <= sigma < 2. 52 // NB the k = 0 factor is just sum = 1.0. 53 auto besselI_0 = [](double t) -> double { 54 auto tSquaredOver4 = t * t / 4.0; 55 auto sum = 1.0; 56 auto factor = 1.0; 57 auto k = 1; 58 // Use a variable number of loops. When sigma is small, this only requires 3-4 loops, but 59 // when sigma is near 2, it could require 10 loops. The same holds for BesselI_1. 60 while(factor > 1.0/1000000.0) { 61 factor *= tSquaredOver4 / (k * k); 62 sum += factor; 63 k += 1; 64 } 65 return sum; 66 }; 67 // BesselI_1 for 0 <= sigma < 2. 68 auto besselI_1 = [](double t) -> double { 69 auto tSquaredOver4 = t * t / 4.0; 70 auto sum = t / 2.0; 71 auto factor = sum; 72 auto k = 1; 73 while (factor > 1.0/1000000.0) { 74 factor *= tSquaredOver4 / (k * (k + 1)); 75 sum += factor; 76 k += 1; 77 } 78 return sum; 79 }; 80 81 // The following formula for calculating the Gaussian kernel is from 82 // "Scale-Space for Discrete Signals" by Tony Lindeberg. 83 // gauss(n; var) = besselI_n(var) / (e^var) 84 auto d = std::exp(var); 85 double b[SkGaussFilter::kGaussArrayMax] = {besselI_0(var), besselI_1(var)}; 86 gauss[0] = b[0]/d; 87 gauss[1] = b[1]/d; 88 89 // The code below is tricky, and written to mirror the recursive equations from the book. 90 // The maximum spread for sigma == 2 is guass[4], but in order to know to stop guass[5] 91 // is calculated. At this point n == 5 meaning that gauss[0..4] are the factors, but a 6th 92 // element was used to calculate them. 93 int n = 1; 94 // The recurrence relation below is from "Numerical Recipes" 3rd Edition. 95 // Equation 6.5.16 p.282 96 while (gauss[n] > kGoodEnough) { 97 b[n+1] = -(2*n/var) * b[n] + b[n-1]; 98 gauss[n+1] = b[n+1] / d; 99 n += 1; 100 } 101 102 normalize(n, gauss); 103 104 return n; 105 } 106 107 static int calculate_gauss_factors(double sigma, double* gauss) { 108 SkASSERT(0 <= sigma && sigma < 2); 109 110 // From the SVG blur spec: 8.13 Filter primitive <feGaussianBlur>. 111 // H(x) = exp(-x^2/ (2s^2)) / sqrt(2 * s^2) 112 auto var = sigma * sigma; 113 auto expGaussDenom = -2 * var; 114 auto normalizeDenom = std::sqrt(2 * kPi) * sigma; 115 116 // Use the recursion relation from "Incremental Computation of the Gaussian" by Ken 117 // Turkowski in GPUGems 3. Page 877. 118 double g0 = 1.0 / normalizeDenom; 119 double g1 = std::exp(1.0 / expGaussDenom); 120 double g2 = g1 * g1; 121 122 gauss[0] = g0; 123 g0 *= g1; 124 g1 *= g2; 125 gauss[1] = g0; 126 127 int n = 1; 128 while (gauss[n] > kGoodEnough) { 129 g0 *= g1; 130 g1 *= g2; 131 gauss[n+1] = g0; 132 n += 1; 133 } 134 135 normalize(n, gauss); 136 137 return n; 138 } 139 140 SkGaussFilter::SkGaussFilter(double sigma, Type type) { 141 SkASSERT(0 <= sigma && sigma < 2); 142 143 if (type == Type::Bessel) { 144 fN = calculate_bessel_factors(sigma, fBasis); 145 } else { 146 fN = calculate_gauss_factors(sigma, fBasis); 147 } 148 } 149