vc++加載透明png圖片方法——GDI+和CImage兩種
這幾天放假在家無聊編一個(gè)程序,在加載png時(shí)遇到了麻煩,后來用了兩個(gè)方法解決了。一個(gè)是用GDI+,另外就是用vs.net
MFC自帶的CImage。
先看看GDI+的方法
方法1:
1.GDI+畫透明圖層(alpha)的png圖片
stdafx加入如下:
#include?//初始化一下com口 #include?"GdiPlus.h" using?namespace?Gdiplus; #pragma?comment(lib,"gdiplus.lib")
開始初始化:
在app類的聲明里(.h)加入:
ULONG_PTR?m_gdiplusToken;
InitInstance()里加入://若沒有usingnamespace Gdiplus; 就要在前面加Gdiplus::
GdiplusStartupInput?gdiplusStartupInput; GdiplusStartup(&m_gdiplusToken,?&gdiplusStartupInput,?NULL);
重載ExitInstance,加入GdiplusShutdown(m_gdiplusToken);
int?CxxxApp::ExitInstance()
{
//?TODO:?在此添加專用代碼和/或調(diào)用基類
GdiplusShutdown(m_gdiplusToken);
return?CWinApp::ExitInstance();
}顯示圖片的過程如下
CClientDC?*pDC?=?new?CClientDC(GetDlgItem(IDC_STATIC_PIC));
CRect?rect;
GetDlgItem(IDC_STATIC_PIC)->GetWindowRect(&rect);
Graphics?graphics(pDC->m_hDC);?//?Create?a?GDI+?graphics?object
Image?image(_T("1.png"));?//?Construct?an?image
graphics.DrawImage(&image,?0,?0,?image.GetWidth(),?image.GetHeight());
delete?pDC;這是用GDI+來顯示圖片。
2.CImage繪制帶alpha透明圖層的png圖片
用MFC自帶的CImage也可以顯示,不過要稍微進(jìn)行轉(zhuǎn)換才能得到正常的帶α通道的png圖片!
在畫圖前進(jìn)行一次轉(zhuǎn)換,其中Image是CImage的對(duì)象
if?(Image.GetBPP()?==?32)?//確認(rèn)該圖像包含Alpha通道
{
int?i;
int?j;
for?(i?=?0;?i?<?Image.GetWidth();?i++)
{
for?(j?=?0;?j?<?Image.GetHeight();?j++)
{
byte?*pByte?=?(byte?*)Image.GetPixelAddress(i,?j);
pByte[0]?=?pByte[0]?*?pByte[3]?/?255;
pByte[1]?=?pByte[1]?*?pByte[3]?/?255;
pByte[2]?=?pByte[2]?*?pByte[3]?/?255;
}
}
}具體方法如下:
HWND?hwnd?=?GetSafeHwnd();?//獲取窗口的HWND
::InvalidateRect(?hwnd,?NULL,?true?);?//或者?::InvalidateRect(?hwnd,?NULL,?false?);
::UpdateWindow(hwnd);
//若使用前不想把原來繪制的圖片去掉,可以刪去上面那三段
CDC?*pDC?=?GetDC();
CImage?Image;
Image.Load(strPath);
if?(Image.IsNull())
{
MessageBox(_T("沒加載成功"));
return?-1;
}
if?(Image.GetBPP()?==?32)?//確認(rèn)該圖像包含Alpha通道
{
int?i;
int?j;
for?(i?=?0;?i?<?Image.GetWidth();?i++)
{
for?(j?=?0;?j?<?Image.GetHeight();?j++)
{
byte?*pByte?=?(byte?*)Image.GetPixelAddress(i,?j);
pByte[0]?=?pByte[0]?*?pByte[3]?/?255;
pByte[1]?=?pByte[1]?*?pByte[3]?/?255;
pByte[2]?=?pByte[2]?*?pByte[3]?/?255;
}
}
}
Image.Draw(pDC->m_hDC,?0,?0);
Image.Destroy();
ReleaseDC(pDC);
代碼中內(nèi)部的框架是對(duì)圖像的再次處理,對(duì)原來進(jìn)行了修正,這樣得到的更加正常,代碼實(shí)測(cè)如下
繪圖后效果
為了方便,封裝成一個(gè)通用函數(shù):
pWnd是需要顯示圖片的控件窗口,strPicPath是png圖片地址,bAutoFit是否需要自動(dòng)適應(yīng)pWnd的窗口
bool?ShowPNGInCtrl(CWnd*?pWnd,const?CString&?strPicPath,BOOL?bAutoFit)
{
if?(NULL?==?pWnd)
{
return?false;
}
if?(NULL?==?pWnd->GetSafeHwnd())
{
return?false;
}
if?(strPicPath.GetLength()GetClientRect(rect);???
HRESULT?hr;
hr?=?img.Load(strPicPath);
if?(S_OK?!=?hr)
{
return?false;
}
std::unique_ptrpDc;
pDc.reset(new?CClientDC(pWnd));
if?(bAutoFit)
{
if?(img.GetWidth()>img.GetHeight())//說明圖片是長(zhǎng)方形
{
nDw??=?rect.Width();
nDh??=?(int)(nDw?*?img.GetHeight()?/?img.GetWidth());
nDPx?=?0;
nDPy?=?(int)(rect.Height()/2.0?-?nDh/2.0);
if?(nDh>rect.Height())//說明圖片控件是橫著放,這時(shí)候就要再次縮減
{
nDh ?=rect.Height();
nDw??=(int)(?img.GetWidth()*nDh/img.GetHeight());
nDPy?=?0;
nDPx?=(int)?(rect.Width()/2.0?-?nDw/2.0);
}
}
else//說明豎著比較明顯
{
nDh??=?rect.Height();
nDw??=(int)(?img.GetWidth()*nDh/img.GetHeight());
nDPy?=?0;
nDPx?=(int)?(rect.Width()/2.0?-?nDw/2.0);
if?(nDw>rect.Width())//說明是另外一種情況
{
nDw??=?rect.Width();
nDh??=?(int)(nDw?*?img.GetHeight()?/?img.GetWidth());
nDPx?=?0;
nDPy?=?(int)(rect.Height()/2.0?-?nDh/2.0);
}
}
}
else
{
nDw?=?img.GetWidth();
nDh?=?img.GetHeight();
}
if?(img.GetBPP()?==?32)?//確認(rèn)該圖像包含Alpha通道
{
int?i;
int?j;
for?(i=0;?i<img.GetWidth();?i++)
{
for?(j=0;?jSetStretchBltMode(HALFTONE);
img.Draw(pDc->m_hDC,nDPx,nDPy,nDw,nDh);
//img.TransparentBlt(pDc->m_hDC,nDPx,nDPy,nDw,nDh,RGB(255,?255,?255));
}
catch?(...)
{
return?false;
}
return?true;
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.如果圖片是在資源里的時(shí)候加載方法又不一樣
這時(shí)需要兩個(gè)函數(shù),
對(duì)于GDI+如下:
BOOL?ImageFromIDResource(UINT?nID,?LPCTSTR?sTR,Image?*&pImg)
{
HINSTANCE?hInst?=?AfxGetResourceHandle();
HRSRC?hRsrc?=?::FindResource?(hInst,MAKEINTRESOURCE(nID),sTR);?//?type
if?(!hRsrc)
return?FALSE;
//?load?resource?into?memory
DWORD?len?=?SizeofResource(hInst,?hRsrc);
BYTE*?lpRsrc?=?(BYTE*)LoadResource(hInst,?hRsrc);
if?(!lpRsrc)
return?FALSE;
//?Allocate?global?memory?on?which?to?create?stream
HGLOBAL?m_hMem?=?GlobalAlloc(GMEM_FIXED,?len);
BYTE*?pmem?=?(BYTE*)GlobalLock(m_hMem);
memcpy(pmem,lpRsrc,len);
GlobalUnlock(m_hMem);
IStream*?pstm;
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
//?load?from?stream
pImg=Gdiplus::Image::FromStream(pstm);
//?free/release?stuff
pstm->Release();
FreeResource(lpRsrc);
GlobalFree(m_hMem);
return?TRUE;
}
這時(shí)加載圖片的代碼變?yōu)椋?/p>
CClientDC?*pDC?=?new?CClientDC(GetDlgItem(IDC_STATIC_PIC));
CRect?rect;
GetDlgItem(IDC_STATIC_PIC)->GetWindowRect(&rect);
Graphics?graphics(pDC->m_hDC);?//?Create?a?GDI+?graphics?object
Image?*pimage;?//?Construct?an?image
ImageFromIDResource(IDB_PNG1,_T("PNG"),pimage);
graphics.DrawImage(pimage,?0,?0,pimage->GetWidth(),?pimage->GetHeight());
delete?pDC;
用CImage時(shí)需要如下函數(shù):
BOOL?LoadImageFromResource(CImage?*pImage,?UINT?nResID,LPCTSTR?lpTyp)
{
if?(?pImage?==?NULL)?
return?false;
pImage->Destroy();
//?查找資源
HRSRC?hRsrc?=?::FindResource(AfxGetResourceHandle(),?MAKEINTRESOURCE(nResID),?lpTyp);
if?(hRsrc?==?NULL)?
return?false;
//?加載資源
HGLOBAL?hImgData?=?::LoadResource(AfxGetResourceHandle(),?hRsrc);
if?(hImgData?==?NULL)
{
::FreeResource(hImgData);
return?false;
}
//?鎖定內(nèi)存中的指定資源
LPVOID?lpVoid?=?::LockResource(hImgData);
LPSTREAM?pStream?=?NULL;
DWORD?dwSize?=?::SizeofResource(AfxGetResourceHandle(),?hRsrc);
HGLOBAL?hNew?=?::GlobalAlloc(GHND,?dwSize);
LPBYTE?lpByte?=?(LPBYTE)::GlobalLock(hNew);
::memcpy(lpByte,?lpVoid,?dwSize);
//?解除內(nèi)存中的指定資源
::GlobalUnlock(hNew);
//?從指定內(nèi)存創(chuàng)建流對(duì)象
HRESULT?ht?=?::CreateStreamOnHGlobal(hNew,?TRUE,?&pStream);
if?(?ht?!=?S_OK?)
{
GlobalFree(hNew);
}
else
{
//?加載圖片
pImage->Load(pStream);
GlobalFree(hNew);
}
//?釋放資源
::FreeResource(hImgData);
return?true;
}
這時(shí)加載圖片的代碼如下:
HWND?hwnd?=?GetSafeHwnd();?//獲取窗口的HWND
::InvalidateRect(?hwnd,?NULL,?true?);?//或者?::InvalidateRect(?hwnd,?NULL,?false?);
::UpdateWindow(hwnd);?
CDC?*pDC?=?GetDC();
CImage?Image;
LoadImageFromResource(&Image,IDB_PNG1,_T("PNG"));
if?(Image.IsNull())
{
MessageBox(_T("沒加載成功"));
return;
}
if?(Image.GetBPP()?==?32)?//確認(rèn)該圖像包含Alpha通道
{?
int?i;
int?j;
for?(i=0;?i<Image.GetWidth();?i++)
{
for?(j=0;?jm_hDC,0,0);
Image.Destroy();
ReleaseDC(pDC);另
要注意的是,最好把繪圖放在OnPaint消息響應(yīng)里,否則,OnSize消息觸發(fā)OnPaint重繪時(shí)可能會(huì)把之前的繪圖全部清除





