【WPF】高效能繪圖的方法
阿新 • • 發佈:2022-05-30
如果我們只需要在畫布中擺放少量的圖形元素,那麼直接使用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(); }