After I gave you the blend mode library for both WPF and Silverlight, you thought, “Finally! I can do some really cool stuff … and what’s even better … it will run on the GPU!” Then, you started playing with it a little and you ran into some limitations.
In this third blog post in the series (Part 1 and Part 2), I will explore these limitations and show you how you can use Jeremiah Morrill’s GlassBehavior (which with his permission I have renamed to BackgroundEffectBehavior) to get around some of them.
Let’s jump in.
Sometimes you’ll have two images of the exact same size that you want to blend together using one of the blend modes in my library. That is:
<!-- Blending Two Images Together -->
<Image
Grid.Column="2"
Width="325"
Height="244"
Source="Resources/summer_325x244.jpg"
>
<Image.Effect>
<bme:PhoenixEffect>
<bme:PhoenixEffect.BInput>
<ImageBrush ImageSource="Resources/fall_325x244.jpg"/>
</bme:PhoenixEffect.BInput>
</bme:PhoenixEffect>
</Image.Effect>
</Image>
The above is easy. As you can see, you just choose one of the images and set the blend mode effect on it. This actually causes that image to become the ‘A’ input (remember: A + B = R). Then, you simply set the ‘B’ input of the blend mode effect to the second image.
But it isn’t always that easy, is it?!
Many times when a designer is thinking of blend modes they aren’t thinking “Ok, I have two images and I want to blend those together.” They often are thinking, “I have this shape and I want to blend it into its background using a cool blend mode.”
Take a look at the following. Here I have a shape, a gray (#FF808080) ‘H’, on top of an image:

And, here I have the gray ‘H’, blended into its background image using the Color Burn blend mode:

Now, anywhere I move that ‘H’ … it is going to look different because it is being blended into its background, similar, of course, to making a shape transparent (although with different math).
So, how would I do this with the blend mode library that I’ve provided? Well, it would seem easy … and here is a valiant try:
<Grid Width="240" Height="150">
<Grid.Resources>
<SolidColorBrush x:Key="solidColorBrush" Color="#FF808080"/>
<Path
x:Key="path"
Width="168.367"
Height="152.44"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Data="(removed for clarity)"
Fill="{DynamicResource solidColorBrush}"
/>
</Grid.Resources>
<Image
Source="Resources/MSwanson - Wide - Water 06.jpg"
Stretch="Uniform"
>
<Image.Effect>
<bme:ColorBurnEffect>
<bme:ColorBurnEffect.BInput>
<VisualBrush Visual="{StaticResource path}"/>
</bme:ColorBurnEffect.BInput>
</bme:ColorBurnEffect>
</Image.Effect>
</Image>
</Grid>
In the above, I am using the Image as the ‘A’ input and then passing the shape (Path) in as a VisualBrush for the ‘B’ input on the color burn blend mode effect. Here is what happens, though:

What is going on? Well, if you recall from Greg Schlecter’s series (1 and 2) on multi-input pixel shader effects (which is how the blend modes are implemented), the inputs have to be either a VisualBrush or an ImageBrush and the inputs have to be the same size. If they are not, the second input is resized to match the first input.
So, our ‘H’ path is getting sized bigger to match the image’s size and the parts outside of the path are taking part in the blending that is occurring (i.e. the dark areas outside the ‘H’) as well.
Greg Schlecter suggests (in the aforementioned articles) how you can solve the ‘resizing’ issue … by using the Viewbox property on the brush.
But, let’s take a step back here. Do we really want to apply the blend mode effect to the background? No, we don’t, as that would limit us to having only one effect per background. For that matter, it is also not very intuitive as you typically think of blending the shape into the background which suggests that it makes more sense to apply effect to the shape.
While thinking about how to solve this problem, I ran into this WPF forum post. I first thought, “Oh, no! I’m up a creek without a paddle.” But, then I dove in on Jeremiah Morrill’s GlassBehavior … and got it too work (at least for certain situations)!
(I’m not going to cover behaviors in this blog post, but they are basically something new with Expression Blend 3.x. They are an implementation of the attached property behavior pattern and allow the designer inside of Blend to drag and drop behaviors onto elements in the element tree. See here and here for more info.)
Jeremiah’s GlassBehavior is a behavior that let’s you apply an effect to the background underneath the element that you are attaching the behavior to. He uses it to apply a SmoothMagnifyEffect (to get a glass like appearance) … but you can really use it to apply any effect. And that is just what I did.
Take a look at this xaml:
<Grid>
<Grid x:Name="grid">
<Image
x:Name="image"
Source="Resources/MSwanson - Wide - Water 06.jpg"
Stretch="Uniform"
/>
</Grid>
<Canvas Width="168.367" Height="152.44">
<Path
Width="168.367"
Height="152.44"
Stretch="Uniform"
Data="(removed for clarity)"
Fill="{StaticResource solidColorBrush}"
>
<i:Interaction.Behaviors>
<local:BackgroundEffectBehavior
Visual="{Binding ElementName=grid, Mode=OneWay}"
>
<local:BackgroundEffectBehavior.Effect>
<bme:ColorBurnEffect>
<bme:ColorBurnEffect.BInput>
<ImageBrush>
<ImageBrush.ImageSource>
<DrawingImage>
<DrawingImage.Drawing>
<GeometryDrawing
Brush="{StaticResource solidColorBrush}"
>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,1,1"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</ImageBrush.ImageSource>
</ImageBrush>
</bme:ColorBurnEffect.BInput>
</bme:ColorBurnEffect>
</local:BackgroundEffectBehavior.Effect>
</local:BackgroundEffectBehavior>
</i:Interaction.Behaviors>
</Path>
</Canvas>
</Grid>
First of all, notice how the BackgroundEffectBehavior attaches to the Path through the attached property collection i:Interaction.Behaviors. This is how the behavior hooks its functionality into Path.
The BackgroundEffectBehavior’s Visual is set to the background (the grid which is holding the image) … and the BackgroundEffectBehavior’s Effect is set to the ColorBurnEffect which has a SolidColorBrush as its ‘B’ input. I also have the same SolidColorBrush set as the Fill of the Path … but this is just so that I can see it in the designer … as at runtime … this behavior kicks in and that Fill is not used.
This is what we get from the xaml above … but basically … success! 🙂

Now, a few caveats. 🙁 This whole thing seems to be very fragile.
For example, in order to get this to work, I had to put the image inside the grid … as a sibling to the Canvas that contains the ‘H’ path. Certain other ways of doing it didn’t work. For example, if I set the BackgroundEffectBehavior’s Visual to the grid which contains both the grid/image and the ‘H’ path … it doesn’t work.
Also, if you change the StaticResource(s) to DynamicResource(s) … it doesn’t work.
Another issue is that using this behavior inside of Blend … does not result in what-you-see-is-what-you-get (WYSIWYG) blending. That is why I also set the Fill of the Path above to the same SolidColorBrush that I used as my ‘B’ input to the color burn blend mode effect. In that way, at least, I can see the ‘H’ inside of Blend.
Finally, I have not done any performance analysis (yet) of this, but the BackgroundEffectBehavior uses two VisualBrush(es) to get its job done. So, if you use a lot of these, I wouldn’t be surprised if it starts slowing stuff down.
The best solution here, is really for Microsoft to bake blend modes into the platform. Unfortunately, blend modes will not be in next release of WPF (.NET 4.x) … but the good news is that Brendan Clark from Microsoft says he’s pushing for it.
Here is the code. Unfortunately, the BackgroundEffectBehavior is WPF-only right now (I hope to fix this), but I have updated the Silverlight test harness to have the gradient and image test harnesses. Enjoy!
Here is a live Silverlight 3 test harness (you will need the Silverlight 3.0 runtime, 3.0.40624).

p.s. I just want to give a shout-out to Mike Swanson and his wallpaper images. They rock. Period. The water droplet background I am using above is one of his.