Chances are if you are using Win2D in a complex app, you may have seen it crash sometimes with an unhandled exception with HResult 0x80004003 and a not very descriptive message containing only the phrase Invalid pointer.
If this is the case, take a second look at all your CanvasCreateResourcesEventArgs.TrackAsyncAction calls – because, while you may think this is a threading problem, the cause is far easier to solve: you are accessing a null object/property instead. Somehow Win2D is throwing an Exception with that error message instead of a NullReferenceException.
Recently I have worked in an UWP app that uses the new adaptive tiles to display both images and text without having to go through the old method of rendering a tile image with a XamlRenderingBackgroundTask. Everything worked flawlessly on my test devices, even on Release mode with .NET Native compilation enabled; however, when published through the Store, some users started complaining that the tile content didn’t update at all. The most strange thing was that the background task that took care of updating the live tile wasn’t failing at all – it wasn’t reporting any errors/exceptions and the tile badge count was updating without issues.
I started investigating a bit and there was something in common with these users – all of them were using either a Lumia 950 or a Lumia 950 XL. And after a bit of investigation, I found the cause: I was using scaled assets for the graphics shown on the Live Tile, but only including the scale-100 images – turns out the runtime wasn’t defaulting to this size in all cases, and the tile was failing to entirely update (it didn’t even show the text without images).
So, lesson learned: don’t use scaled asset in your adaptive tile, or if you have to, be sure to include ALL the supported sizes.
Blur effects are getting pretty common in app designs, especially since iOS 7 started introducing them in some OS components like Control Center – and even the Windows 10 Start Menu uses the same effect to display a “frosted glass” look over the current background window.
Instead of a simple blur, which has been already explained by the awesome Deani Hansen, we are going to implement an iris blur (as Photoshop calls it; other resources call it “blur around borders”, and Instagram names it “tilt shift” with a radial blur in an UWP app by using the awesome Win2D library. It consists of a circular or ellipsoid area where the image shows focused, but everything else around it is defocused (or “blurred”) progressively, to give it a nice fading effect with no solid transition. So, let’s get started!
To achieve the result shown above, we are going to draw a blurred version of the image (in our case, a very beautiful one taken from Windows 10’s Spotlight backgrounds) over the original one – however, it will have a transparency mask applied so it can show a “hole” where there is no blurring. For additional artistic result, the hole won’t have clearly defined borders – instead we are going to use a radial gradient so it blends progressively over the non-blurred image. The following image describes how the blurred image with mask applied will be obtained:
We could store the resulting image in a CanvasRenderTarget instance and then draw it to our CanvasControl when needed – however, we can skip this intermediate step if we directly draw the original image and then the blurred one on top, just like the followind diagram shows:
With all concepts clear, let’s go ahead with the implementation – I promise you it’s going to be really easy!
Start by adding the Win2D.UWP library to your project from NuGet, and creating a CanvasControl in XAML. Hook up its CreateResources event and instantiate a new member variable of type CanvasRadialGradientBrush – along with loading the image we are going to draw. Our radial brush will have Colors.Transparent as a starting colour and Colors.Black as the ending one – the ending one could have been Colors.White or any other solid one though, as the layer will only take the alpha (transparency) component into account.
private void Canvas_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
// Create instance of radial gradient brush; the center will be transparent and the extremes opaque black.
radialBrush = new CanvasRadialGradientBrush(sender, Colors.Transparent, Colors.Black);
// Load image to draw.
args.TrackAsyncAction(Task.Run(async () =>
image = await CanvasBitmap.LoadAsync(sender, new Uri("ms-appx:///SpotlightImage.png"));
Next, we are going to wire up the Draw event – and perform all of our drawing there. Start by clearing the CanvasDrawingSession’s background to a colour of your choice, and then set the Center, RadiusX and RadiusY parameters of the radial gradient brush to meaningful values (in our case the center is always the center of the image/canvas, and the radius will be updated through a Slider control); then, draw the untouched image through CanvasDrawingSession.DrawImage:
// Start drawing session and clear background to white.
var session = args.DrawingSession;
// Set the center of the radial gradient as the center of the image.
radialBrush.Center = new System.Numerics.Vector2((float)(image.Size.Width / 2.0f), (float)(image.Size.Height / 2.0f));
// Assing gradient radius from slider control.
radialBrush.RadiusX = radialBrush.RadiusY = (float)BlurRadius.Value;
// Draw unaltered image first.
Now comes the fun part – working with layers. The logic behind is that you create a layer by calling CanvasDrawingSession.CreateLayer and passing an opacity value, ICanvasBrush or CanvasGeometry and everything that is drawn from that point is affected by the opacity/mask/clipping area – pretty convenient, huh? Then, to stop using it, you just have to Dispose the CanvasActiveLayer – or instead, wrap everything inside an using block.
So, we are going to pass the CanvasRadialGradientBrush as the mask parameter, and then draw the image again but after applying a GaussianBlurEffect on it. This way, the masked, blurred version of the image will draw on top of the solid one, composing the desided effect:
// Create a layer, this way all elements drawn will be affected by a transparent mask
// which in our case is the radial gradient.
// Create gaussian blur effect.
using (var blurEffect = new GaussianBlurEffect())
// Set image to blur.
blurEffect.Source = image;
// Set blur amount from slider control.
blurEffect.BlurAmount = (float)BlurAmount.Value;
// Explicitly set optimization mode to highest quality, since we are using big blur amount values.
blurEffect.Optimization = EffectOptimization.Quality;
// This prevents the blur effect from wrapping around.
blurEffect.BorderMode = EffectBorderMode.Hard;
// Draw blurred image on top of the unaltered one. It will be masked by the radial gradient
// thus showing a transparent hole in the middle, and properly overlaying the alpha values.
session.DrawImage(blurEffect, 0, 0);
I told you it was easy, wasn’t it? And just for reference, here is a video of how the effect looks in the sample project that complements this tutorial: