| Index: src/effects/SkBlurMask.cpp
|
| ===================================================================
|
| --- src/effects/SkBlurMask.cpp (revision 10932)
|
| +++ 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
|
|
|
| /**
|
| @@ -473,24 +485,40 @@
|
|
|
| bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
|
| SkScalar radius, Style style, Quality quality,
|
| - SkIPoint* margin)
|
| -{
|
| + SkIPoint* margin) {
|
| + return SkBlurMask::BoxBlur(dst, src,
|
| + SkBlurMask::ConvertRadiusToSigma(radius),
|
| + style, quality, margin);
|
| +}
|
|
|
| +bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src,
|
| + SkScalar sigma, Style style, Quality quality,
|
| + SkIPoint* margin) {
|
| +
|
| if (src.fFormat != SkMask::kA8_Format) {
|
| return false;
|
| }
|
|
|
| // Force high quality off for small radii (performance)
|
| - if (radius < SkIntToScalar(3)) {
|
| + if (sigma <= SkIntToScalar(2)) {
|
| quality = kLow_Quality;
|
| }
|
|
|
| + 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(SkBlurMask::ConvertRadiusToSigma(radius),
|
| + dst, src,
|
| + style, margin, createMode);
|
| +}
|
|
|
| - float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor));
|
| +bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
|
| + const SkRect &src, Style style,
|
| + SkIPoint *margin, SkMask::CreateMode createMode) {
|
| + 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(ConvertRadiusToSigma(radius), dst, src, style, margin);
|
| +}
|
| // 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(SkScalar sigma, SkMask* dst, const SkMask& src,
|
| + Style style, SkIPoint* margin) {
|
|
|
| if (src.fFormat != SkMask::kA8_Format) {
|
| return false;
|
| }
|
|
|
| - 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;
|
|
|
|
|