WPF高性能绘图的方法

原文: WPF高性能绘图的方法

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

一、使用GDI+绘制图形

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


  
  
  1. <Grid>
  2. <Grid.RowDefinitions>
  3. <RowDefinition Height="30"/>
  4. <RowDefinition/>
  5. </Grid.RowDefinitions>
  6. <StackPanel Orientation="Horizontal">
  7. <Button Content="绘制图形" Margin="5" Click="Button1_Click"/>
  8. <Button Content="操作像素" Margin="5" Click="Button2_Click"/>
  9. </StackPanel>
  10. <Canvas Grid.Row="1" Name="OutCanvas" Margin="5">
  11. <Image Name="DisplayImage"/>
  12. </Canvas>
  13. </Grid>

然后把WriteableBitmap对象作为这个Image的Source。只要我们改变WriteableBitmap,前台的画面也会相应地改变。


  
  
  1. width = ( int)OutCanvas.ActualWidth;
  2. height = ( int)OutCanvas.ActualHeight;
  3. if (width > 0 && height > 0)
  4. {
  5. DisplayImage.Width = width;
  6. DisplayImage.Height = height;
  7. wBitmap = new WriteableBitmap(width, height, 72, 72, PixelFormats.Bgr24, null);
  8. DisplayImage.Source = wBitmap;
  9. }

接下来,我们只要操作WriteableBitmap即可。要使用GDI+,我们需要先把WriteableBitmap的后台缓存交给Bitmap管理,然后使用Bitmap的Graphics进行绘制。


  
  
  1. wBitmap.Lock();
  2. Bitmap backBitmap = new Bitmap(width, height, wBitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, wBitmap.BackBuffer);
  3. Graphics graphics = Graphics.FromImage(backBitmap);
  4. graphics.Clear(System.Drawing.Color.White); //整张画布置为白色
  5. //画一些随机线
  6. Random rand = new Random();
  7. for ( int i = 0; i < 100; i++)
  8. {
  9. int x1 = rand.Next(width);
  10. int x2 = rand.Next(width);
  11. int y1 = rand.Next(height);
  12. int y2 = rand.Next(height);
  13. graphics.DrawLine(Pens.Red, x1, y1, x2, y2);
  14. }
  15. graphics.Flush();
  16. graphics.Dispose();
  17. graphics = null;
  18. backBitmap.Dispose();
  19. backBitmap = null;
  20. wBitmap.AddDirtyRect( new Int32Rect( 0, 0, width, height));
  21. wBitmap.Unlock();

二、基于像素的操作

假如我们需要做一些图像处理,例如是图像翻转、转成灰度图等,我们就需要操作图像的像素。

下面是对像素进行处理的方法:


  
  
  1. unsafe
  2. {
  3. var bytes = ( byte*)wBitmap.BackBuffer.ToPointer();
  4. wBitmap.Lock();
  5. //整张画布置为白色
  6. for ( int i = wBitmap.BackBufferStride * wBitmap.PixelHeight - 1; i >= 0; i--)
  7. {
  8. bytes[i] = 255;
  9. }
  10. //画一些随机的红点
  11. Random rand = new Random();
  12. for ( int i = 0; i < 10000; i++)
  13. {
  14. int x = rand.Next(width);
  15. int y = rand.Next(height);
  16. int array_start = y * wBitmap.BackBufferStride + x * 3;
  17. bytes[array_start] = 0;
  18. bytes[array_start + 1] = 0;
  19. bytes[array_start + 2] = 255;
  20. }
  21. wBitmap.AddDirtyRect( new Int32Rect( 0, 0, width, height));
  22. wBitmap.Unlock();
  23. }

示例源代码下载

猜你喜欢

转载自www.cnblogs.com/lonelyxmas/p/12783980.html
今日推荐