【WPF】高性能绘图的方法
如果我们只需要在画布中摆放少量的图形元素,那么直接使用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(); }
编程是个人爱好