VC++使用GDI+实现HBITMAP与图片文件之间的相互转换(附源码)

       有时我们需要把图片文件加载到内存中的HBITMAP中,有时我们需要把内存中的HBITMAP位图保存成图片文件。如果仅使用GDI函数去处理,是比较难实现的,借助GDI+对象我们可以轻易的实现。本文我们就来讲述一下如何实现HBITMAP与图片文件之间的相互转换。

VC++使用GDI+实现HBITMAP与图片文件之间的相互转换(附源码)_第1张图片

1、将图片文件加载到HBITMAP对象中

       先将指定路径的图片文件加载到CImage对象中,然后再将CImage对象中的图片数据绘制到HBITMAP中。其中CImage类是Visual Studio中自带的MFC类,是专门用来处理图片文件的,CImage类内部其实就是使用GDI+来实现的,所以我们当前的这种方法,本质是使用GDI+实现的。相关代码如下:

HBITMAP GetHBitmapFromFile( CString strFilePath )
{
	if ( !PathFileExists( strFilePath ) )
	{		
		return NULL;
	}

	// 先将图片加载到CImage对象中
	CImage img;
	HRESULT hResult = img.Load( strFilePath );
	if ( hResult != S_OK )
	{
		return NULL;
	}

	// 然后将CImage对象中的图片绘制到新创建的HBITMAP对象上
	HDC hDC = GetDC( NULL ); // 获取屏幕DC
	HDC hDstDC = CreateCompatibleDC( hDC );
	HBITMAP hDstBitmap = ::CreateCompatibleBitmap( hDC, img.GetWidth(), img.GetHeight() );
	::SelectObject( hDstDC, hDstBitmap );
	img.Draw( hDstDC, 0, 0 );	

	::DeleteDC( hDstDC );
	::ReleaseDC( NULL, hDC );

    return hDstBitmap;
}

       有人可能会说,我当前进行的是Win32窗口编程,不使用MFC框架,使用MFC库还需要将MFC库带上,也会增大程序安装包的大小。其实Visua Studio中可以直接go到CImage类的cpp实现代码中我们完全可以将CImage类从MFC库中剥离出来的,即直接将CImage类的.h头文件和.cpp源文件拷贝出来,然后将文件中依赖MFC的接口换掉就可以了,操作起来很简单,我们就这样处理过。

2、将HBITMAP中的图片保存成多种类型的图片文件

       我们需要使用到GDI+中的Bitmap对象,先调用Bitmap::FromHBITMAP接口将HBITMAP中的图片数据加载到Bitmap对象中,然后再调用Bitmap::Save接口将Bitmap对象中的图片数据保存成图片文件。相关代码如下所示:(代码中根据要保存成的文件路径中的后缀名,确定要保存成什么类型的图片)

// 获取jpeg等编码Clsid
// 这是一个中间函数,下面的主函数SaveHBitmapToFile会调用到该函数
int GetEncoderClsid( const WCHAR* format, CLSID* pClsid )   
{  
	UINT num = 0;                    // number of image encoders  
	UINT size = 0;                   // size of the image encoder array in bytes  
	ImageCodecInfo* pImageCodecInfo = NULL;  

	GetImageEncodersSize( &num, &size );  
	if( size == 0 ) 
	{
		return -1;     
	}

	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));  
	if( pImageCodecInfo == NULL )  
	{
		return -1;     
	}

	GetImageEncoders( num, size, pImageCodecInfo );  

	for( UINT j = 0; j < num; ++j )  
	{  
		if( wcscmp( pImageCodecInfo[j].MimeType, format ) == 0 )  
		{  
			*pClsid = pImageCodecInfo[j].Clsid;  
			free(pImageCodecInfo);  
			return j;     
		}          
	}  

	free( pImageCodecInfo );  

	return -1;     
}



// 使用gdi+将HBITMAP位图句柄图片资源保存成图片文件,图片文件的类型包括bmp、jpg、png、gif和tiff
BOOL SaveHBitmapToFile( HBITMAP hBmp, LPCTSTR lpfilename )
{
	if ( hBmp == NULL || lpfilename == NULL )
	{
		WriteLog( _T("[SaveHBitmapToFile] hBmp或lpfilename为空,return") );
		return FALSE;
	}

	CUIString strLog;
	int nLastErrCode = 0;
	Bitmap *pSrcBmp = Bitmap::FromHBITMAP( hBmp, NULL );
	if ( pSrcBmp == NULL )
	{
		nLastErrCode = ::GetLastError();
		strLog.Format( _T("[SaveHBitmapToFile] Bitmap::FromHBITMAP失败,GetLastError: %d"), 
			nLastErrCode );
		WriteLog( strLog );
		return FALSE;
	}

	// 获取文件的扩展名,根据文件名中的扩展名确定要保存成什么类型的图片
	CUIString strExtName = _T("");
	CUIString strFilePatch = lpfilename;
	int nPos = strFilePatch.ReverseFind(_T('.'));
	if ( nPos != -1)
	{
		strExtName = strFilePatch.Right( strFilePatch.GetLength() - nPos - 1 );
	}

	// GDI+中有五种编码:bmp、jpeg、gif、tiff和png,下面根据
	// 文件名的扩展部分来获取对应的编码CLSID
	CLSID encoderClsid;
	if ( strExtName == _T("bmp") )
	{
		GetEncoderClsid( L"image/bmp", &encoderClsid );
	}
	else if ( strExtName == _T("jpg") )
	{
		GetEncoderClsid( L"image/jpeg", &encoderClsid );
	}
	else if ( strExtName == _T("png") )
	{
		GetEncoderClsid( L"image/png", &encoderClsid );
	}
	else if ( strExtName == _T("gif") )
	{
		GetEncoderClsid( L"image/gif", &encoderClsid );
	}
	else if ( strExtName == _T("tiff") )
	{
		GetEncoderClsid( L"image/tiff", &encoderClsid );
	}
	else // 没有指定类型的扩展名,则默认使用png
	{
		GetEncoderClsid( L"image/png", &encoderClsid );
	}

	LPWSTR lpWStrFileName = NULL; // 注意:Bitmap::Save接口第一个参数是宽字节参数
#ifdef _UNICODE
	lpWStrFileName = const_cast(lpfilename); // 如果是_UNICODE模式,直接使用
#else
	WCHAR wchFileName[MAX_PATH*2] = {0};
	MultiByteToWideChar( CP_ACP, 0, lpfilename, -1, wchFileName, sizeof(wchFileName)/sizeof(WCHAR) ); // 将窄字符转化为宽字符
	lpWStrFileName = wchFileName;
#endif

	Gdiplus::Status statusRet = pSrcBmp->Save( lpWStrFileName, &encoderClsid );
	if ( statusRet != Gdiplus::Ok )
	{
		nLastErrCode = ::GetLastError();
		strLog.Format( _T("[SaveHBitmapToFile] Bitmap::FromHBITMAP失败,,Gdiplus错误码:%d, GetLastError: %d"), 
			statusRet, nLastErrCode );
		WriteLog( strLog );

		// 释放内存
		delete pSrcBmp;
		return FALSE;
	}

	// 释放内存
	delete pSrcBmp;
	return TRUE;
}

你可能感兴趣的:(VC++常用功能代码封装,HBITMAP,图片文件,GDI+,CImage,Bitmap)