Introduction to Win2D

November 19, 2014

3 comments

The Windows Runtime UI stack uses XAML for general 2D layout and graphics. It provides various controls, such as TextBox, ItemsControl and DatePicker. It even provides shape-like elements such as Line, Ellipse, Rectangle and Path. However, the XAML layout and rendering engine, while flexible, may not be performant enough for certain kind of applications and games. Also, it does not support general “drawing” functions (WPF for example, does provide that with the DrawingContext class).

Win2D is a new Windows Runtime library that is currently in development by Microsoft that provides a WinRT wrapper over Direct2D. Direct2D is a DirectX family member for doing 2D graphics but it’s only readily accessible to C++ developers. And even for C++ developers Direct2D may be too low level, with many little details that must be taken care of properly.

Other wrappers for Direct2D exist For .NET developers, such as the SharpDX library. But this is a thin wrapper and still requires handling low level details; and it’s only for .NET developers. What about C++ developers?

This is where Win2D comes in. It’s an immediate mode wrapper over Direct2D. What does “immediate mode” mean? The XAML engine by contrast uses “retained mode”. Retained mode means the developer does not need to “maintain” the graphics in any way – she just places elements and controls and sets various properties – the XAML rendering engine takes care of all the rest – drawing and refreshing when needed.

Immediate mode, on the other hand, leaves everything to the developer in the sense that the she must draw everything each frame from scratch – no one maintains the drawing instructions. The advantages of immediate mode are speed and control.

Win2D is currently hosted as open source on GitHub. It is now also available as a NuGet package, so is no easy to install and no source building is required. Let’s see how to get started with Win2D.

Creating the project

Create a new Windows Runtime project – Windows 8.1, Windows Phone 8.1 or Universal. I’ve created a C# project, but C++ and VB would also work. Next, right click the project node in Solution Explorer (or the References node) and select “Manage NuGet Packages…”. Search for “Win2d” and make sure that you include pre-release packages in the search:

image

The Win2D component is named Microsoft.Graphics.Canvas. The easiest way to start is with the CanvasControl class (not to be confused with the standard Canvas layout panel), which is a user control that encapsulates a Direct2D drawing surface that can be placed anywhere in XAML.

CanvasControl adds very few members to UserControl. The most important is a Draw event that is raised any time the control should be refreshed. The Invalidate method forces a refresh (i.e. raising the Draw event) because of some application logic change. This means the Draw event is not steadily fired at a certain rate – calling Invalidate is mandatory if some logic is running in the background.

For this demo, I wanted to draw and move some circles. For this I created a simple circle data class like so:

class CircleData {
	public float Radius { get; set; }
	public Vector2 Center { get; set; }
	public Color Color { get; set; }
	public float Thickness { get; set; }
	public Vector2 Speed { get; set; }
}

Vector2 is a simple struct managing a 2D point with float values (it’s very similar to the XNA Vector2 type).

The XAML I used is extremely simple:

<Grid>         <win2d:CanvasControl ClearColor="Black" Grid.Row="1" Draw="OnDraw" x:Name="_canvas"                          Tapped="OnTapped" Holding="OnHolding" />
</Grid>

Basically we have the CasnvasControl with the Draw event wired up as well as setting the background color to black. The Tapped event will be used to add a random circle. The Holding event will be used to clear all circles.

The MainPage class holds some state including the list of circles, with and height of the canvas control and a timer that is used to move the circles:

List<CircleData> _circles = new List<CircleData>();
DispatcherTimer _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(17) };
float _width, _height;
Random _rnd = new Random();

 

To actually compile successfully, you must select a x86 or ARM build – AnyCPU won’t work here.

The Draw event handler is the workhorse of the application:

private void OnDraw(CanvasControl sender, CanvasDrawEventArgs args) {
	var session = args.DrawingSession;
	foreach(var circle in _circles)
		session.DrawCircle(circle.Center, circle.Radius, circle.Color, circle.Thickness);
}

 

The DrawingSession property is of type CanvasDrawingSession, holding a multitude of methods for drawing purposes, from which DrawCircle is used here. Many more are included (with overloads) such as DrawEllipse, DrawImage, DrawLine, DrawRectangle, FillCircle, FillEllipse and a few others.

The timer handler code moves the circles based on their speed, reversing direction of they reach an edge:

_timer.Tick += (s, e) => {
	foreach(var circle in _circles) {
		var pos = circle.Speed + circle.Center;
		var radius = circle.Radius;
		if(pos.X + radius > _width || pos.X - radius < 0)
			circle.Speed = new Vector2(-circle.Speed.X, circle.Speed.Y);
		else if(pos.Y + radius > _height || pos.Y - radius < 0)
			circle.Speed = new Vector2(circle.Speed.X, -circle.Speed.Y);
		circle.Center = circle.Center + circle.Speed;
	}
	_canvas.Invalidate();
};

 

Notice the final Invalidate call.

Adding a new circle is done in the Tapped method handler:

private void OnTapped(object sender, TappedRoutedEventArgs e) {
	float radius = (float)_rnd.NextDouble() * 120 + 10;
	_circles.Add(new CircleData {
		Center = new Vector2((float)_rnd.NextDouble() * (_width - radius * 2) + radius, (float)_rnd.NextDouble() * (_height - radius * 2) + radius),
		Radius = radius,
		Speed = new Vector2((float)_rnd.NextDouble() * 10 - 5, (float)_rnd.NextDouble() * 12 - 6),
		Color = Color.FromArgb(255, (byte)_rnd.Next(1, 255), (byte)_rnd.Next(1, 255), (byte)_rnd.Next(1, 255)),
		Thickness = (float)_rnd.NextDouble() * 8 + 1
	});
}

Here’s a snapshot of the result (on the phone’s emulator):

circles

Of course, all circles are moving… it’s better to actually run it. Tap or click to add a circle, hold to clear everything.

Win2D looks promising. You can get more info on the Win2D team blog and the source code itself.

The sample can be downloaded from here.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

3 comments

  1. Pingback: Build 2015 Impressions | Pavel's Blog

  2. Pingback: Here Comes the Win2D | cuteprogramming

  3. MrMooseApril 2, 2016 ב 23:03

    This doc is missing some steps like adding dependencies to the top of the xaml sheet. I would suggest using this instead:

    http://microsoft.github.io/Win2D/html/QuickStart.htm

    Reply