首页 > 代码库 > vc++加载透明png图片方法——GDI+和CImage两种
vc++加载透明png图片方法——GDI+和CImage两种
vc++加载透明png图片方法——GDI+和CImage两种
在加载png时遇到了麻烦,后来用了两个方法解决了。一个是用GDI+,另外就是用vs.net MFC自带的CImage。
先看看GDI+的方法
方法1:
1.GDI+画透明图层(alpha)的png图片
stdafx加入如下:
#include <comdef.h>//初始化一下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: 在此添加专用代码和/或调用基类
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也可以显示,不过要稍微进行转换才能得到正常的带α通道的png图片!
在画图前进行一次转换,其中Image是CImage的对象
if (Image.GetBPP() == 32) //确认该图像包含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) //确认该图像包含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);
代码中内部的框架是对图像的再次处理,对原来进行了修正,这样得到的更加正常,代码实测如下
绘图后效果
为了方便,封装成一个通用函数:
pWnd是需要显示图片的控件窗口,strPicPath是png图片地址,bAutoFit是否需要自动适应pWnd的窗口
[cpp] view plaincopy
- bool ShowPNGInCtrl(CWnd* pWnd,const CString& strPicPath,BOOL bAutoFit)
- {
- if (NULL == pWnd)
- {
- return false;
- }
- if (NULL == pWnd->GetSafeHwnd())
- {
- return false;
- 10. }
- 11. if (strPicPath.GetLength()<=0)
- 12. {
- 13. return false;
- 14. }
- 15. try
- 16. {
- 17. CImage img;
- 18. CRect rect;
- 19. int nDPx(0),nDPy(0);//画图的起点
- 20. int nDw(0),nDh(0);//画图的宽和高
- 21. pWnd->GetClientRect(rect);
- 22. HRESULT hr;
- 23. hr = img.Load(strPicPath);
- 24. if (S_OK != hr)
- 25. {
- 26. return false;
- 27. }
- 28. std::unique_ptr<CClientDC> pDc;
- 29. pDc.reset(new CClientDC(pWnd));
- 30.
- 31. if (bAutoFit)
- 32. {
- 33. if (img.GetWidth()>img.GetHeight())//说明图片是长方形
- 34. {
- 35. nDw = rect.Width();
- 36. nDh = (int)(nDw * img.GetHeight() / img.GetWidth());
- 37. nDPx = 0;
- 38. nDPy = (int)(rect.Height()/2.0 - nDh/2.0);
- 39. if (nDh>rect.Height())//说明图片控件是横着放,这时候就要再次缩减
- 40. {
- 41. nDh =rect.Height();
- 42. nDw =(int)( img.GetWidth()*nDh/img.GetHeight());
- 43. nDPy = 0;
- 44. nDPx =(int) (rect.Width()/2.0 - nDw/2.0);
- 45. }
- 46. }
- 47. else//说明竖着比较明显
- 48. {
- 49. nDh = rect.Height();
- 50. nDw =(int)( img.GetWidth()*nDh/img.GetHeight());
- 51. nDPy = 0;
- 52. nDPx =(int) (rect.Width()/2.0 - nDw/2.0);
- 53. if (nDw>rect.Width())//说明是另外一种情况
- 54. {
- 55. nDw = rect.Width();
- 56. nDh = (int)(nDw * img.GetHeight() / img.GetWidth());
- 57. nDPx = 0;
- 58. nDPy = (int)(rect.Height()/2.0 - nDh/2.0);
- 59. }
- 60. }
- 61. }
- 62. else
- 63. {
- 64. nDw = img.GetWidth();
- 65. nDh = img.GetHeight();
- 66. }
- 67. if (img.GetBPP() == 32) //确认该图像包含Alpha通道
- 68. {
- 69. int i;
- 70. int j;
- 71. for (i=0; i<img.GetWidth(); i++)
- 72. {
- 73. for (j=0; j<img.GetHeight(); j++)
- 74. {
- 75. byte *pByte = (byte *)img.GetPixelAddress(i, j);
- 76. pByte[0] = pByte[0] * pByte[3] / 255;
- 77. pByte[1] = pByte[1] * pByte[3] / 255;
- 78. pByte[2] = pByte[2] * pByte[3] / 255;
- 79. }
- 80. }
- 81. }
- 82.
- 83. pDc->SetStretchBltMode(HALFTONE);
- 84. img.Draw(pDc->m_hDC,nDPx,nDPy,nDw,nDh);
- 85. //img.TransparentBlt(pDc->m_hDC,nDPx,nDPy,nDw,nDh,RGB(255, 255, 255));
- 86. }
- 87. catch (...)
- 88. {
- 89. return false;
- 90. }
- 91. return true;
92. }
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.如果图片是在资源里的时候加载方法又不一样
这时需要两个函数,
对于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;
}
这时加载图片的代码变为:
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时需要如下函数:
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;
}
// 锁定内存中的指定资源
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);
// 解除内存中的指定资源
::GlobalUnlock(hNew);
// 从指定内存创建流对象
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) //确认该图像包含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);
另
要注意的是,最好把绘图放在OnPaint消息响应里,否则,OnSize消息触发OnPaint重绘时可能会把之前的绘图全部清除
vc++加载透明png图片方法——GDI+和CImage两种