惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
C
Cisco Blogs
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Security Archives - TechRepublic
Security Archives - TechRepublic
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
S
Security Affairs
L
Lohrmann on Cybersecurity
Hacker News - Newest:
Hacker News - Newest: "LLM"
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
小众软件
小众软件
Security Latest
Security Latest
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
F
Full Disclosure
S
Schneier on Security
L
LangChain Blog
MyScale Blog
MyScale Blog
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
Google Online Security Blog
Google Online Security Blog
Scott Helme
Scott Helme
Stack Overflow Blog
Stack Overflow Blog
爱范儿
爱范儿
A
Arctic Wolf
Martin Fowler
Martin Fowler
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
The Register - Security
The Register - Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
Latest news
Latest news
F
Fortinet All Blogs
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN

David Baron's Weblog

Software engineering, responsibility, and ownership Software engineering, responsibility, and ownership David Baron's weblog: Security and Inequality Running animations on the compositor thread David Baron's weblog: Tying ecosystems through browsers David Baron's weblog: Payments on the Web Thoughts on migrating to a secure Web David Baron's weblog: The need for government David Baron's weblog: Priority of constituencies How browser developers should seek feedback from Web developers A possible approach to shorter release cycles David Baron's weblog: Fifteen years Why debug builds (and assertions) are important Ten years of the Mozilla Foundation Open licensing at the W3C Why adding compositing and blending to CSS is harder than it looks How you can help with removing -moz- prefixes Moving bug history out of the primary display of a bug report Beware of locale-specific behavior in the C library Eating dogfood and shipping software Specification style and the future of the Web The bug system I wish I had CSS border-image changes and unprefixing Improving font size readability on Firefox for Android David Baron's weblog: CSS Animations, part 2 Hue-preserving color inversion with SVG filters Changes to handling of @-moz-keyframes David Baron's weblog: window.matchMedia() David Baron's weblog: CSS Animations Crash analysis in the future David Baron's weblog: calc() David Baron's weblog: colorDepth David Baron's weblog: Hidden complexity in specifications The most important field in a bug report: the summary WOFF font format submitted to W3C David Baron's weblog: :-moz-any() selector grouping setTimeout with a shorter delay Faster repainting in SVG foreignObject David Baron's weblog: Distributed Extensibility David Baron's weblog: Broadening crash analysis Correlating crashes with binary extensions or plugins David Baron's weblog: ex-HTML Downloadable font formats for the Web Web Accessibility as a Political Movement David Baron's weblog: CSS priorities David Baron's weblog: Bug priorities David Baron's weblog: Semi-vacation Some new CSS features in Firefox 3 David Baron's weblog: New selectors David Baron's weblog: The age of bugs Seeking a good Linux distribution David Baron's weblog: Teaching to the test David Baron's weblog: March 2008 David Baron's weblog: February 2008 David Baron's weblog: January 2008 David Baron's weblog: October 2007 David Baron's weblog: September 2007 David Baron's weblog: August 2007 David Baron's weblog: June 2007 David Baron's weblog: April 2007 David Baron's weblog: March 2007 David Baron's weblog: January 2007 David Baron's weblog: September 2006 David Baron's weblog: August 2006 David Baron's weblog: July 2006 David Baron's weblog: May 2006 David Baron's weblog: February 2006 David Baron's weblog: January 2006 David Baron's weblog: December 2005 David Baron's weblog: October 2005 David Baron's weblog: September 2005 David Baron's weblog: June 2005 David Baron's weblog: May 2005 David Baron's weblog: April 2005 David Baron's weblog: March 2005 David Baron's weblog: February 2005 David Baron's weblog: October 2004 David Baron's weblog: September 2004 David Baron's weblog: August 2004 David Baron's weblog: June 2004 David Baron's weblog: May 2004 David Baron's weblog: April 2004 David Baron's weblog: March 2004 David Baron's weblog: February 2004 David Baron's weblog: January 2004 David Baron's weblog: November 2003 David Baron's weblog: October 2003 David Baron's weblog: September 2003 David Baron's weblog: August 2003 David Baron's weblog: July 2003 David Baron's weblog: June 2003 David Baron's weblog: May 2003 David Baron's weblog: April 2003 David Baron's weblog: March 2003 David Baron's weblog: February 2003 David Baron's weblog: January 2003 David Baron's weblog: December 2002 David Baron's weblog: November 2002 David Baron's weblog: September 2002
What does a blur radius mean?
David Baron · 2011-02-26 · via David Baron's Weblog

[Note that this blog entry contains a good bit of markup, including script and SVG, and will probably not syndicate very well.]

A bunch of Web platform features involve blurring. For example, the CSS text-shadow property lets a shadow be both positioned and blurred. Each shadow is given with three numbers: the first two give the position and the third gives the blur radius. For example:

text with a shadow

span {
  font: italic 3em serif;
  color: rgb(255, 64, 0);
  text-shadow: silver 0.2em 0.2em 0.07em;
}

The CSS box-shadow property is similar, though it also takes a fourth number, a spread radius, which I won't discuss here, except to say that nothing discussed here is relevant to it.

The HTML canvas element has a similar shadow mechanism, which applies to all drawing operations:

Different browsers, however, have historically done different things for the same blur radius, both in terms of blurring algorithm and what the radius means for that algorithm (i.e., how blurry a given radius makes things). In some cases the same browser has done different things for canvas and the CSS properties. Over the past year, the CSS and HTML specifications have changed (for CSS) to define what this blur radius means or (for HTML) so that they agree with each other on this definition.

(I'm largely ignoring SVG filters here, which also have a blurring mechanism, since the rules for it have been stable for years. But I'll explain at the end how they relate to the rules for canvas and CSS.)

In Firefox 4, we've changed our implementation to match these changes in the specs.

Here, I'll explain what the blur radius now means. To do that properly, I need to explain how blurring works. So let's look at how we'd blur an image or any other pixel-based display. I'm going to do this with grayscale images for now, since it's simpler, but this treatment extends to RGB images and to premultiplied RGBA images, by just doing the same math for each color channel.

So how would we do a blur operation that takes some source image and produces a blurrier result image? In general, we'd do this using what's called a kernel function, where each pixel in the result image is a weighted average of pixels near that location in the source image. For example, we might use the following kernel to to compute the value of the highlighted pixel at the center in the result image from the nearby pixels in the source image:

00.010.030.010
0.010.050.110.050.01
0.030.110.160.110.03
0.010.050.110.050.01
00.010.030.010

In other words, we compute the pixel at that center position in the result image by multiplying these numbers times the color values in the source image. (It doesn't matter if 0 is black and 1 is white, or 0 is black and 255 is white, as long as we're consistent.) It's important that the numbers in the kernel add up to 1; that's what makes it a weighted average, and what keeps it from darkening or lightening the image. All the values further away, outside this grid, are also 0 in this kernel function. I just didn't bother making the grid bigger than needed. (Note that I'm ignoring what happens when we're near the edge of the image. There are multiple options, including assuming that the pixels at the edge of the image extend out infinitely, and assuming that everything past the edge of the image is transparent.)

However, computing a blur this way is very expensive, especially for large blurs. It requires that, for each pixel in the result image, we look at a large number of pixels in the source image and multiply it by some number that's specific to that result pixel. For a blur that takes input from pixels up to only 10 pixels away from the result pixel, this would mean looking at up to 441 (21×21) pixels in the source image. This would make blurring very slow.

So let's look at what we could do faster. One thing that we can do quickly is a blur in one dimension, with a kernel function whose values are uniform across a specific width. For example, the function:

1/71/71/71/71/71/71/7

does a horizontal blur that looks at pixels in the source image up to three pixels away, horizontally, from the result pixel. We can also represent this kernel function as a graph:

-4 -3 -2 -1 0 1 2 3 4

where the area under the graph is 1.

This kernel function is called a box blur. It's quick to compute because we can compute it by keeping a running total. We don't need to do an amount of work proportional to the size of the blur for each pixel in the result image. In particular, we can compute it as follows (assuming we're treating area past the edges of the image as transparent):

  1. Add up the values in the first four pixels in the row in the source image.
  2. Put the current value divided by seven in the first pixel in the row in the result image.
  3. Add the fifth pixel of the source.
  4. Put the current value divided by seven in the second pixel in the row in the result image.
  5. ...
  6. Add the seventh pixel of the source.
  7. Put the current value divided by seven in the fourth pixel in the row in the result image.
  8. Add the eight pixel of the source and subtract the first pixel
  9. Put the current value divided by seven in the fifth pixel in the row in the result image.
  10. ...

So far, this isn't a very interesting looking kernel function, though. It produces an ugly and horizontal-only blur.

But we can do something else here. We can run the same blur again, which is a form of convolution. When we do that, we end up with a kernel function like this:

1/492/493/494/495/496/497/496/495/494/493/492/491/49

-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7

Alternatively, we can pretend we have a large number of pixels and draw a smooth graph:

(When we did a single box blur, we had a graph that was piecewise constant. Now, with a double box blur, we have a graph that's piecewise linear. If you don't know what this means, don't worry about it.)

We can keep repeating this convolution, and as we do, the central limit theorem says we'll end up with a distribution that is closer and closer to a Gaussian, also known as a normal distribution. When we do a box blur three times, we have a function that is piecewise quadratic, and quite close to a Gaussian:

Gaussian Triple box blur

We could do more than three times, but three is (as shown above) quite close.

Now, the Gaussian function has a very interesting property. The product of a Gaussian kernel function in the horizontal direction and one in the vertical direction is a kernel function composed of perfect circles. In other words, if we do a Gaussian blur horizontally and then do the same vertically, the contribution of a point in the source image to a nearby point in the result image is a function only of the distance between the points; it doesn't matter at all what component of that distance is horizontal and what component is vertical. Or, to put it another way, there's no difference between an image that is rotated 45 degrees, run through a horizontal Gaussian blur and then a vertical Gaussian blur or one where the rotation happens last.

So the standard technique for blurring is to do exactly this: approximate a Gaussian blur by doing a triple box blur: three quick passes over the image in one dimension and then three quick passes over the image in the other dimension.

We use a Gaussian blur because it's computationally easy to approximate and it's relatively smooth looking, not, as far as I can tell, because it matches the way physical blurring effects happen with lenses or non-point light sources. My understanding is that the use of this approximation technique is mostly common across browsers, although Chrome uses (or used?) a single box blur, which leads the blur to have square-looking artifacts.

So, getting back to CSS and HTML: what does this blur radius mean? A Gaussian distribution is described by two parameters: the mean (μ) and the standard deviation (σ). We obviously want the mean (the center) of the kernel function applied to the source image to be the same as the pixel in the result image that we're computing. The blur effect is now defined by css3-background and by HTML to be a Gaussian blur with the standard deviation (σ) equal to half the given blur radius, with allowance for reasonable approximation error. So the kernel function looks, in one dimension, like this:

σ blur radius = 2σ

This means that a blur with a 10px radius has an effect that extends a little past 10 pixels, but the bulk of the visible effect is within the 10px blur radius.

I mentioned SVG at the beginning, and I'll mention it again here. The SVG feGaussianBlur filter primitive has a stdDeviation attribute, which takes the standard deviation (σ) of the Gaussian blur. So in SVG, the number given is half the number that you would give to get the same blur with CSS or canvas.

[Update 2016-03-11, 14:45 +0800: The “in SVG” rule also applies to the blur() filter function for the ‘filter’ CSS property, since the CSS filter functions are modeled on SVG filters. This means that the distinction is now that the length given is 2σ for the blur in a CSS ‘box-shadow’ or ‘text-shadow’, and the length given is σ for the blur in an SVG feGaussianBlur filter or CSS blur() filter.]

See also: