Heute früh habe ich mir ein paar Videos von der diesjährigen Build angesehen und bin dabei auf den Talk „Introducing Win2D“ gestoßen [1]. Über diese API habe ich zwar schon vorher gelesen, habe sie mir bis jetzt aber noch nicht tiefer angeschaut, da ich Direct2D normalerweise per SharpDX direkt von C# aus verwende. Grundsätzlich finde ich es aber sehr gut, dass Microsoft selbst an einen Weg arbeitet, die Direct2D-API über Win2D auch für C#/.Net Entwickler verfügbar zu machen. Die Performance ist super und die API der von System.Drawing sehr ähnlich. Einzig die Tatsache, dass Win2D für Universal Apps ausgelegt ist, finde ich etwas schade. In Seeing# etwa binde ich auch Direct2D ein, mache das dann aber auch für Desktop-Plattformen, also Win.Forms, WPF und Universal/WinRT. Schnelles hardwarebeschleunigtes 2D-Rendering ist schließlich auch bei Desktop-Programmen interessant.
Seeing# baut vollständig auf Direct3D 11 auf, was eine Integration von Direct2D grundsätzlich möglich macht. Denn: Direct2D selbst nutzt ebenfalls Direct3D um damit von der Hardwarebeschleunigung moderner Grafikkarten zu profitieren. Technisch sieht das so aus, dass Direct2D beispielsweise direkt in eine Direct3D-Textur rendern kann. Folgendes Coding aus Seeing# legt ein Direct2D RenderTarget auf Basis einer Direct3D Textur an. Das vollständige Coding der Direct2DOverlayRenderer Klasse [2], von der dieses Coding stammt, ist etwas komplizierter, da die Integration in Windows 7 und im Falle von Software Rendering etwas anders aussehen kann.
// Try to create a Direct2D render target based on the Direct3D 11 texture directly // => This should work starting with windows 8 try { using (DXGI.Surface dxgiSurface = m_renderTarget3D.QueryInterface<DXGI.Surface>()) { m_renderTarget2D = new D2D.RenderTarget( m_device.Core.FactoryD2D, dxgiSurface, new D2D.RenderTargetProperties() { MinLevel = D2D.FeatureLevel.Level_10, Type = D2D.RenderTargetType.Default, //m_device.IsSoftware ? D2D.RenderTargetType.Software : D2D.RenderTargetType.Hardware, Usage = D2D.RenderTargetUsage.ForceBitmapRemoting, PixelFormat = new D2D.PixelFormat(GraphicsHelper.DEFAULT_TEXTURE_FORMAT, D2D.AlphaMode.Premultiplied), DpiX = dpiScaling.DpiX, DpiY = dpiScaling.DpiY }); m_graphics2D = new Graphics2D(m_device, m_renderTarget2D, scaledScreenSize); return; } } catch (Exception) { }
Das RenderTarget ist nichts anderes als das Graphics-Objekt aus dem System.Drawing Namensraum des .Net Framework. Um den Umgang mit Direct2D möglichst einfach zu machen, baue ich in Seeing# Step-by-Step kleine Wrapper um die Klassen von Direct2D, um sich beispielsweise möglichst wenig um die Verwaltung der Speicher-Ressourcen und die Verteilung derer auf die vorhandenen Grafikkarten machen zu müssen. Folgender Coding-Ausschnitt rendert etwa die Textur aus dem Screenshot oben.
// 2D rendering is made here m_solidBrush = new SolidBrushResource(Color4.Gray); m_textFormat = new TextFormatResource("Arial", 36); m_textBrush = new SolidBrushResource(Color4.RedColor); Custom2DDrawingLayer d2dDrawingLayer = new Custom2DDrawingLayer((graphics) => { RectangleF d2dRectangle = new RectangleF(10, 10, 236, 236); graphics.Clear(Color4.LightBlue); graphics.FillRoundedRectangle( d2dRectangle, 30, 30, m_solidBrush); d2dRectangle.Inflate(-10, -10); graphics.DrawText("Hello Direct2D!", m_textFormat, d2dRectangle, m_textBrush); });
Sieht doch sehr vertraut aus, oder? Das soll es auch sein. Alle Klassen zum Rendern von Direct2D-Inhalt befinden sich bei Seeing# im Namensraum SeeingSharp.Multimedia.Drawing2D [3]. Es ist längst nicht alles drin, was Direct2D kann. Aber die Wrapper selbst sind sehr dünn und schnell zu entwickeln. Ich selbst gehe so vor, dass ich entsprechend nach Bedarf neue Wrapper-Klassen hinzufügen.