1. 程式人生 > 其它 >【WPF】高效能繪圖的方法

【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();
}