Chromium Code Reviews| Index: src/effects/SkBlurMask.cpp |
| =================================================================== |
| --- src/effects/SkBlurMask.cpp (revision 10522) |
| +++ src/effects/SkBlurMask.cpp (working copy) |
| @@ -12,8 +12,20 @@ |
| #include "SkTemplates.h" |
| #include "SkEndian.h" |
| -const SkScalar SkBlurMask::kBlurRadiusFudgeFactor = SkFloatToScalar(.57735f); |
| +SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) { |
| + // This constant approximates the scaling done in the software path's |
| + // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). |
| + // IMHO, it actually should be 1: we blur "less" than we should do |
| + // according to the CSS and canvas specs, simply because Safari does the same. |
| + // Firefox used to do the same too, until 4.0 where they fixed it. So at some |
| + // point we should probably get rid of these scaling constants and rebaseline |
| + // all the blur tests. |
| + static const SkScalar kBLUR_SIGMA_SCALE = SkFloatToScalar(0.57735f); |
| + |
| + return radius ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; |
| +} |
| + |
| #define UNROLL_SEPARABLE_LOOPS |
| /** |
| @@ -471,26 +483,42 @@ |
| SkMask::FreeImage(image); |
| } |
| -bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, |
| - SkScalar radius, Style style, Quality quality, |
| - SkIPoint* margin) |
| -{ |
| +bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, |
| + SkScalar radius, Style style, Quality quality, |
| + SkIPoint* margin) { |
| + return SkBlurMask::BoxBlur(dst, src, |
| + SkBlurMask::ConvertRadiusToSigma(radius), |
| + style, quality, margin, true); |
| +} |
| +bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, |
| + SkScalar sigma, Style style, Quality quality, |
| + SkIPoint* margin, bool unused) { |
| + |
| if (src.fFormat != SkMask::kA8_Format) { |
| return false; |
| } |
| // Force high quality off for small radii (performance) |
|
robertphillips
2013/08/05 18:11:27
This should really be 2.232 to be completely consi
|
| - if (radius < SkIntToScalar(3)) { |
| + if (sigma <= SkIntToScalar(2)) { |
| quality = kLow_Quality; |
| } |
|
robertphillips
2013/08/05 18:11:27
If we were willing to live with this code here:
|
| + SkScalar passRadius; |
| + if (kHigh_Quality == quality) { |
| + // For the high quality path the 3 pass box blur kernel width is |
| + // 6*rad+1 while the full Gaussian width is 6*sigma. |
| + passRadius = sigma - (1/6.0f); |
| + } else { |
| + // For the low quality path we only attempt to cover 3*sigma of the |
| + // Gaussian blur area (1.5*sigma on each side). The single pass box |
| + // blur's kernel size is 2*rad+1. |
| + passRadius = 1.5f*sigma - 0.5f; |
| + } |
| + |
| // highQuality: use three box blur passes as a cheap way |
| // to approximate a Gaussian blur |
| int passCount = (kHigh_Quality == quality) ? 3 : 1; |
| - SkScalar passRadius = (kHigh_Quality == quality) ? |
| - SkScalarMul( radius, kBlurRadiusFudgeFactor): |
| - radius; |
| int rx = SkScalarCeil(passRadius); |
| int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255); |
| @@ -510,7 +538,7 @@ |
| margin->set(padx, pady); |
| } |
| dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady, |
| - src.fBounds.fRight + padx, src.fBounds.fBottom + pady); |
| + src.fBounds.fRight + padx, src.fBounds.fBottom + pady); |
| dst->fRowBytes = dst->fBounds.width(); |
| dst->fFormat = SkMask::kA8_Format; |
| @@ -651,13 +679,6 @@ |
| return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x); |
| } |
| -// Compute the size of the array allocated for the profile. |
| - |
| -static int compute_profile_size(SkScalar radius) { |
| - return SkScalarRoundToInt(radius * 3); |
| - |
| -} |
| - |
| /* compute_profile allocates and fills in an array of floating |
| point values between 0 and 255 for the profile signature of |
| a blurred half-plane with the given blur radius. Since we're |
| @@ -669,13 +690,13 @@ |
| memory returned in profile_out. |
| */ |
| -static void compute_profile(SkScalar radius, unsigned int **profile_out) { |
| - int size = compute_profile_size(radius); |
| +static void compute_profile(SkScalar sigma, unsigned int **profile_out) { |
| + int size = SkScalarCeilToInt(6*sigma); |
| int center = size >> 1; |
| unsigned int *profile = SkNEW_ARRAY(unsigned int, size); |
| - float invr = 1.f/radius; |
| + float invr = 1.f/(2*sigma); |
| profile[0] = 255; |
| for (int x = 1 ; x < size ; ++x) { |
| @@ -705,17 +726,18 @@ |
| } |
| bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, |
| - SkScalar provided_radius, Style style, |
| + SkScalar radius, Style style, |
| SkIPoint *margin, SkMask::CreateMode createMode) { |
| - int profile_size; |
| + return SkBlurMask::BlurRect(dst, src, |
| + SkBlurMask::ConvertRadiusToSigma(radius), |
| + style, margin, createMode, true); |
| +} |
| - float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor)); |
| +bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, |
| + SkScalar sigma, Style style, |
| + SkIPoint *margin, SkMask::CreateMode createMode, bool unused) { |
| + int profile_size = SkScalarCeilToInt(6*sigma); |
| - // adjust blur radius to match interpretation from boxfilter code |
| - radius = (radius + .5f) * 2.f; |
| - |
| - profile_size = compute_profile_size(radius); |
| - |
| int pad = profile_size/2; |
| if (margin) { |
| margin->set( pad, pad ); |
| @@ -745,7 +767,7 @@ |
| } |
| unsigned int *profile = NULL; |
| - compute_profile(radius, &profile); |
| + compute_profile(sigma, &profile); |
| SkAutoTDeleteArray<unsigned int> ada(profile); |
| size_t dstSize = dst->computeImageSize(); |
| @@ -775,8 +797,8 @@ |
| if (profile_size <= sw) { |
| horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w); |
| } else { |
| - float span = float(sw)/radius; |
| - float giX = 1.5f - (x+.5f)/radius; |
| + float span = float(sw)/(2*sigma); |
| + float giX = 1.5f - (x+.5f)/(2*sigma); |
| horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span))); |
| } |
| } |
| @@ -786,8 +808,8 @@ |
| if (profile_size <= sh) { |
| profile_y = profile_lookup(profile, y, dstHeight, h); |
| } else { |
| - float span = float(sh)/radius; |
| - float giY = 1.5f - (y+.5f)/radius; |
| + float span = float(sh)/(2*sigma); |
| + float giY = 1.5f - (y+.5f)/(2*sigma); |
| profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span))); |
| } |
| @@ -834,22 +856,24 @@ |
| return true; |
| } |
| +bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar radius, |
| + Style style, SkIPoint* margin) { |
| + return BlurGroundTruth(dst, src, ConvertRadiusToSigma(radius), style, margin, true); |
| +} |
| // The "simple" blur is a direct implementation of separable convolution with a discrete |
| // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but very |
| // useful for correctness comparisons. |
| -bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provided_radius, |
| - Style style, SkIPoint* margin) { |
| +bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar sigma, |
| + Style style, SkIPoint* margin, bool unused) { |
| if (src.fFormat != SkMask::kA8_Format) { |
| return false; |
| } |
|
robertphillips
2013/08/05 18:11:27
This is a bit of a radical switch in how sigma is
|
| - float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor)); |
| - float stddev = SkScalarToFloat(radius) /2.0f; |
| - float variance = stddev * stddev; |
| + float variance = sigma * sigma; |
| - int windowSize = SkScalarCeil(stddev*4); |
| + int windowSize = SkScalarCeil(sigma*4); |
| // round window size up to nearest odd number |
| windowSize |= 1; |