這是在博客園的第一篇文章
如果你在尋找能夠快速在image控件刷新大圖像(比如分辨率3000*3000的圖像)的辦法,尤其是想把內存中的裸數據(只有圖像的數據,不包含圖像頭等信息)快速顯示到界面,那么你來對地方了,看完這篇博客會解決困擾了你一天,或者一個禮拜,或者一年,或者一輩子的問題,時間的長短取決於你看到這篇博客的時間。
請注意:如果本篇博客對於解決你的問題起到了決定性的作用,那么請在你的代碼里加上以下兩行內容,請尊重別人的努力。轉載請注明出處
// provide by zhangshaohui
// 本文網址
以下是正文:
在你尋找解決方案的過程中,一定看到過這樣的代碼:
1、這個代碼最常見,網上到處都是,的確可以用,也簡單清晰,但是速度太慢,顯示一個3000*3000的大概要40ms,我跟蹤了一下代碼,主要是new stream,以及EndInit比較耗時,但是用這個方法又繞不過去這兩行代碼。
public BitmapImage BitmapToBitmapImage(Bitmap bitmap) { using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); stream.Position = 0; BitmapImage result = new BitmapImage(); result.BeginInit(); result.CacheOption = BitmapCacheOption.OnLoad; result.StreamSource = stream; result.EndInit(); result.Freeze(); return result; } }
2、這個也是常見的辦法,好像還是msdn上推薦的,缺點是更慢
public static ImageSource ChangeBitmapToImageSource(Bitmap bitmap) { IntPtr hBitmap = bitmap.GetHbitmap(); ImageSource wpfBitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); if (!APIConverter.DeleteObject(hBitmap)) { throw new System.ComponentModel.Win32Exception(); } return wpfBitmap; }
如果對於顯示速度沒有什么要求,那么這兩個辦法還是可以用用的,但是如果對於性能有要求,而且又數據量很大,比如接收超高清的視頻數據,那么這兩個方法是完全滿足不了需求的。
本文的方案是:
1、以顯示3000 * 3000的圖像為例,下面的代碼是偽代碼
2、定義ImageSource ImgSource,ImgSource綁定到image控件的Source屬性
3、PixelFormats.Gray16,定義為PixelFormats.Gray8也是可以的,不過就需要在WriteableBitmap構造函數最后一個參數添加偽彩表,當然還可定義rgb的格式,這個看裸數據的格式以及需求來了,這里只是拋磚引玉,方法是通用的。
4、本方案的優點是沒有頻繁的內存分配和釋放,既節省時間,又不用擔心內存溢出,想更新哪里更新哪里,代碼簡單易懂,速度極快
ViewModel中 public class MainWindowViewModel : ViewModelBase { private WriteableBitmap _wbBitmap; public MainWindowViewModel() { _wbBitmap = new WriteableBitmap(3000, 3000, 96, 96, PixelFormats.Gray16, null); ImgSource = _wbBitmap; } public void ShowImage(short[] rawData)// rawData是存儲圖像裸數據的buffer { unsafe { _wbBitmap.Lock(); Marshal.Copy(rawData,0,_wbBitmap.BackBuffer,3000*3000); //請注意_wbBitmap的數據格式以及buffer大小,以免溢出和顯示異常
_wbBitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, 3000, 3000)); _wbBitmap.Unlock(); } } }