這幾天放假在家無聊編一個程序,在加載png時遇到了麻煩,后來用了兩個方法解決了。一個是用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也可以顯示,不過要稍微進行轉(zhuǎn)換才能得到正常的帶α通道的png圖片!
在畫圖前進行一次轉(zhuǎn)換,其中Image是CImage的對象
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)部的框架是對圖像的再次處理,對原來進行了修正,這樣得到的更加正常,代碼實測如下
繪圖后效果
為了方便,封裝成一個通用函數(shù):
pWnd是需要顯示圖片的控件窗口,strPicPath是png圖片地址,bAutoFit是否需要自動適應(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())//說明圖片是長方形 { 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())//說明圖片控件是橫著放,這時候就要再次縮減 { 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ù),
對于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; }
這時加載圖片的代碼變?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ù):
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)建流對象 HRESULT?ht?=?::CreateStreamOnHGlobal(hNew,?TRUE,?&pStream); if?(?ht?!=?S_OK?) { GlobalFree(hNew); } else { //?加載圖片 pImage->Load(pStream); GlobalFree(hNew); } //?釋放資源 ::FreeResource(hImgData); return?true; }
這時加載圖片的代碼如下:
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重繪時可能會把之前的繪圖全部清除