/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/core/SkBlurMask.h" #include "include/core/SkBlurTypes.h" #include "include/core/SkColorPriv.h" #include "include/core/SkPoint.h" #include "include/core/SkRect.h" #include "include/private/base/SkMath.h" #include "include/private/base/SkSafe32.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTemplates.h" #include "include/private/base/SkTo.h" #include "src/base/SkMathPriv.h" #include "src/core/SkMaskBlurFilter.h" #include <cmath> #include <cstring> #include <utility> class SkRRect; usingnamespaceskia_private; // 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 = …; SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) { … } SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) { … } template <typename AlphaIter> static void merge_src_with_blur(uint8_t dst[], int dstRB, AlphaIter src, int srcRB, const uint8_t blur[], int blurRB, int sw, int sh) { … } template <typename AlphaIter> static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes, AlphaIter src, int srcRowBytes, int sw, int sh) { … } template <typename AlphaIter> static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes, AlphaIter src, int srcRowBytes, int sw, int sh) { … } /////////////////////////////////////////////////////////////////////////////// // we use a local function to wrap the class static method to work around // a bug in gcc98 void SkMask_FreeImage(uint8_t* image); void SkMask_FreeImage(uint8_t* image) { … } bool SkBlurMask::BoxBlur(SkMaskBuilder* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style, SkIPoint* margin) { … } /* Convolving a box with itself three times results in a piecewise quadratic function: 0 x <= -1.5 9/8 + 3/2 x + 1/2 x^2 -1.5 < x <= -.5 3/4 - x^2 -.5 < x <= .5 9/8 - 3/2 x + 1/2 x^2 0.5 < x <= 1.5 0 1.5 < x Mathematica: g[x_] := Piecewise [ { {9/8 + 3/2 x + 1/2 x^2 , -1.5 < x <= -.5}, {3/4 - x^2 , -.5 < x <= .5}, {9/8 - 3/2 x + 1/2 x^2 , 0.5 < x <= 1.5} }, 0] To get the profile curve of the blurred step function at the rectangle edge, we evaluate the indefinite integral, which is piecewise cubic: 0 x <= -1.5 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3 -1.5 < x <= -0.5 1/2 + 3/4 x - 1/3 x^3 -.5 < x <= .5 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3 .5 < x <= 1.5 1 1.5 < x in Mathematica code: gi[x_] := Piecewise[ { { 0 , x <= -1.5 }, { 9/16 + 9/8 x + 3/4 x^2 + 1/6 x^3, -1.5 < x <= -0.5 }, { 1/2 + 3/4 x - 1/3 x^3 , -.5 < x <= .5}, { 7/16 + 9/8 x - 3/4 x^2 + 1/6 x^3, .5 < x <= 1.5} },1] */ static float gaussianIntegral(float x) { … } /* ComputeBlurProfile 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 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y)) all the time, we actually fill in the profile pre-inverted (already done 255-x). */ void SkBlurMask::ComputeBlurProfile(uint8_t* profile, int size, SkScalar sigma) { … } // TODO MAYBE: Maintain a profile cache to avoid recomputing this for // commonly used radii. Consider baking some of the most common blur radii // directly in as static data? // Implementation adapted from Michael Herf's approach: // http://stereopsis.com/shadowrect/ uint8_t SkBlurMask::ProfileLookup(const uint8_t *profile, int loc, int blurredWidth, int sharpWidth) { … } void SkBlurMask::ComputeBlurredScanline(uint8_t *pixels, const uint8_t *profile, unsigned int width, SkScalar sigma) { … } bool SkBlurMask::BlurRect(SkScalar sigma, SkMaskBuilder *dst, const SkRect &src, SkBlurStyle style, SkIPoint *margin, SkMaskBuilder::CreateMode createMode) { … } bool SkBlurMask::BlurRRect(SkScalar sigma, SkMaskBuilder *dst, const SkRRect &src, SkBlurStyle style, SkIPoint *margin, SkMaskBuilder::CreateMode createMode) { … } // 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(SkScalar sigma, SkMaskBuilder* dst, const SkMask& src, SkBlurStyle style, SkIPoint* margin) { … }