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中的资源,在整个程序运行周期中,都应该尽量保留,以节省资源。
下面是运行结果: