以前结合网上的一些代码及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; }