前幾天寫程序需要將DIB(設備無關位圖)格式的圖片在WPF 程序中顯示出來,在網上查了一些資料和代碼,記錄下來以便以后參考。
設備有關位圖主要是顯卡在顯存中保存的圖片格式,顯卡就是從顯存里面將要顯示的圖片數據直接打印到顯示器上,這樣不僅節省了系統內存的空間,還大大節省了從內存把圖片數據搬到顯存的時間—因為顯卡芯片可以實現一些指令,直接操作顯存中的圖片。所以設備有關位圖的格式不是固定的,是由顯卡制造商自由決定的,這也就意味著我們沒有辦法直接操作DDB格式圖片的每一個像素。
如果保存在硬盤的圖片格式是以DDB格式保存的話,那就意味著你使用一個顯卡保存的圖片不能在使用不同型號的顯卡的機器上打開—因為不同顯卡制造商使用的DDB的格式不一樣。為了解決這個問題,在很久以前,微軟的GDI系統就提供了DIB(設備無關位圖),相當于一種中間格式,在顯示DIB圖片的之前,GDI先將DIB位圖轉換成顯卡能夠理解的DDB格式—當然這一系列的操作都是由顯卡的指令完成的。
在WPF和Winform的世界里,大家針對圖片的編程相對來說都比較舒服,因為.NET Framework提供了簡化的API幫你隱藏了這些細節,所有你需要做的就是:
WPF:
var image = new Image();
把圖片改成jpg格式。?image.Source = new BitmapImage(new Uri("some image.jpg"));
Winform:
var image = System.Drawing.Image.FromFile("some image.jpg");
?
恩,看起來很美,但問題是,因為微軟的函數庫實在是太多了,強大如.NET這種類庫也有考慮不周的時候,WPF并沒有公開的API直接從DIB格式的圖片中生成BitmapImage實例。為什么有的時候我們需要直接操作DIB格式的圖片呢?例如你需要從剪貼板里面拿一個圖片出來,而把圖片放置到剪貼板的程序有可能并不是.NET程序,而是古老的OLE程序或者Win32程序,這些老古董經常喜歡在剪貼板里面放兩個格式的圖片,一個是DIB格式的,另外一個可能是支持IDataObject的接口—這個接口怎么實現又是仁者見仁,智者見智的事情。這個時候,你就發現你必須要處理DIB格式的圖片;另外一種情況就是拖拉操作,因為拖拉操作中數據源和接受數據的程序有可能不是同一個程序,例如,你從一個OLE程序里面拖拉一個圖片到WPF程序里。
根據MSDN,DIB圖片的格式如下圖所示:
wpf圖片環繞、
?
下面的代碼是我從網上搜到的關于如何從DIB格式生成BitmapImage實例的代碼:
??????? private System.Drawing.Bitmap CreateBitmapFromDib(Stream dib) ??????? { ??????????? BinaryReader reader = new BinaryReader(dib); ? ??????????? int headerSize = reader.ReadInt32(); ??????????? int pixelSize = (int)dib.Length - headerSize; ??????????? int fileSize = 14 + headerSize + pixelSize; ? ??????????? MemoryStream bmp = new MemoryStream(fileSize); ??????????? BinaryWriter writer = new BinaryWriter(bmp); ? ??????????? // 1. 把位圖的一些元數據寫進去,下面這幾次Write相當于填寫Win32的 ??????????? // BITMAPFILEHEADER結構 ??????? ??????????? writer.Write((byte)'B'); ??????????? writer.Write((byte)'M'); ??????????? writer.Write(fileSize); ??????????? writer.Write((int)0); ??????????? writer.Write(14 + headerSize); ? ??????????? // 2. 把DIB位圖中的像素矩陣拷貝出來到我們指定的MemoryStream里。? ??????????? // 因為我們要從MemoryStream里面生成System.Drawing.Bitmap對象 ??????????? // 然后再頗為曲折地從Bitmap對象生成WPF的BitmapImage對象 ??????????? dib.Position = 0; ??????????? byte[] data = new byte[(int)dib.Length]; ??????????? dib.Read(data, 0, (int)dib.Length); ??????????? writer.Write(data, 0, (int)data.Length); ? ??????????? // 3. 生成Bitmap對象—這個是Winform里面的Bitmap對象 ??????????? bmp.Position = 0; ??????????? return new System.Drawing.Bitmap(bmp); ??????? } |
?
wpf設置背景圖片、上面那一段代碼也相當于下面的C++代碼,從MFC的實例代碼里面找到的:
HDIB WINAPI ReadDIBFile(CFile& file) { ??????????????? BITMAPFILEHEADER bmfHeader; ??????????????? UINT nBitsSize; ??????????????? HDIB hDIB; ??????????????? LPSTR pDIB; ? ??????????????? /* ??????????????? ?* get length of DIB in bytes for use when reading ??????????????? ?*/ ? ??????????????? nBitsSize = (UINT)file.GetLength(); ? ??????????????? /* ??????????????? ?* Go read the DIB file header and check if it's valid. ??????????????? ?*/ ??????????????? if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) != sizeof(bmfHeader)) ??????????????????????????????? return NULL; ? ??????????????? if (bmfHeader.bfType != DIB_HEADER_MARKER) ??????????????????????????????? return NULL; ? ??????????????? /* ??????????????? ?* Allocate memory for DIB ??????????????? ?*/ ??????????????? hDIB = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD)nBitsSize); ??????????????? if (hDIB == 0) ??????????????? { ??????????????????????????????? return NULL; ??????????????? } ??????????????? pDIB = (LPSTR) ::GlobalLock((HGLOBAL) hDIB); ? ??????????????? /* ??????????????? ?* Go read the bits. ??????????????? ?*/ ??????????????? if (file.Read(pDIB, nBitsSize - sizeof(BITMAPFILEHEADER)) != ??????????????????????????????? nBitsSize - sizeof(BITMAPFILEHEADER) ) ??????????????? { ??????????????????????????????? ::GlobalUnlock((HGLOBAL) hDIB); ??????????????????????????????? ::GlobalFree((HGLOBAL) hDIB); ??????????????????????????????? return NULL; ??????????????? } ??????????????? ::GlobalUnlock((HGLOBAL) hDIB); ??????????????? return hDIB; } |
?
得到System.Drawing.Bitmap實例以后,就可以通過下面的函數來創建BitmapImage對象了:
private BitmapSource BitmapToImageSource(System.Drawing.Bitmap bitmap) { ??? BitmapSource destination; ??? IntPtr hBitmap = bitmap.GetHbitmap(); ??? BitmapSizeOptions sizeOptions = BitmapSizeOptions.FromEmptyOptions(); ??? destination = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, sizeOptions); ??? destination.Freeze(); ??? return destination; } |
可編輯的圖片格式??
?
?