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!

Silverlight 3 Launches and Blend 3 RC is Released

Today is the launch of Silverlight 3! Also being released is a release candidate of Expression Blend 3!

Here is the official launch site and here is a new Microsoft site on Silverlight. The silverlight.net site has been updated as well.

One of my favorite new features of Silverlight 3 is, of course, Pixel Shader Effects. The Microsoft Silverlight site has a great overview of the main features and one of those sections is on these GPU rendered effects. Here is a link to a Silverlight 3 application that lets you play with them.

In honor of the Silverlight 3 launch, I have updated my test harness for my blend mode effects library. These effects, of course, are implemented with the new Pixel Shader Effects.

And, let’s not forget Blend! Here is Christian Schormann’s blog post announcing the Expression Blend 3 RC. In this version of Expression Blend, Sketch Flow is finally being released to the general public. This is an awesome tool that is geared toward the early, conceptual design phase and enables one to quickly mock up concepts allowing you to explore ideas at a much lower cost. SketchFlow is just the tip of the iceberg of what’s new in Blend 3. I personally just love the usability improvements (for example, the direct selection improvements) … but what about behaviors (1, 2, 3)? Whoa! Can we say powerful?!

Here is the link to download the Silverlight 3 Tools for Visual Studio 2008 SP1.

Here is the link to download the Expression Blend 3 + SketchFlow RC.

Here is the link to latest version of the Silverlight Toolkit (July 2009) which also released in conjunction with the Silverlight 3 release.

A bunch of blog posts are hitting the internet about these releases and I see that Scott Guthrie’s highly anticipated post has finally been published.

Woot! Woot! Go Silverlight 3 and Blend 3!

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!

Kaxaml: Don’t Leave Home Without It

KaxamlLogo

Ok, I just have to give a shout out to one of the best tools out there for authoring loose xaml. And, if the logo didn’t give it away … it’s Robby Ingebretsen’s Kaxaml tool. It is available here for download … free of charge!

It’s been out for quite some time … so the purpose of this blog post is really to just publicly thank him for making such an awesome tool … one, in fact, that I don’t leave home without.

Recently, I have been composing Haikus … just because a tool like this … deserves poetry … and poetry better than mine!

But, in case you haven’t heard about it or haven’t played with it much … here is some quick info. I believe you pronounce it <ka-zam-al> … and I like to think like Shazam! Pow! (I have not confirmed this was the intent of its author … it might just be the meanderings of this crazy mind of mine.)

It has a pretty sweet list of features:

  • A snippet library with the ability to insert snippets via shortcut
  • IntelliSense
  • A folding editor
  • An integrated color picker
  • A xaml scrubber (or pretty printer or formatter or whatever you want to call it)
  • The ability to zoom in and out on your rendered xaml
  • The ability to make your xaml larger (use ctrl-wheel)
  • Handles both WPF and Silverlight
  • And more!
    Bonus: Robby recently recorded several videos that will bring you up to Kaxaml power user status in a matter of minutes. Check them out.