Download for Windows Download for Linux Download for FreeBSD Download for Mac Manual Wiki Forum IRC Trac

Friday, July 25, 2008

How VSFilter renders border and shadow

If you've ever used the \be or the new \blur tag you might wonder, why do they blur edges and not everything?

Example of how VSFilter blurs just edges and not everything
The answer lies in how VSFilter internally handles fill, border and shadow, and the relationship between them.


The basic component of a subtitle rendering is the fill. The fill is the main shape of the text, ie. what you see if you disable border and shadow. (When I write "text" here it can just as well be a vector drawing made with \p1.)

I am keeping things simple here, there are some technical details in the actual implementation I'm skipping over because they aren't relevant for the discussion, although they actually impact the actual algorithm used greatly. I might discuss the detailed algorithm later.

When I talk about bitmaps in this post, they are single-channel bitmaps, ie. black/white bitmaps. Colour is applied during the painting-step, this is described in detail below.

When a subtitle is to be rendered, VSFilter first creates a bitmap of the text fill. It then sees if the text should have a "wide outline", ie. if a \bord tag is in effect. If there is a wide outline, it allocates an additional bitmap that will contain the widened region.

The widened region is the fill bitmap modified in a way so it's effectively "embolded", ie. the outline is expanded outwards, but the entire fill is still kept.

Visualisation of how the widened region is the fill expanded outwards by border size
You might notice that the widened region looks a lot like the shadow. This is entirely correct, because it is used for rendering not just the border but also the shadow.

When the line is to be rendered, the fill bitmap is used as-is for fill, the widened region is used as-is for shadow, and the border is generated by subtracting the fill from the widened region, ie. the border is the part of the widened region that is not also in the fill.

Things work a little different when there is no border. In that case, the fill is painted as-is, but the shadow is also painted using the fill.

I wrote above that the fill and widened region bitmaps are black/white bitmaps, so what about colour? The bitmaps are simply re-coloured with the selcted colour during painting, or more correctly, used as alpha masks to paint a frame full of the colour.

Okay, so on to the blur effects.

The blur effects are applied to the bitmaps, either to the fill or to the widened region bitmap. If there is a widened region bitmap, it is applied to that one only and the fill is left alone. If there is no widened region (ie. no border) the blur is applied to only the fill bitmap.

This is why only the border blurs when you use \be or \blur along with a border, and why the fill does blur when you use blur on lines with no border: The fill bitmap is rendered on top of the blurred border, even though the border blur extends below the fill.

Related Posts by Categories



5 comments:

  1. Mark RaishbrookAug 20, 2008 04:52 PM
    Another enjoyable article!

    I guess the blurring employed by VSFilter runs a weighted kernel across the bitmap. Does it use a source and clone bitmap for this or just the source? The results can be quite different. What size kernel is used? 3x3? 5x5? Or does this depend on the user-selected depth? In other words, if it uses a 3x3 kernel (blur = 1) and the user wants a blur depth of 2, does it run the 3x3 code twice or increase the size of the kernel to 5x5? Is the blur process split into two passes (vertical and horizontal) to increase rendering speed? And is true Gaussian blurring used or just an approximation?

    Sorry for all the questions! I find this stuff fascinating, having knocked myself out for 24 months adding a pseudo-Gaussian blur to our app's bitmap subtitles. And I'm still not happy with the results! (http://www.spotsoftware.nl/bitmap.html) :)
    ReplyDelete
  2. Heh, late reply (somehow hadn't noticed this comment before) but here's an answer anyway.

    The \be tag uses a simple 3x3 matrix, [ 1 2 1; 2 4 2; 1 2 1 ]. It is not applied in a separated manner but just as a 3x3 convolution.

    With the "extended \be" patch you can give a larger parameter than 1 to \be, this causes the convolution to be applied that number of times. As the image in the main post shows, the results aren't very pretty.

    The \blur tag uses a real gaussian kernel applied as a separated filter. It is an approximated kernel, the elements are unsigned bytes ie. 0-255 range, and rather than being of infinite length it uses a cutoff which should be large enough to get the significant part of it.
    ReplyDelete
  3. I put {\be10} in my video, and i when i encode it with x264 it doesnt show :( What is the problem? I also use K-Lite codec pack. Can someone please help me out?
    ReplyDelete
  4. Please ask your support question in the forums, not in a blog comment.
    ReplyDelete

If you need help with Aegisub or have a bug report please use our forum instead of leaving a comment here. If you have a feature request, please go to our UserVoice page.

You will get better help on our forum than in the blog comments.