The mask-type property

TL;DR: The mask-type property is now available in Firefox Nightly.

Here is a simple use of an SVG mask:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     viewBox="0 0 512 341" width="512" height="341">
  <defs>
    <mask id="m">
      <circle cx="256" cy="200" r="150"/>
    </mask>
  </defs>
  <g mask="url(#m)">
    <image width="512" height="341.5" xlink:href="http://farm8.staticflickr.com/7063/6860283873_b9f2e6d511_b.jpg"/>
    <!-- original: http://www.flickr.com/photos/yewenyi/6860283873/
         http://creativecommons.org/licenses/by-nc/2.0/ -->
  </g>
</svg>

And here is how it renders:

Er, what? This should be a simple raster image masked by a circle. What went wrong?

If you’re like me, you would have made this mistake a thousand times. The problem is that the mask element looks at the luminance of the mask child content to determine how much of the masked graphics to let show through. Luminance is a measure of brightness – black has luminance of 0 and white has a luminance of 1. The different red, green and blue component values contribute with different weights, and the luminance value is then multiplied with the alpha of the mask content pixel to determine the final mask value. For each pixel, the mask value is multiplied by the RGBA pixel in the masked content. Since our mask consists only of a black circle (black is the initial value of the fill property), the mask value over the entire mask is 0, letting nothing of the masked content through! What we should have done is specified fill="white" on the circle.

It is a very common misconception that SVG masks look just at the alpha of the mask content, and this is also a common way that masks are used in authoring tools. To accommodate this, SVG 2 uses the new CSS Masking specification, which, among other things, allows an author to specify that the alpha of a mask’s content, and not the luminance, is used to determine mask values. This is done by the use of the mask-type property. We can use it in our example as follows:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     viewBox="0 0 512 341" width="512" height="341">
  <defs>
    <mask id="m" mask-type="alpha">
      <circle cx="256" cy="200" r="150"/>
    </mask>
  </defs>
  <g mask="url(#m)">
    <image width="512" height="341.5" xlink:href="http://farm8.staticflickr.com/7063/6860283873_b9f2e6d511_b.jpg"/>
    <!-- original: http://www.flickr.com/photos/yewenyi/6860283873/
         http://creativecommons.org/licenses/by-nc/2.0/ -->
  </g>
</svg>

and the resulting image would look like this:

Now, obviously this example is simple enough that if you could remember that you needed to change something to make the mask work, you would probably remember that fill="white" would work just as well as mask-type="alpha". The mask-type property could become more useful if you already have some graphics (either SVG content, perhaps referencing some raster images too) that has alpha. Although it would be possible to use an SVG filter to convert the alpha channel into an opaque image with just the right shade of white, this seems unnecessary work for the author.

Since mask-type is a CSS property, it means you can also set it once for all masks in a document with a single style rule, mask { mask-type: alpha }.

Implementation

Bug 793617 has just landed, which means that the mask-type property is now available in Firefox. As with other new features we have been implementing recently, this property is not prefixed. Instead, it lives behind a pref – layout.css.masking.enabled in this case. The pref will default to being true for Nightly and Aurora builds, and false for Beta and Release. Once the CSS Masking specification has matured further, the feature will be enabled by default on all builds. This means that you can begin experimenting with the property, but don’t write productions web sites that rely on it just yet!

In WebKit land, Dirk Schulze has implemented the property, also without a prefix.

[Edited 29 December 2012 – fix the examples to not use fill="white" as pointed out by roc]