WPF高性能绘图的方法

如果我们只需要在画布中摆放少量的图形元素,那么直接使用Line、Rectangle这些对象是没有问题的。但当我们的图形元素数量巨大(比如说10万个),或是刷新频繁(比如说50毫秒刷新一次)时,WPF就会消耗大量的资源和出现卡顿的现象。为了解决这个问题,我们使用WriteableBitmap,用它作为绘图的基础画布。使用过GDI+的同学应该都听过双缓存,也就是我们先把复杂的绘图过程写到内存里,然后把内存里的内容一次性的贴到要显示的元素上。这种方法表现出来是耗资源少、画面流畅。但很多同学以为WPF抛弃了GDI+,也没办法再使用双缓存的方法。其实,WriteableBitmap就有两层缓存,我们操作完它的后台缓存后,也可以一次性地把后台缓存显示出来,跟GDI+的操作一样。而且,我们在WPF开发时,还是可以用GDI+方法去绘图的。

一、使用GDI+绘制图形

我们先在界面中增加一个Image:


    
        
        
    

    
        

然后把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();
}

示例源代码下载

你可能感兴趣的:(WPF)