Blend Modes: Hue, Saturation, Color, and Luminosity with WPF 4.0

I’ve been toying with and working on this blend mode library of mine for some time now (see posts 1, 2, 3, 4, 5). However, it is now only with WPF 4.0 (currently Beta 2) … that I can actually implement all the blend modes.

Hue, Saturation, Color, and Luminosity: Where Are You?

If you were watching closely, you will have noticed that I have not (to date) implemented the Hue, Saturation, Color, and Luminosity blend mode effects.

Why is that? Whenever fxc.exe would compile the .fx file (through the Shader Effects BuildTask and Templates), it would give me the following two errors:

  • error X5608: Compiled shader code uses too many arithmetic instruction slots (100). Max. allowed by target (ps_2_0) is 64.
  • error X5609: Compiled shader code uses too many instruction slots (102). Max. allowed by the target (ps_2_0) is 96.

Those errors mean that you’ve exceeded the limitations of Pixel Shader 2.0 … which is what WPF 3.5 SP1 targets for pixel shader effects.

So, what can you do about this? You could optimize the HLSL so that your effect code goes under the limits … but if you notice in the errors above (these particular errors are for the Saturation effect) … you will see that I am significantly above the number of allowed arithmetic instruction slots (100 versus 64). I would have to optimize significantly.

WPF 4.0 to the Rescue

Another option is to utilize WPF 4.0!

WPF 4.0 increases what you can do with effects because it now allows you to target Pixel Shader 3.0. This wikipedia article has a great chart on the differences between the different shader models. With PS 3.0, we shouldn’t hit any limits.

Realizing this, as soon as Visual Studio 2010 and .NET 4 Beta 2 came out (PS 3.0 is only available in Beta 2), I installed it and gave it a whirl. There was really only one gotcha in getting this to work. The Shader Effects BuildTask only compiles to PS 2.0 … and there is no option to toggle between PS 2.0 and PS 3.0.

This means that, for now, probably until release, you will have to manually run fxc.exe (targeting ps_3_0) and add the compiled shader output file (the .ps file) to the project as a Resource.

Caveats

There are some caveats to using PS 3.0 in WPF 4.0. Most notably, there is no software fallback if your hardware or situation (remote desktop, forced software rendering) don’t support it. This means that your effect code will simply not run and not appear.

Some good news is that Microsoft has extended the RenderCapability API so that you can determine what level of pixel shader support is currently available. They’ve updated the RenderCapability.IsPixelShaderVersionSupported method and have added two additional methods: RenderCapability.IsPixelShaderVersionSupportedInSoftware and RenderCapability.MaxPixelShaderInstructionSlots.

(The above information and more is available in David Teitlebaum’s PDC09 session titled Mastering WPF Graphics and Beyond. Jamie Rodriguez also has some great posts covering what’s new in WPF 4.0 (Beta 1, Beta 2).)

Another caveat that bears mentioning is that Silverlight (both 3.0 and 4.0) only support PS 2.0. So, if you are creating and using effects across both technologies, you will not be able to use PS 3.0. This also means that the Hue, Saturation, Color, and Luminosity blend modes (and the attached code) are WPF only blend modes. Sorry Silverlight! I love you, but that’s how the chips fall.

Hue, Saturation, Color, and Luminosity: Here They Are!

Alright … enough with the caveats! Let’s see the effects!

First, let me refer you back to Angie Bowen’s post where she covers what each blend mode does … including the Hue, Saturation, Color and Luminosity effects.

To show off what each effect does, I’m going to go ahead and use her reference images … first showing how they blend out of Photoshop … and then showing how they blend with my library … for comparison purposes. Why? Well, it seems as if my blend mode library does not always match what is coming out of Photoshop … sometimes it’s pretty darn close and at other times it’s more than just a little bit off.

I have been through and through the math (and HLSL) for these effects and I honestly can’t find anything wrong there. If I could speculate what is going on, I would say that I’m bumping up against some float versus double precision issues (where Photoshop is using doubles for greater precision) during the conversion from RGB to HSL and back. If you can figure out what I’ve done wrong (if anything) please let me know.

However, for the most part, they work great!

Hue Blend Mode

The result is the hue of the blend layer and the saturation and luminance of the base layer.

Photoshop

HuePhotoshop

My Blend Mode Library

Hue

Photoshop

Hue2Photoshop

My Blend Mode Library

Hue2

Saturation Blend Mode

The result is the saturation of the blend layer and the hue and luminance of the base layer.

Photoshop

SaturationPhotoshop

My Blend Mode Library

Saturation

Photoshop

Saturation2Photoshop

My Blend Mode Library

Saturation2

Color Blend Mode

The result is luminance of the base layer and the hue and the saturation of the blend layer.

Photoshop

ColorPhotoshop

My Blend Mode Library

Color

Photoshop

Color2Photoshop

My Blend Mode Library

Color2

Luminosity Blend Mode

The result is hue and the saturation of the base layer and the luminosity of the blend layer.

Photoshop

LuminosityPhotoshop

My Blend Mode Library

Luminosity

Photoshop

Luminosity2Photoshop

My Blend Mode Library

Luminosity2

The Binaries and the Source Code

Here is the source code for the library and here is the assembly.

Again, remember that this is a WPF-only release as Silverlight does not yet have support for Pixel Shader 3.0.

Also, all of these new blend modes are opacity aware … meaning that you can pull the opacity out of the upper layer to lessen the effect of the blend mode (see my last post on the matter).

Enjoy!

p.s.

At one point, I thought that the Shader Effects BuildTask and Templates was built into Visual Studio 2010. It appears I was mistaken about that. I have updated this post to indicate that.

Blend Modes: Now Opacity Aware

After I wrote my trio of blog posts (1, 2, 3) about blend modes using pixel shader effects, I was toying around with it …

 

The Problem

… and noticed that it didn’t handle an opacity changes on the upper layer (B).

In fact, this was brought to my attention by a great blog post by Angie Bowen. In it she explains how the blend modes work and, she says:

Remember that to get better results you can also adjust the opacity of the upper layer.

Trying it out, revealed that some of the blend modes were okay, but most were not. Most of the blend modes would simply result in a black square if you pulled all the opacity out of the upper layer (B). This was obviously wrong, for if you pull all the opacity out of the upper layer (B), you should get the lower layer (A).

Argh!

 

The Solution

So, I dove back in on the blend mode math, trying to figure out what I needed to do to make these blend modes … opacity aware. That’s got a nice ring to it, doesn’t it!?

I started at the top of the list and got the NormalEffect working:

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float4 inputColor;
    inputColor = tex2D(input, uv);

    float4 blendColor;
    blendColor = tex2D(blend, uv);

    inputColor.rgb = (1 - blendColor.a) * inputColor.rgb + blendColor.rgb;

    return inputColor;
}

Ok, the above math made sense. When the opacity of the upper layer (blendColor.a) was 1 (opaque), the result was just blendColor. Otherwise when the opacity of the upper layer was 0 (transparent), the result was inputColor.

So, I then started to tackle the darken blend modes (Darken, Multiply, …) and quickly ran into problems. It was at that point, that I ran into this post in the WPF Forum. My blend modes were rendering full black (not white), but it provided a crucial piece of knowledge.

Namely, WPF uses pre-multiplied alpha everywhere for performance reasons.

What does that mean? Well, it means that the RGB values for inputColor are already multiplied by the alpha value for inputColor and that the RGB values for blendColor are already multiplied by the alpha value for blendColor.

Ah! Do you see it? This explains why the blend modes were going to a black square when pulling the opacity out. Take the Multiply blend mode. In the above HLSL, it would be:

// R = Base * Blend
resultColor.rgb = inputColor.rgb * blendColor.rgb

So, if the alphas were pre-multiplied in and you were pulling opacity out of the blend (upper) layer … then blendColor.rgb would go to zero … which would cause resultColor to go to zero … which would cause the gradient square to go to black!

Thinking about this … brought about the general solution for making these blend modes opacity aware. I needed to simply:

  1. Un-pre-multiply the blend layer alpha value out.
  2. Apply the blend mode math.
  3. Then re-multiply the blend layer alpha value in again.
    Here is the HLSL for the opacity aware Multiply blend mode:
    float4 main(float2 uv : TEXCOORD) : COLOR
    {
        float4 inputColor;
        inputColor = tex2D(input, uv);
    
        float4 blendColor;
        blendColor = tex2D(blend, uv);
    
        float4 resultColor;
        resultColor.a = inputColor.a;
        // un-premultiply the blendColor alpha out from blendColor
        blendColor.rgb = clamp(blendColor.rgb / blendColor.a, 0, 1);
    
        // apply the blend mode math
        // R = Base * Blend
        resultColor.rgb = inputColor.rgb * blendColor.rgb;
    
        // re-multiply the blendColor alpha in to blendColor
        // weight inputColor according to blendColor.a
        resultColor.rgb =
            (1 - blendColor.a) * inputColor.rgb +
            resultColor.rgb * blendColor.a;
    
        return resultColor;
    }

    A few comments about the above code. Notice that I am clamp(ing) when I un-premultiply (i.e. divide) the alpha out. This assures that the RGB values will be between 0 and 1 (where 0 is black and 1 is white … HLSL operates in ScRGB color space). This is necessary since dividing by values close to 0 (blendColor.a) can yield large numbers or even positive infinity … which throws off the math.

Secondly, when I re-multiply the blend layer alpha value back in … I need to also properly weight the inputColor … just like I did in the NormalEffect above.

Finally, notice that I really don’t have worry about the opacity on the lower layer (A). I just pass its value off to the result by setting resultColor.a equal to inputColor.a.

Applying this general formula worked in all cases!

 

The Gradient Contour Test Harness

In order to verify that I was doing math correctly, and to see the effect of pulling the opacity out of the blend modes … I have built a new gradient test harness. I have called it the gradient contour test harness since it not only shows the A + B = R gradient squares but it also shows the R gradient square with contours … just like Paul Dunn’s post does when you mouse over the R squares.

It is extremely interesting (to me at least) watching the contours as you pull out the opacities.

For example, Here are three gradient contour squares for the Pin Light blend mode at opacity values of 1.0, 0.5, and 0.0:

Gradient Squares

Gradient Contour Squares

Opacity

PinLight1.00nc PinLight1.00

1.0

PinLight0.50nc PinLight0.50

0.5

PinLight0.00nc PinLight0.00

0.0

I have also included a button labeled ‘Swap’ which swaps the A and B layers … since not all blend mode effects are commutative.

The gradient contours were made possible via Dwayne Need’s library. Check out the code (the class Grayscale4Bitmap) and see this post for more info.

The Image Test Harness

I’ve also put opacity sliders in the image test harnesses. Let’s take a look at the Pin Light effect at opacity values of 1.0, 0.5, and 0.3.

Images

Opacity
PinLightImage1.0 1.0
PinLightImage0.75 0.50
image 0.3

As you can see … pulling out the opacity … lessens the effect that the upper layer/texture has on the lower layer … and proves the truth of what Angie Bowen was saying earlier about using the opacity of the upper layer to achieve better results.

 

The Binaries and the Source Code (aka The Goods)

So, as I’m fond of saying … without further adieu … here is source code for the Blend Mode library and here are the library binaries … now opacity aware!

I also have updated the Silverlight test harness (as always you will need the Silverlight 3.0 runtime).

p.s.

The gradient contour test harness is WPF only … you won’t find that on the Silverlight side. Maybe someday I’ll get my Silverlight test harnesses up to parity with what’s in the WPF test harnesses … but I can’t see when. Any one want to do it for me? Bueller? Bueller?

WPF & Silverlight Charting: A Logarithmic Axis

I love controls. Even more … I love graph controls. In fact, graph controls have been one of the main things I have worked on during my tenure at my current place of employment.

A small aside: I think it is what I was meant to do (i.e. work on graph controls). For, my name is … Cory Plotts. Plotts, you know, as in plots. Ok, never mind, that was lame. 😀

So, it was quite natural, and actually part of my job, to take a look at what Microsoft is offering in the WPF and Silverlight toolkits. I have to hand it to David Anson and the other fellows at Microsoft. They have taken a very nice approach.

First, almost everything is a System.Windows.Controls.Control that you can restyle and re-template to your heart’s content. So, that makes it very designable.

Second, I love the data binding model. They have decided to follow an ItemsControl like approach where you have an ItemsSource property that you just plunk your data into. Very nice. It literally takes you seconds to get something up and running.

Third, they are developing it … with Blend in mind. That is, they are trying to provide a positive Blend experience and have gone to pains to make it so. So, not only is it designable … but designers can actually use Blend to do their designing … instead of hacking through xaml in a code editor. Woot! Woot!

Now, this post is not going to be an introduction on how to start using the charting component. Many others have done that already. If that is what you are looking for … I would suggest that you go to this blog post by David Anson where he lists out lots and lots of links to other blog posts and articles. In fact, he is so nice … that he has even separated them out by difficulty level.

No, in this blog post, I am going to show you the fourth thing I love about this charting component: how extensible it is! And, I am going to do that by showing how I created a logarithmic axis by simply deriving from NumericAxis.

So, here’s the code (sorry for just dumping it all in one place … but it gives you a nice place to copy out the entire implementation at once):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;

namespace System.Windows.Controls.DataVisualization.Charting
{
    /// <summary>
    /// An axis that displays numeric values along a logarithmic range.
    /// </summary>
    [StyleTypedProperty(Property = "GridLineStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "MajorTickMarkStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "MinorTickMarkStyle", StyleTargetType = typeof(Line))]
    [StyleTypedProperty(Property = "AxisLabelStyle", StyleTargetType = typeof(NumericAxisLabel))]
    [StyleTypedProperty(Property = "TitleStyle", StyleTargetType = typeof(Title))]
    [TemplatePart(Name = AxisGridName, Type = typeof(Grid))]
    [TemplatePart(Name = AxisTitleName, Type = typeof(Title))]
    public class LogarithmicAxis : NumericAxis
    {
        /// <summary>
        /// Instantiates a new instance of the LogarithmicAxis
        /// </summary>
        public LogarithmicAxis()
        {
            ActualRange = new Range<IComparable>(1.0, 2.0);
        }

        /// <summary>
        /// Returns the plot area coordinate of a value.
        /// </summary>
        /// <param name="value">The value to plot.</param>
        /// <param name="range">The range of values.</param>
        /// <param name="length">The length of the axis.</param>
        /// <returns>The plot area coordinate of the value.</returns>
        protected override UnitValue? GetPlotAreaCoordinate(object value, Range<IComparable> range, double length)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (range.HasData)
            {
                double doubleValue = ValueHelper.ToDouble(value);
                Range<double> actualDoubleRange = range.ToDoubleRange();

                return
                    new UnitValue
                    (
                        length /
                        Math.Log10(actualDoubleRange.Maximum / actualDoubleRange.Minimum) *
                        Math.Log10(doubleValue / actualDoubleRange.Minimum),
                        Unit.Pixels
                    );
            }

            return new UnitValue?();
        }

        /// <summary>
        /// Returns the value range given a plot area coordinate.
        /// </summary>
        /// <param name="value">The plot area position.</param>
        /// <returns>The value at that plot area coordinate.</returns>
        protected override IComparable GetValueAtPosition(UnitValue value)
        {
            if (ActualRange.HasData && ActualLength != 0.0)
            {
                if (value.Unit == Unit.Pixels)
                {
                    double coordinate = value.Value;
                    Range<double> actualDoubleRange = ActualRange.ToDoubleRange();

                    double output =
                        Math.Pow
                        (
                            10,
                            coordinate *
                            Math.Log10(actualDoubleRange.Maximum / actualDoubleRange.Minimum) /
                            ActualLength
                        )
                        *
                        actualDoubleRange.Minimum;

                    return output;
                }
                else
                {
                    throw new NotImplementedException();
                }
            }

            return null;
        }

        /// <summary>
        /// Returns a sequence of values to create major tick marks for.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of values to create major tick marks for.
        /// </returns>
        protected override IEnumerable<IComparable> GetMajorTickMarkValues(Size availableSize)
        {
            return GetMajorValues(availableSize).Cast<IComparable>();
        }

        /// <summary>
        /// Returns a sequence of values to plot on the axis.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of values to plot on the axis.</returns>
        protected override IEnumerable<IComparable> GetLabelValues(Size availableSize)
        {
            return GetMajorValues(availableSize).Cast<IComparable>();
        }

        /// <summary>
        /// Returns a sequence of major axis values.
        /// </summary>
        /// <param name="availableSize">The available size.</param>
        /// <returns>A sequence of major axis values.
        /// </returns>
        private IEnumerable<double> GetMajorValues(Size availableSize)
        {
            if (!ActualRange.HasData || ValueHelper.Compare(ActualRange.Minimum, ActualRange.Maximum) == 0 || GetLength(availableSize) == 0.0)
            {
                yield break;
            }

            yield return 125;
            yield return 250;
            yield return 500;
            yield return 1000;
            yield return 2000;
            yield return 4000;
            yield return 8000;
        }
    }
}

To explain the above code a little bit, I would first direct your attention to two methods. The first is called GetPlotAreaCoordinate. This method is the method responsible for converting from world coordinates into device coordinates. That is, it converts all the data point values in your Series into pixel coordinates … so that your data point is placed where you expect it should be.

The second method is called GetValueAtPosition. This method does the exact opposite as GetPlotAreaCoordinate. It converts from device coordinates into world coordinates. In other words, it takes the mouse position (for example) and it tells you where your mouse is at on the respective axis.

So, the above two methods contain the math behind the axis … and if you look closely, you’ll see the Math.Log10 function getting called. Yep. That’s right. This is a logarithmic axis.

After those two methods, you will see GetMajorTickValues and GetLabelValues. They both delegate to a third method called GetMajorValues. GetMajorTickValues returns the values at which we want major tick marks and GetLabelValues returns the values at which we want grid line labels. Pretty straightforward.

Now, a word about my implementation of GetMajorValues. In it, I have chosen to hardcode very specific values for this axis’ grid lines (i.e. 125, 250, 500, 1000, 2000, 4000, 8000). I did that because I was lazy. For inside of LinearAxis, there is an Interval property which you can use to generate the grid lines in a dynamic fashion … and I didn’t feel like figuring out how to make the Interval property work for the logarithmic axis. (If anyone out there does take the time to do so … please share!)

So, more than likely, you will be wanting to replace those specific values with some of your own … or will be wanting to generate the major values in a much more dynamic way.

Ok, now let me show you how you would use this axis. Check out this xaml (this is just a snippet … I am leaving out all the styling xaml and more):

<charting:Chart
    x:Name="chart"
    Width="480"
    Height="480"
    Margin="10"
    Title="Response"
    BorderBrush="{x:Null}"
    Style="{StaticResource chartStyle}"
>
    <charting:Chart.Axes>
        <charting:LogarithmicAxis
            Orientation="X"
            ShowGridLines="True"
            Title="Frequency (Hz)"
            Minimum="100"
            Maximum="10000"
        />
        <charting:LinearAxis
            Orientation="Y"
            ShowGridLines="True"
            Title="Response (dB SPL)"
            Minimum="20"
            Maximum="120"
            Interval="10"
        />
    </charting:Chart.Axes>
    <charting:LineSeries
        ItemsSource="{StaticResource responseCurve}"
        IndependentValueBinding="{Binding Frequency}"
        DependentValueBinding="{Binding Response}"
        DataPointStyle="{StaticResource lineDataPointStyle}"
        AnimationSequence="Simultaneous"
        TransitionDuration="0:0:0"
        IsSelectionEnabled="True"
    />
</charting:Chart>

In the above, you can see my new and proud LogarithmicAxis with a Minimum value of 100 and a Maximum value of 10000. Here is a snapshot of the chart that the above xaml creates:

image

Woot! Doesn’t it look sweet!

Now, go out and create some logarithmic charts!

p.s.

I have had to make these changes in a local edit of the toolkits. However, as David Anson points out in this post … they are unsealing everything! So, in a little while … you will no longer need to do this. You will be free to simply derive a new axis at your leisure. In my mind, that is another point for extensibility!

p.s.s.

Here is the source code for this article … note that it contains David Anson’s development release version 1 … and it contains his DataVisualizationDemos applications. Finally, and most importantly for this article, it contains the sample code for the above … under the title Visibility (I was working on some DataPoint visibility functionality at the time I drafted up that test harness).

p.s.s.s.

David Anson and the fellows at Microsoft have finally released the version of the Silverlight Toolkit that unseals everything. Check out this blog post for more info. When David publishes another development release, I’ll update the source code for this article so that the above is not a local edit.

p.s.s.s.s.

I have finally gotten around to getting the source code together where the above logarithmic axis is just an extension of toolkit … versus the local edit as before. So, here is that source code. It is actually from David Anson’s development release 3 (although I have stripped out the WPF 4 and Silverlight 4 projects). I won’t remove the above source code … as some people might want to see both ways of doing it.

One additional comment. In David’s development release 2, there was an internal NumericAxis constructor. So, even though he unsealed everything, that internal constructor was inhibiting me from inheriting a new numeric axis type via an extension. That meant I had to wait till development release 3 in order to provide this code. (I also had to find some time too. :D)

p.s.s.s.s.s.

I have finally decided to crack out the WPF 4.0 and Silverlight 4.0 version of my logarithmic axis above as several people have expressed confusion and frustration trying to get it to work.

The source code can be downloaded here.

This source code is based on David Anson’s development release 4 version (i.e. here for the core bits and here for the test harnesses).

Please note that you should open up DataVisualizationDemos4.sln from the downloaded code as that solution contains all 4 projects: the 2 core projects and the 2 test harness projects. The 2 test harness projects have project references to the assemblies built in the 2 core projects.

Also note that you should unload either the Silverlight 4 test harness … or the WPF 4 test harness, when running, setting the other one as your startup project … as I have had problems with the built executables/bits for the test harnesses stomping on top of one another.

Finally, once you get it running, there is a ‘Logarithmic Axis’ test harness. Good luck!

WPF Control Development Unleashed

book

I just received my copy of the masterpiece WPF Control Development Unleashed in the mail. Woohoo!

Written by the veritable master Pavan Podila with a little help from Kevin Hoffman, it is sure to be an instant classic.

Why is that?

Well, WPF is known for its learning curve. In particular, the amount of concepts that you have to digest in order to create custom elements and controls … is rather daunting.

This book will ease your journey. Even more, this book will give you the insight and the tricks to deliver compelling user experiences.

 

 

 

 

You know me, the presentation layer is my passion, and in particular, building controls is what I love to do. This book was meant for me. In fact, I was honored to help review a few of the chapters and now that I have it in front of me … I’m going to devour the rest of it. Who knows, maybe I will start up a series of blog posts as I go along.

Muhawhahaha! World domination! With this in my hands, the world is mine! All mine!

Blend Modes, Part III

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:

HOnImage600

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

HOnImageColorBurn600

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:

HOnImageColorBurn600NoCigar

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! 🙂

HOnImageColorBurn600Success

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).

HelloWorldColorBurn600

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.

Blend Modes for Silverlight

Given the great response to my series on Blend Modes … I decided to quickly convert the library so that it could be compiled for the Silverlight platform. I pretty much copied how the WPF Pixel Shader Effect Library did it. Oh, and I renamed a bunch of things too.

So, here it is … the new and improved blend mode effects library … now with a little Silverlight love!

p.s. And here are the WPF binaries and here are the Silverlight binaries.

p.s.s. I now have a live Silverlight test harness (you will need the Silverlight 3.0 runtime, 3.0.40624).

Blend Modes, Part II

In Part I of this series, I showed how I was able to eventually figure out how to create the Linear Burn blend mode effect and how to apply this effect to a Border element.

In Part II of this series, I am going to start off by showing how I was able to truly verify that I had written the Linear Burn blend mode effect correctly. And, then, I will share a few of the A + B = R gradient squares for a few of the blend modes that Paul Dunn’s post didn’t have. Finally, I will share the full blend mode effect library and the test harnesses that go with it.

So … here we go …

With the Linear Burn blend mode effect that I had created, it bothered me that the gradient that I had blended with the green … was white to gray … and not white to black as Robby’s post had shown. Was I doing things correctly? Was the HLSL that I written, right?

A little more searching and I ran into Paul Dunn’s post on Photoshop’s blend modes. Ah! Perfect! Exactly what I wanted, and sure enough, I had written Linear Burn correctly. Check out the screen shot below (from my test harness) and compare it to what you see on Paul Dunn’s post. A match. Whew!

linearburnverified.png

 

At that point, I started creating and going through every blend mode in his blog post, verifying each along the way. This went along fine until I hit the Vivid Light blend mode. The math that he had just wasn’t working. I tried to figure out what was wrong with it, but eventually I ran into another post that had a different way of expressing the math … it was in HLSL! Plugging in this HLSL worked! And what’s better, he had some additional effects that could be added to the library I was creating. And, so, I finished it off.

Now, as mentioned above, mouaif’s post had some blend mode effects that Paul Dunn’s post didn’t show visually. And, so, I will share those here:

Glow Effect

gloweffect.png

 

 

Reflect Effect

  reflecteffect.png

 

Hard Mix Effect

 hardmixeffect.png

 

Negation Effect

 negationeffect.png

 

Phoenix Effect

 phoenixeffect.png

 

Finally, Nathan’s post had a visual way of verifying things as well (near the bottom) and so I included in my test harness a window that let’s you apply the various blend mode effects to images from Nathan’s page. Here’s a screen shot of that window (with the Phoenix blend mode effect applied … which I think looks cool):

Test Harness Window (Images)

 

Without any further adieu, here’s the blend mode effects library and the test harness. All the same caveats apply to what you need on your machine to build the sample code (.NET 3.5 SP1, DirectX SDK, and the Shader Effects BuildTask and Templates from the WPF Futures stuff on CodePlex).

If you don’t want to build the library and test harness yourself … here are the binaries.

Also, I now have a live Silverlight test harness (you will need the Silverlight 3.0 runtime, 3.0.40624). 

Enjoy!

p.s. Thanks to Kevin Moore for the Color Picker that I used in this sample code. I believe I created that Color Picker from an article he did at one point. In fact, I think that some variant of this Color Picker ended up in his Bag-o-Tricks.

Blend Modes, Part I

A while back (now some time ago), I was inspired by Robby Ingebretsen’s post on blend modes that were made possible with pixel shader effects, a new feature of .NET 3.5 SP1. In his post, he suggested that someone take a weekend and create a library of these effects for the community to use. Well, a little late, but I have eventually gotten around to this and done so … because I wanted to give a designer I work with some power tools that he is familiar with in applications like Photoshop.

In this trio (1, 2, 3) of blog posts, I will not be covering how to create a shader effect in WPF, Greg Schlecter has a great introductory series on that already. In particular, it is the multi-input shader effects that make blend modes possible, so be sure to read all the way through his series.

In order to write this library of blend mode effects, I first needed to find the math behind them. Then, I needed to convert this math into the proper HLSL. And finally, I needed some way to verify that the HLSL that I had written … was actually correct. I will cover these topics in the first two blog (first, second) posts.

In the third blog post, I will show how you can use Jeremiah Morrill’s GlassBehavior to blend two shapes with differing geometries.

So … let’s jump in.

I quickly started off with a simple Google search and it led me to Nathan Moinvaziri’s post which is all the blend mode math in C. So, I decided to buckle down and see if I could do Linear Burn … which is the subject of Robby’s post. Nathan had the math (for each channel) for Linear Burn as:

#define ChannelBlend_Subtract(B, L)
     ((uint8)((B + L < 255) ? 0 : (B + L – 255)))
#define ChannelBlend_LinearBurn(B, L)
     (ChannelBlend_Subtract(B, L))

 

Just plugging in the macro definitions into the .fx file didn’t work and through some trial and error, I discovered that the correct HLSL was:

inputColor.r = inputColor.r + blendColor.r - 1;
inputColor.g = inputColor.g + blendColor.g - 1;
inputColor.b = inputColor.b + blendColor.b - 1;

 

This finally gave me the blending Robby had talked about in his post:

Linear Burn

Following is the xaml for the above rectangle which is using the Linear Burn blend mode effect:

<Border
    Width="300"
    Height="100"
    Margin="0,10"
    Background="#FF6AB400"
>
    <Border.Effect>
        <bme:LinearBurnEffect>
            <bme:LinearBurnEffect.Texture>
                <ImageBrush>
                    <ImageBrush.ImageSource>
                        <DrawingImage>
                            <DrawingImage.Drawing>
                                <GeometryDrawing>
                                    <GeometryDrawing.Geometry>
                                        <RectangleGeometry Rect="0,0,1,1"/>
                                    </GeometryDrawing.Geometry>
                                    <GeometryDrawing.Brush>
                                        <LinearGradientBrush
                                            StartPoint="0,0"
                                            EndPoint="0,1"
                                        >
                                            <GradientStop
                                                Color="#FFFFFFFF"
                                                Offset="0"
                                            />
                                            <GradientStop
                                                Color="#FF808080"
                                                Offset="1"
                                            />
                                        </LinearGradientBrush>
                                    </GeometryDrawing.Brush>
                                </GeometryDrawing>
                            </DrawingImage.Drawing>
                        </DrawingImage>
                    </ImageBrush.ImageSource>
                </ImageBrush>
            </bme:LinearBurnEffect.Texture>
        </bme:LinearBurnEffect>
    </Border.Effect>
</Border>

As you can see above, it is rather easy to apply the blend mode effect. Each blend mode effect takes two shader effect inputs (where input is a shader effect sampler input).

The first input is the element it is set on which is in this case the Border element with the #FF6AB400 background.

The second input is the ImageBrush (it can also be a VisualBrush) that is set on the Texture property. Notice that the alpha channels for the GradientStop(s) are set 100% and not 50% like the Border with opacity overlay (see the attached code, .zip file below). That is, the two layers are being blended by the blend mode effect and not with opacity.

Another thing to mention is that the first input maps to A in the picture above and the second input maps to B in the picture above. R maps to the blend result which is what the user sees. In the next post, I will use this mapping (A + B = R) to illustrate what each blend mode effect does.

Here is the code for this post and here are the binaries. Also, I now have a live Silverlight test harness (you will need the Silverlight 3.0 runtime, 3.0.40624).

Enjoy!

(To build the above sample code, you need .NET 3.5 SP1 and you need a version of the DirectX SDK installed on your machine. Besides all that, you must also install the Shader Effects BuildTask and Templates from the WPF Futures CodePlex site.)

Code to Work Along With While Watching Jason Dolinger’s M-V-VM Video

I meant to post this to my blog sometime ago … but I forgot. I was recently reminded about it when a fellow software engineer I work with was looking for good information on M-V-VM.

I pointed him to my answer to a StackOverflow question on the matter and then pointed him to Jason’s video … sending him at the same time the code that I captured while working through this great video/screen cast. But, hey, why not share this with the wider community?!

What’s great about this sample code is that I’ve captured ‘snapshots’ along every step of the way.

Enjoy, and I would appreciate you leaving a comment if you find it useful. It’s always fun to know that you’ve helped someone.

p.s. I didn’t start out at ground zero for this code, Robert’s code helped me get started. So, thanks Robert!

WPF & Memory Leaks

The Red Herring:

Recently, I had to track down why all of our new WPF screens were leaking memory like a sieve. A coworker of mine ran into a discussion on the WPF forum and my jaw dropped. I rapidly dove into this issue and soon discovered that this issue was not really the one causing us problems (that I could tell). It seems as if this issue only raises its ugly head in certain situations (see the post for more detail).

The Awesome Tool:

memprofiler.png

At this point, we ran into a tool that, in my opinion, is a must have if you are trying to find memory leaks in a .NET application. It is called (aptly enough), .NET Memory Profiler and it is made by a company called SciTech software. Previously, we had been using AQtime and I still would recommend that tool for performance profiling but when it comes to memory allocation profiling (i.e. memory leaks), it is quite slow. The .NET Memory Profiler doesn’t do performance profiling and focuses exclusively on memory allocation profiling. I think this has made it the best tool out there. I have tried Jet Brains dotTRACE as well, which is also very fast, but the .NET Memory Profiler beats that one hands down in how it presents the information to the user and how it allows you to navigate through all the objects that have been created or are still living on the heap. They do offer a free 14-day trial and I would recommend that you try their 3.5 version which you can preview now. This version has a very nice feature that they call Automatic Memory Analysis, which is basically tips and warnings regarding common memory usage issues.

The Real Issue:

Using this tool and some grit, the previously mentioned coworker and I finally ran into the issue that was causing us problems. It is a known issue. Personally, I love what it says at the knowledge base article just referenced:

When you use data binding in Microsoft Windows Presentation Foundation (WPF), a memory leak may occur.

Ha! Are you kidding me?!

You can check out the knowledge base article for more info, but basically, if you are binding to something that isn’t a DependencyProperty or doesn’t implement INotifyPropertyChanged, WPF tries to use the ValueChanged event. In doing so, it calls PropertyDescriptor.AddValueChanged which creates a strong reference from the property descriptor to the object being bound to.

Something as simple as the following xaml causes a leak:

<Label Name="MyLabel">
    <Stack Panel Name="MyStackPanel">
        <TextBlock Text="{Binding ElementName=MyStackPanel, Path=Children.Count}" />
    </StackPanel>
</Label>

One way that you can verify that this issue is causing you problems is to see PropertyDescriptor in the root path. Check out this screen capture:

rootpath.png

The Solution:

So, how did we rid ourselves of this problem? Well, we basically went in and modified the objects that were causing us leaks to inherit from INotifyPropertyChangedeven if we didn’t really want to notify anyone of changes. In the those cases, we would #pragma the CS00067 warning away that came from not using the PropertyChanged event:

public class SomeClassBeingDataBoundTo : INotifyPropertyChanged
{
    // class implementation

#pragma warning disable 0067
    // If you data-bind to a regular .NET property on a regular .NET object, that object will never get garbage-collected.
    //    •    Dependency properties are not affected.
    //    •    Objects implementing the INotifyPropertyChanged interface are not affected.
    public event PropertyChangedEventHandler PropertyChanged;
#pragma warning restore 0067
}

How the heck did I miss this?

So, after all the excitement subsided, I did a Google search to see if I could find any other blogs that talks about this problem.  Heh heh. The link at the top of search results … was about our issue. Argh! I don’t know how I missed it before when I was doing my original research.

Even better, the search results brought up Jossef Goldberg’s priceless post on finding memory leaks in WPF applications (that I’ve run into before). That post is a must read if you are trying to find leaks in your WPF applications. And, yes, it too, talks about our issue.

Someone, please take my software engineering card away.

I hope my pain is someone else’s gain.