Winform中的2D绘图,以前用的都是GDI+。在简单应用环境下,如果不在乎速度,GDI+可以表现的很好。机缘巧合,前段时间做了一个简单的3D程序,使用的是C#托管 + DirectX9 SDK,平台是Win7 + VS2012。 项目做完以后,回顾发现,其实也可以使用directX11的Direct2D来进行2D绘图,这样可以尽量使用硬件加速,尽量解放CPU,使程序可以应用于更多场合。

Direct2D的理想环境是MFC/C++;如今的我只有在编写驱动程序时会用C++,对于已经习惯使用C#编写Winform的我来说,使用MFC开发Direct2D不是不可以,但是毕竟抛开MFC已经很久了(那还是VisualC++5.0时的回忆)。。。

在C#中使用Direct2D,目前看来,最合适的工具就是SharpDx,sdx可以说是目前对DirectX的最好封装,而且免费;其最大的缺点是,可以利用的文档实在太少了。闲话少叙,下面就从SharpDx开始:


一、首先从官网下载sdx(我用的时基于Desktop的应用),找到那一堆dll

二、建立一个新工程,本例是创建一个基于SharpDx的Progress的控件。

注:我使用的sdx是3.02版本,新建的c#工程需要定义为.Net FrameWork 4.5

0,准备工作:sdx中,无法使用C#的Color,Rectangle。。。因此需要做一个转换类

    
    public class dxc{public static SharpDX.Mathematics.Interop.RawColor4 ToColor4(Color cor){return new SharpDX.Mathematics.Interop.RawColor4((float)cor.R / 256f, (float)cor.G / 256f, (float)cor.B / 256f, (float)cor.A / 256f);}public static SharpDX.Mathematics.Interop.RawRectangle ToRect(Rectangle rect){return new SharpDX.Mathematics.Interop.RawRectangle(rect.Left, rect.Top, rect.Right, rect.Bottom);}public static SharpDX.Mathematics.Interop.RawRectangleF ToRectF(RectangleF rect){return new SharpDX.Mathematics.Interop.RawRectangleF(rect.Left, rect.Top, rect.Right, rect.Bottom);}}

sdx中使用的Color,Rectanglef等,大都在SharpDX.Mathematics.Interop空间中,上面dxc中静态函数的作用就是做最基本的转换。


在sdx中,颜色RawColor4的定义是R/G/B/A,各个分量的取值是0~1

在sdx中,RawRectangleF使用的比较多。


1,新建一个基于Control的类

[ToolboxItem(true), ToolboxBitmap(typeof(System.Windows.Forms.ProgressBar))]
public class dxProgressBar : Control

2,添加属性

     
        int _pValue = 0;public int Value{get { return _pValue; }set{_pValue = value;if (_pValue > 100) _pValue = 100;if (_pValue < 0) _pValue = 0;this.Refresh();}}Color _pBorderCor = Color.Black;public Color BorderColor{get { return _pBorderCor; }set{_pBorderCor = value;Refresh();}}Color _pTextColor = Color.Blue;public Color TextColor{get { return _pTextColor; }set{_pTextColor = value;Refresh();}}


3,构造函数 

   
        public dxProgressBar(){InitD2D();this.SizeChanged+=dxProgressBar_SizeChanged;}

initD2D的作用是初始化sdx设备,后面有它的函数原型。

三、下面是绘图相关处理

1,添加引用

using SharpDX.DXGI;
using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using SharpDX.DirectWrite;
using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using DxFactory = SharpDX.Direct2D1.Factory;
using TxFactory = SharpDX.DirectWrite.Factory;

2,添加变量声明


        DxFactory dxFactory;WindowRenderTarget dxTarget;PixelFormat pixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied);SolidColorBrush dxBorderBrush;SolidColorBrush dxValueBruseh;RawRectangleF dxBorderRectf;RawRectangleF dxValueRectf;TxFactory txFactory;TextFormat txFormat;TextLayout txLayout;SolidColorBrush txtBrush;

本例中,用于绘图的资源、变量都使用dx开头;而用于绘制文本的都使用 tx开头。


3,变量初始化函数


        private void InitD2D(){dxFactory = new DxFactory(SharpDX.Direct2D1.FactoryType.SingleThreaded);HwndRenderTargetProperties properties = new HwndRenderTargetProperties();properties.Hwnd = this.Handle;properties.PixelSize = new SharpDX.Size2(this.Width, this.Height);properties.PresentOptions = PresentOptions.None;dxTarget = new WindowRenderTarget(dxFactory, new RenderTargetProperties(pixelFormat), properties);dxTarget.AntialiasMode = AntialiasMode.PerPrimitive;dxTarget.TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode.Cleartype;txFactory = new TxFactory();txtBrush = new SolidColorBrush(dxTarget, dxc.ToColor4(_pTextColor));txFormat = new TextFormat(txFactory, this.Font.Name, this.Font.Size) { TextAlignment = TextAlignment.Center, ParagraphAlignment = ParagraphAlignment.Center };}


Direct2D1.Factory可以声明单线程或者多线程。

WindowRenderTarget 是要绘制图像的目标。

SolidColorBrush 是画笔,Direct2D不使用Pen,它同样使用画笔来绘制轮廓。

TxFactory、TextFormat、TextLayout等,用于绘制文本。


每次控件大小变更,则重新分配资源


        int px, py;private void dxProgressBar_SizeChanged(object sender, EventArgs e){InitD2D();Rectangle _txRect = this.ClientRectangle;_txRect.Inflate(-1, -1);dxBorderRectf = dxc.ToRectF(_txRect);Refresh();}

dxBorderRectf是控件的轮廓区域。


4,渲染

      
        private void Render(){dxTarget.Clear(dxc.ToColor4(this.BackColor));dxBorderBrush = new SolidColorBrush(dxTarget, dxc.ToColor4(_pBorderCor));dxTarget.DrawRectangle(dxBorderRectf, dxBorderBrush, 2);px = Width * _pValue / 100;py = this.Height-2;dxValueRectf = new RawRectangleF(2, 2, px, py);dxValueBruseh = new SolidColorBrush(dxTarget, dxc.ToColor4(ForeColor));dxTarget.FillRectangle(dxValueRectf, dxValueBruseh);txLayout = new TextLayout(txFactory, _pValue.ToString("F2") + "%", txFormat, 60, 25);dxTarget.DrawText(_pValue.ToString("F2") + "%", txFormat, dxBorderRectf, txtBrush, DrawTextOptions.Clip);}


Render函数,首先清空绘图区域;初始化画笔,绘制外形轮廓;计算进度条举行区域,并绘制当前进度;打印当前进度值。


5,用OnPaint对控件绘图


        protected override void OnPaint(PaintEventArgs e){//base.OnPaint(e);dxTarget.BeginDraw();Render();dxTarget.EndDraw();}


使用sdx绘图,都要以BeginDraw开始,以EndDraw结束,这个跟D3D很像。

sdx中的资源,在整个程序运行周期中,都应该尽量保留,以节省资源。


下面是运行结果:

GDI+的替代者—D2D-编程之家