Skip to content

Instantly share code, notes, and snippets.

@robmikh
Created April 6, 2016 02:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robmikh/a05c07c53ab0d227b07da4a13d53b0e9 to your computer and use it in GitHub Desktop.
Save robmikh/a05c07c53ab0d227b07da4a13d53b0e9 to your computer and use it in GitHub Desktop.
Using the InvertEffect with Windows.UI.Composition
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
InitComposition();
}
//
// Normally you want to cache and reuse most of these objects,
// but we won't for the purposes of this demo.
//
private void InitComposition()
{
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var visual = compositor.CreateSpriteVisual();
visual.Size = new Vector2(675, 248);
visual.Offset = new Vector3(50, 50, 0);
// Can be found at https://github.com/robmikh/compositionimageloader
var imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);
var surface = imageLoader.LoadImageFromUri(new Uri("http://ncmedia.azureedge.net/ncmedia/2014/10/MSFT_logo_rgb_C-Gray.png"));
var brush = compositor.CreateSurfaceBrush(surface);
IGraphicsEffect graphicsEffect = new InvertEffect
{
Name = "invertEffect",
Source = new CompositionEffectSourceParameter("image")
};
var effectFactory = compositor.CreateEffectFactory(graphicsEffect);
var effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("image", brush);
visual.Brush = effectBrush;
ElementCompositionPreview.SetElementChildVisual(this, visual);
}
}
@robmikh
Copy link
Author

robmikh commented Apr 6, 2016

Using the InvertEffect with Windows.UI.Composition

Getting a Compositor and creating a Visual

You can think of the Compositor as the gateway into the Composition APIs. The Compositor creates all composition related objects, so getting one is pretty important. When writing a XAML app, you can use ElementCompositionPreview.GetElementVisual(yourUIElementHere) to get back a Visual that represents the UIElement you pass in. Most compositon objects have a pointer back to their Compositor, so you just have to ask the Visual for the Compositor and you're ready to go!

Creating a visual is pretty easy:

var visual = compositor.CreateSpriteVisual();

visual.Size = new Vector2(675, 248);
visual.Offset = new Vector3(50, 50, 0);

This creates a new SpriteVisual that you can manipulate. Keep in mind that for visuals to show up on the screen they need to be attached to the Visual Tree. To do this in a XAML application, you can attach a visual to be the child of any UIElement using ElementCompositionPreview.SetElementChildVisual(yourUIElementHere, visual). Visuals can also be children of other Visuals as long as the parent inherits from ContainerVisual (such as SpriteVisual).

Loading content

Currently loading content is done through something like Direct2D or Win2D. I've written an image loader for use with the Composition APIs that you can find here. You'll find that there is a NuGet package for your convenience. The ImageLoader is largely a wrapper for Win2D. Using it is pretty easy:

// Can be found at https://github.com/robmikh/compositionimageloader
var imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);

var surface = imageLoader.LoadImageFromUri(new Uri("http://ncmedia.azureedge.net/ncmedia/2014/10/MSFT_logo_rgb_C-Gray.png"));
var brush = compositor.CreateSurfaceBrush(surface);

Keep in mind that you really should only be making on ImageLoader, so in a real application you'd want to store it somewhere to be able to reuse it later. Something to keep in mind with the Composition APIs is that you are susceptible to having your graphics device being lost. You can read about that DirectX concept here. What this means is that when the device is lost you need to redraw your content. CompositionImageLoader can take care of that for you through the ManagedSurface object. We won't use it here, but it is easy to consume (check out the GitHub page to learn more).

Creating an EffectFactory

Now for what you're here for. Keep in mind that while we're using the InvertEffect for this demo, there are many more effects that are supported. You can find a list of supported effects here.

The first step is to create the effect description:

IGraphicsEffect graphicsEffect = new InvertEffect
{
    Name = "invertEffect",
    Source = new CompositionEffectSourceParameter("image")
};

The name property is largely used when you want to animate an effect property, which is a different topic that you can see in action here. Next is to set the source as a CompositionEffectSourceParameter and to give it a name so that you can refer to it later. After you have the description, create an effect factory for it:

var effectFactory = compositor.CreateEffectFactory(graphicsEffect);

Your effect is now compiled, so whenever you want to create a new InvertEffect all you have to do is ask the effect factory to create a new brush for you to assign to your Visual. The factory is another object you'll want to keep around if you intend to use more effects of the same time. What is left is to create an effect brush and set the image to the image we generated earlier:

var effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("image", brush);

Now you're free to attach the brush to your Visual:

visual.Brush = effectBrush;

After you attach your visual to the tree you should see your inverted image!

Final Product

Final Product

What's Next?

You'll probably want to roll this into a custom control if you don't want your Visual just floating there on the page. ElementCompositionPreview.SetElementChildVisual can take any UIElement, so keep that in mind. By creating a custom control you can get the benefits of layout and ease of use from XAML and the power of the Composition APIs. For more information please come by and visit our GitHub page! There you can find more examples, including more effects and how to animate them, as well as a custom CompositionImage control!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment