This is part 2 of a 3 part series about Win2D image effects. I strongly recommend you start with part 1 if you haven’t already read it.
Can I combine multiple image effects?
Yes! In fact, combining effects is one of the most powerful things you can do with the API. As mentioned in Part 1, an effect is itself a valid effect input. This means that you can create a pipeline of effects. In addition, some effects support more than one input and all effects can be used as the input to any number of other effects. Therefore, you can combine effects into complex effect graphs; in general, any directed acyclic graph is valid.
Some of Win2D’s built-in effects are actually just wrappers around graphs of other effects. We do this to provide greater convenience for some commonly used effect scenarios. For example, shadow wraps a Gaussian blur and color matrix.
Example 3: Glowing text
This time, we’ll do something a little more complex with our “Effects are cool” text from Part 1. We will add a red/orange glow around the text. This requires a series of operations:
Here is a condensed description of what the effect graph is doing:
- The morphology effect thickens the lines of the text.
- Our good friend Gaussian blur smoothens out the text’s outline.
- The color matrix effect colorizes the text from black to red to orange as its alpha value increases (a sort of “alpha to luminance” effect).
- The composite effect combines the glow with the original text bitmap.
The corresponding code in Canvas_Draw is:
var myTextBitmap = new CanvasRenderTarget(sender, 500, 100);
using (var ds = myTextBitmap.CreateDrawingSession())
ds.Clear(Color.FromArgb(0, 0, 0, 0));
ds.DrawText(“Effects are hot!”, 0, 0, Colors.White, new CanvasTextFormat
FontSize = 24,
FontWeight = Windows.UI.Text.FontWeights.Bold
var effectGraph = new CompositeEffect
Source = new GaussianBlurEffect
Source = new MorphologyEffect
Source = myTextBitmap,
Mode = MorphologyEffectMode.Dilate,
Width = 7,
Height = 1
BlurAmount = 3f
ColorMatrix = new Matrix5x4
M11 = 0f, M12 = 0f, M13 = 0f, M14 = 0f,
M21 = 0f, M22 = 0f, M23 = 0f, M24 = 0f,
M31 = 0f, M32 = 0f, M33 = 0f, M34 = 0f,
M41 = 0f, M42 = 1f, M43 = 0f, M44 = 1f,
M51 = 1f, M52 = -0.5f, M53 = 0f, M54 = 0f
Just like before, in the first 8 lines we create the CanvasRenderTarget and draw text into it. This time we’ve made the text bigger using CanvasTextFormat to create a more visible final effect.
In the next block starting from var effectGraph = new CompositeEffectwe define the entire effect graph in one statement using initializer syntax. Effect inputs are treated like any other property and can be defined inline, so we can nest the definitions of the effects. You can “read” the entire definition by going from right to left (most indented to least indented):
- myTextBitmap is the input to MorphologyEffect
- MorphologyEffect is the input to GaussianBlurEffect
- GaussianBlurEffect is the input to ColorMatrixEffect
- ColorMatrixEffect and myTextBitmap are the inputs to CompositeEffect
Finally, we call args.DrawingSession.DrawImage(effectGraph). Note that we only pass in the CompositeEffect which is the final effect in the graph. Because we’ve defined the relationship between all of the effects in this graph, Win2D knows that to correctly render the CompositeEffect, it must first go and execute every other effect in the correct order.
How do I apply image effects to dynamic content/how do I animate effects?
If you only are using static visual content, you should consider using an image editor like Photoshop or GIMP to apply filter effects to your visual assets and then bake them into your application.
Win2D image effects really shine in dynamic scenarios, where you construct and manipulate your content at runtime. At any time you can alter and swap out the inputs to an effect and make arbitrary changes to the effect graph topology. You also can change the values of any property. This lets you animate your content in an almost infinite number of ways.
Example 4: Burning text
For this example, we’ll kick things up a notch. Let’s take the previous example’s glow and make it wavy like flames. Then, let’s animate the flames over time to make them move like a real fire. Finally, let’s allow the user to dynamically change the text that is being rendered and have the fire automatically adjust.
The entire effect graph looks like this:
From the previous example, we are already familiar with the parts in green. The new parts are in orange:
- The turbulence effect is a zero-input effect, or a source effect. Source effects are special in that they generate their content internally, and hence can be used as inputs to an effect graph. Turbulence generates a Perlin noise field, which is pseudorandom but has a “smooth” appearance.
- The border effect allows us to extend an image past its normal bounds, for example by tiling or mirroring. In this example, it is used as a performance optimization. We only generate a small region of turbulence because it is relatively expensive, and simply repeat the texture in order to obtain the entire area we need.
- The transform2D effect applies a 2D affine transform (scale, rotate, and/or translate). The first instance translates the Perlin noise upwards over time to make the flames’ move upwards. The second instance scales the flame 2x vertically.
- The displacement map effect takes two inputs, Source and Displacement. For every point in Source, it will shift the point by a 2D vector whose value comes from the corresponding point in Displacement. We use the Perlin noise to displace the glow, which turns it into wavy “flames” whose shape is based on the original text.
To demonstrate this example, we’ve added a “Burning Text” sample to the Example Gallery that is included in the Win2D source. To obtain and run this sample, follow the instructions to clone and build the Win2D GitHub project, and in Visual Studio run the ExampleGallery.Windows or ExampleGallery.WindowsPhone project. The code to implement Burning Text is contained within:
The most important methods to focus on are Canvas_Draw and UpdateAnimation:
private void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
private void UpdateAnimation(float elapsedMilliseconds)
flameAnimation.TransformMatrix = Matrix3x2.CreateTranslation(
-elapsedMilliseconds * 60.0f
flamePosition.TransformMatrix = Matrix3x2.CreateScale(
new Vector2(1.0f, 2.0f),
(float)this.canvas.ActualWidth / 2,
(float)this.canvas.ActualHeight / 2 + 20)
Every time we draw, we update the relevant effect properties with their new values and then draw the effect graph.
I’ve attached a video showing the resulting animation.