使用GDI+保存帶Alpha通道的圖像(續)

以前結合網上的一些代碼及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;
}
相關文章
相關標籤/搜索