Tuesday 11 October 2016

SVG Filter Primitives—Input and Output - Vanseo Design

SVG Filter Primitives—Input and Output - Vanseo Design


SVG Filter Primitives—Input and Output

Posted: 11 Oct 2016 05:30 AM PDT

SVG filter primitives are the building blocks to create the effect you desire. You can use multiple primitives inside a single filter for more interesting effects, however, you need to make the different primitives aware of each other.

For the last couple of weeks I've been talking about SVG filter effects. I started with an overview and then offered more details about the filter element, the region it affects, and the filter property that references it.

Today I want to focus on how filter primitives work, mainly how the output of one primitive can be used as the input for another inside the same filter. Starting next week, I'll dig into the details of the specific filters.

Input and Output of Filter Primitives

SVG filter primitives take an image or graphic element as input, perform an operation, and then output a resulting image or graphic. So far the examples in this series have taken a single graphic, referenced the id of a filter containing a single filter primitive, <feGaussianBlur>, inside. The filter altered the graphic and the output of the filter appeared on screen.

You aren't limited to the one filter primitive, though. You can take the result of one primitive and pass it to another. You can do that using the in and result attributes.

  • result—<filter-primitive-reference>
  • in —SourceGraphic | SourceAlpha | BackgroundImage | BackgroundAlpha | FillPaint | StrokePaint | <filter-primitive-reference>

The value of result is defined by you. You can give it any name you'd like. Note the result isn't an id. It only has local scope.

When you set the result on a filter primitive any subsequent primitive inside the same filter can access it through the in attribute. If, however, you don't supply a value for result the output of that primitive can only be passed implicitly to the very next primitive in the filter.

The in attribute identifies the input for the primitive. It can accept a number of values including the result of one of the primitives that comes before it in the filter and there are several other possible sources of input.

  • SourceGraphic —represents the original graphic that references the filter
  • SourceAlpha —represents the alpha channel only of the original graphic that references the filter
  • BackgroundImage —represents an image snapshot of the canvas under the filter region at the time that the filter element was invoked
  • BackgroundAlpha —represents the alpha channel of an image snapshot of the canvas under the filter region at the time that the filter element was invoked
  • FillPaint —represents the value of the fill property on the target element for the filter effect and it's conceptually infinite
  • StrokePaint —represents the value of the stroke property on the target element for the filter effect and it's also conceptually infinite
  • <filter-primitive-reference> —the name given to the output of a previous filter primitive in the same filter

Some of those definitions probably aren't all that clear so let's see if a few examples can help.

In this example I added a second filter primitive, <feOffset> to the example I've been working throughout this series. The feOffset primitive comes first and uses the SourceGraphic (the initial blue square) as its input (the value of in) and I've given its result the highly original name offset.

The feGaussianBlur primitive takes "offset" as the value of its in attribute and performs the blur. I gave this primitive a result too, though it's not really needed since I don't ever refer to its result in the filter.

1  2  3  4  5  6  7  8  9  10  11  12  
<svg width="100%" height="220" style="outline: 1px solid red">   <defs>    <filter id="io">     <feOffset in="SourceGraphic" dy="5" result="offset" />     <feGaussianBlur in="offset" stdDeviation="3" result="blur" />       </filter>   </defs>     <rect x="10" y="10" width="100" height="100" fill="#00f" />   <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#io)" />  </svg>

Here's the result and you can see the blue square has been offset vertically as well as blurred.

Here's the example again, with one small change. Now the value of in for the feGaussianBlur is SourceGraphic instead of offset.

1  2  3  4  5  6  7  8  9  10  11  12  
<svg width="100%" height="220" style="outline: 1px solid red">   <defs>    <filter id="io2">     <feOffset in="SourceGraphic" dy="5" result="offset" />     <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur" />       </filter>   </defs>     <rect x="10" y="10" width="100" height="100" fill="#00f" />   <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#io2)" />  </svg>

Notice the result isn't offset like the previous example. The filter first performs the offset, but then the feGaussianBlur primitive ignores the result and uses for its input the initial source graphic which hasn't been offset.

Where SourceGraphic uses the original graphic as input, SourceAlpha uses only the alpha channel of the original graphic. In this example I changed feOffset to use SourceAlpha and have feGaussianBlur once again using the result of feOffset.

1  2  3  4  5  6  7  8  9  10  11  12  
<svg width="100%" height="220" style="outline: 1px solid red">   <defs>    <filter id="io3">     <feOffset in="SourceAlpha" dy="5" result="offset" />     <feGaussianBlur in="offset" stdDeviation="3" result="blur" />       </filter>   </defs>     <rect x="10" y="10" width="100" height="100" fill="#00f" />   <rect x="115" y="10" width="100" height="100" fill="#00f" filter="url(#io3)" />  </svg>

The result is now a blurry black square instead of a blue one. That's because the square is opaque (alpha = 1.0). Had the alpha channel of the original source been 0.5, you'd be looking at a blurry medium gray square.

SourceAlpha might not appear to be all that useful, but it allows us to create things like drop shadows. I'll show you an example when I cover the <feMerge> filter primitive in a couple of weeks.

The rest of the values for the in attribute will need a little more explanation.

Pseudo Inputs

The remaining four inputs BackgroundImage, BackgroundAlpha, FillPaint, and StrokePaint, are pseudo inputs. The idea being that the user agent would maintain these in a buffer and we'd be able to access what's inside the buffer. The first two, BackgroundImage and BackgroundAlpha, are meant to be accessible through an enable-background property.

Unfortunately the concept behind BackgroundImage and BackgroundAlpha was incompatible with CSS stacking context and no browser has implemented them and I don't think any intend to at this point. I mention both because they're still in the spec, even the SVG 2.0 spec.

The other two values, FillPaint and StrokePaint have some support. I've been able to access both in Firefox, which offers support. Because these pseudo inputs have infinite extent they fill the entirety of the filter effects region.

Here's an example, though you'll need to view it using Firefox to see the result. Most of the example should look familiar. The main thing to notice is the value of in, which is StrokePaint and that I've set the filterUnits to userSpaceOnUse.

1  2  3  4  5  6  7  8  9  10  
<svg width="100%" height="220" style="outline: 1px solid red">   <defs>     <filter id="pseudo" filterUnits="userSpaceOnUse">       <feGaussianBlur stdDeviation="3" in="StrokePaint" />     </filter>   </defs>     <rect x="10" y="10" width="100" height="100" fill="#00f" />   <rect x="115" y="10" width="100" height="100" fill="#0f0" stroke="#f00" filter="url(#pseudo)" />   </svg>

Here's the result, which again you'll need to view using Firefox. If you are you should see the entire viewport filled with the stroke color, red.

If you remove the filterUnits or set them back to ObjectBoundingBox, then the stroke color will display in a region 10% larger than the square on all four sides. You can also set specific x, y, width, and height values to control the location and size of ether pseudo input.

I haven't been able to get either to work in Chrome, Safari, or Opera. I haven't tested in Internet Explorer or Edge, but I don't think either offers support.

It's hard to know if support will grow or if FillPaint and StrokePaint will be like BackgroundImage and BackgroundAlpha. I would think support will grow, but that's up to the browsers and not me.

For now go ahead and use SourceGraphic, SourceAlpha, and any filter-primitive-reference you set, but hold off using BackgroundImage, BackgroundAlpha, FillPaint, and StrokePaint.

Closing Thoughts

I hope the last section didn't cause too much confusion. I debated even mentioning the pseudo inputs as values, but figured I would as they are still listed in the spec.

The main thing to take away from this post is that you can choose the input and output for each filter primitive, allowing you to chain a number of primitives together inside a single filter for more interesting effects.

However, before you can do that, you need to know the different filter primitives you can use. Next week I'll start to cover all the filter primitives in detail and with examples, beginning with a few simple filter effects, including a couple we've already seen.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now