如果我们只需要在画布中摆放少量的图形元素,那么直接使用Line、Rectangle这些对象是没有问题的。但当我们的图形元素数量巨大(比如说10万个),或是刷新频繁(比如说50毫秒刷新一次)时,WPF就会消耗大量的资源和出现卡顿的现象。为了解决这个问题,我们使用WriteableBitmap,用它作为绘图的基础画布。使用过GDI+的同学应该都听过双缓存,也就是我们先把复杂的绘图过程写到内存里,然后把内存里的内容一次性的贴到要显示的元素上。这种方法表现出来是耗资源少、画面流畅。但很多同学以为WPF抛弃了GDI+,也没办法再使用双缓存的方法。其实,WriteableBitmap就有两层缓存,我们操作完它的后台缓存后,也可以一次性地把后台缓存显示出来,跟GDI+的操作一样。而且,我们在WPF开发时,还是可以用GDI+方法去绘图的。
一、使用GDI+绘制图形
我们先在界面中增加一个Image:
-
<Grid>
-
<Grid.RowDefinitions>
-
<RowDefinition Height="30"/>
-
<RowDefinition/>
-
</Grid.RowDefinitions>
-
-
<StackPanel Orientation="Horizontal">
-
<Button Content="绘制图形" Margin="5" Click="Button1_Click"/>
-
<Button Content="操作像素" Margin="5" Click="Button2_Click"/>
-
</StackPanel>
-
<Canvas Grid.Row="1" Name="OutCanvas" Margin="5">
-
<Image Name="DisplayImage"/>
-
</Canvas>
-
</Grid>
然后把WriteableBitmap对象作为这个Image的Source。只要我们改变WriteableBitmap,前台的画面也会相应地改变。
-
width = (
int)OutCanvas.ActualWidth;
-
height = (
int)OutCanvas.ActualHeight;
-
if (width >
0 && height >
0)
-
{
-
DisplayImage.Width = width;
-
DisplayImage.Height = height;
-
-
wBitmap =
new WriteableBitmap(width, height,
72,
72, PixelFormats.Bgr24,
null);
-
DisplayImage.Source = wBitmap;
-
}
接下来,我们只要操作WriteableBitmap即可。要使用GDI+,我们需要先把WriteableBitmap的后台缓存交给Bitmap管理,然后使用Bitmap的Graphics进行绘制。
-
wBitmap.Lock();
-
Bitmap backBitmap =
new Bitmap(width, height, wBitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, wBitmap.BackBuffer);
-
-
Graphics graphics = Graphics.FromImage(backBitmap);
-
graphics.Clear(System.Drawing.Color.White);
//整张画布置为白色
-
-
//画一些随机线
-
Random rand =
new Random();
-
for (
int i =
0; i <
100; i++)
-
{
-
int x1 = rand.Next(width);
-
int x2 = rand.Next(width);
-
int y1 = rand.Next(height);
-
int y2 = rand.Next(height);
-
graphics.DrawLine(Pens.Red, x1, y1, x2, y2);
-
}
-
-
graphics.Flush();
-
graphics.Dispose();
-
graphics =
null;
-
-
backBitmap.Dispose();
-
backBitmap =
null;
-
-
wBitmap.AddDirtyRect(
new Int32Rect(
0,
0, width, height));
-
wBitmap.Unlock();
二、基于像素的操作
假如我们需要做一些图像处理,例如是图像翻转、转成灰度图等,我们就需要操作图像的像素。
下面是对像素进行处理的方法:
-
unsafe
-
{
-
var bytes = (
byte*)wBitmap.BackBuffer.ToPointer();
-
wBitmap.Lock();
-
-
//整张画布置为白色
-
for (
int i = wBitmap.BackBufferStride * wBitmap.PixelHeight -
1; i >=
0; i--)
-
{
-
bytes[i] =
255;
-
}
-
-
//画一些随机的红点
-
Random rand =
new Random();
-
for (
int i =
0; i <
10000; i++)
-
{
-
int x = rand.Next(width);
-
int y = rand.Next(height);
-
int array_start = y * wBitmap.BackBufferStride + x *
3;
-
-
bytes[array_start] =
0;
-
bytes[array_start +
1] =
0;
-
bytes[array_start +
2] =
255;
-
}
-
-
wBitmap.AddDirtyRect(
new Int32Rect(
0,
0, width, height));
-
wBitmap.Unlock();
-
}