以前結合網上的一些代碼及ATL::CImage的實現,本身寫了一個將HBITMAP以PNG格式保存到文件到函數。見上一篇日記。函數
不過,後來換了個環境又發現了問題,昨天和今天上午把《Windows程序設計》中位圖處理相關的部分又粗略瞄了一下,而後把以前的函數改了一下,如今在新環境下也能夠了,固然,這個函數也並不十分嚴謹,可是考慮到位圖格式的歷史淵源和複雜性,測試起來目測會至關麻煩,仍是不要深究的好。並且,如今基本上都是32位圖像,老的格式中不少東西都已無用武之地,因此且將就用着。測試
首先,幸虧須要處理的只是帶Alpha通道的圖像,而Alpha通道只有ARGB有,ARGB又不須要顏色表(每一個像素值都是真實的顏色值,而非顏色表索引)。對於ARGB來講,位圖數據能夠隨意拷貝,不須要先針對目標DC或目標DDB/DIB進行轉換。設計
因此,鑑於只要解決Alpha通道失效的問題,因此處理辦法能夠簡化一下,首先判斷位圖每像素的位數(ARGB爲32位),若是不是32位,則一定不是ARGB,不存在Alpha通道,所以能夠直接使用Gdiplus::Bitmap::FromHBITMAP再保存。這樣,就能夠把非32位的位圖可能的種種狀況(對齊、顏色掩碼、顏色表、座標系)統統丟給GDI+去處理。指針
若是是32位,便可認爲是ARGB,因爲不存在顏色表,因此也能夠用採比較粗糙的辦法處理:用ARGB格式來創建一個新的Bitmap對象,而後不考慮原位圖是DIB仍是DDB,直接將它的圖像數據填充到新Bitmap對象。code
上一篇日記裏的函數其實也是這個思路,可是有個小問題:經過GetObject獲得的BITMAP,其中的bmBits成員(即圖像數據塊的指針),只有在圖像是DIB時纔是有效的,若是圖像是DDB,獲得的指針將是NULL。以前沒有在GetObject的說明中留意到這一點,又在ATL::CImage中找不到DDB的處理代碼,因此就直接放棄了DDB。orm
新函數糾正了這個問題,對於DDB,經過GetDIBits能夠拿到圖像數據再填充到Bitmap。因爲ARGB不存在顏色表,因此能夠用更簡單的GetBitmapBits來代替GetDIBits。對象
新函數中依然存一個不肯定性問題:blog
在從ATL::CImage中提取出來DIB處理代碼中,會處理座標系。當取到的BITMAPINFOHEADER.biHeight爲正數時,會把位數據塊的指針移到最後一行,並把行寬置爲負值(這樣每次經過+行寬值的操做就能獲得上一行的數據地址)。索引
而對DDB來講,我不知道是否是也存在座標系的問題,而沒有BITMAPINFOHEADER數據,而根據MSDN,BITMAP中的bmHeight是必須爲正值的,它不能用來指示座標系。因此,我沒有考慮這個問題,用GetBitmapBits把位圖數據取出來並直接填到Bitmap對象中去了。目前看到的結果是圖像沒有發生倒置,可是我不肯定是否會存在其它狀況。ip
代碼在此:
bool ImageUtil::SavePng( HBITMAP hBmp, LPCTSTR lpszFilePath ) { DIBSECTION dibsection = {0}; int nBytes = ::GetObject( hBmp, sizeof( DIBSECTION ), &dibsection ); Gdiplus::Bitmap* bitmap = 0; if(dibsection.dsBm.bmBitsPixel != 32) { bitmap = Gdiplus::Bitmap::FromHBITMAP(hBmp, NULL); } else { int width, height, pitch; LPVOID bits; width = dibsection.dsBm.bmWidth; height = abs( dibsection.dsBm.bmHeight ); pitch = (((width*dibsection.dsBm.bmBitsPixel)+31)/32)*4; //計算行寬,四字節對齊 ATL::CImage::ComputePitch // 32位位圖不存在對齊問題,so其實不必 bits = dibsection.dsBm.bmBits; if( dibsection.dsBmih.biHeight > 0 ) // 對於DDB,不會取到dsBmih數據,因此biHeight成員始終爲0 { bits = LPBYTE( bits )+((height-1)*pitch); pitch = -pitch; } bitmap = new Gdiplus::Bitmap(width, height, pitch, PixelFormat32bppARGB, static_cast< BYTE* >(bits )); if(0 == bits) { BitmapData bitmapData; Rect rc(0, 0, width, height); bitmap->LockBits(&rc, ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData); GetBitmapBits(hBmp, pitch * height, bitmapData.Scan0); // 上面的bits在biHeight>0時要倒置的,可是這裏不知道要不要,也很差測試 bitmap->UnlockBits(&bitmapData); } } bool ret = false; CLSID clsid = GetGdiplusEncoderClsid(NULL, &Gdiplus::ImageFormatPNG); if(clsid != CLSID_NULL) { ret = (Gdiplus::Ok == bitmap->Save(lpszFilePath, &clsid, NULL)); } delete bitmap; return ret; }