首页 > 代码库 > 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

  1. bool ShowPNGInCtrl(CWnd* pWnd,const CString& strPicPath,BOOL bAutoFit)  
  2. {  
  3.     if (NULL == pWnd)  
  4.     {  
  5.         return false;  
  6.     }  
  7.     if (NULL == pWnd->GetSafeHwnd())  
  8.     {  
  9.         return false;  
  10. 10.     }  
  11. 11.     if (strPicPath.GetLength()<=0)  
  12. 12.     {  
  13. 13.         return false;  
  14. 14.     }  
  15. 15.     try  
  16. 16.     {  
  17. 17.         CImage img;  
  18. 18.         CRect rect;  
  19. 19.         int nDPx(0),nDPy(0);//画图的起点  
  20. 20.         int nDw(0),nDh(0);//画图的宽和高  
  21. 21.         pWnd->GetClientRect(rect);     
  22. 22.         HRESULT hr;  
  23. 23.         hr = img.Load(strPicPath);  
  24. 24.         if (S_OK != hr)  
  25. 25.         {  
  26. 26.             return false;  
  27. 27.         }  
  28. 28.         std::unique_ptr<CClientDC> pDc;  
  29. 29.         pDc.reset(new CClientDC(pWnd));  
  30. 30.   
  31. 31.         if (bAutoFit)  
  32. 32.         {  
  33. 33.             if (img.GetWidth()>img.GetHeight())//说明图片是长方形  
  34. 34.             {  
  35. 35.                 nDw  = rect.Width();  
  36. 36.                 nDh  = (int)(nDw * img.GetHeight() / img.GetWidth());  
  37. 37.                 nDPx = 0;  
  38. 38.                 nDPy = (int)(rect.Height()/2.0 - nDh/2.0);  
  39. 39.                 if (nDh>rect.Height())//说明图片控件是横着放,这时候就要再次缩减  
  40. 40.                 {  
  41. 41.                     nDh  =rect.Height();  
  42. 42.                     nDw  =(int)( img.GetWidth()*nDh/img.GetHeight());  
  43. 43.                     nDPy = 0;  
  44. 44.                     nDPx =(int) (rect.Width()/2.0 - nDw/2.0);  
  45. 45.                 }  
  46. 46.             }  
  47. 47.             else//说明竖着比较明显  
  48. 48.             {  
  49. 49.                 nDh  = rect.Height();  
  50. 50.                 nDw  =(int)( img.GetWidth()*nDh/img.GetHeight());  
  51. 51.                 nDPy = 0;  
  52. 52.                 nDPx =(int) (rect.Width()/2.0 - nDw/2.0);  
  53. 53.                 if (nDw>rect.Width())//说明是另外一种情况  
  54. 54.                 {  
  55. 55.                     nDw  = rect.Width();  
  56. 56.                     nDh  = (int)(nDw * img.GetHeight() / img.GetWidth());  
  57. 57.                     nDPx = 0;  
  58. 58.                     nDPy = (int)(rect.Height()/2.0 - nDh/2.0);  
  59. 59.                 }  
  60. 60.             }  
  61. 61.         }  
  62. 62.         else  
  63. 63.         {  
  64. 64.             nDw = img.GetWidth();  
  65. 65.             nDh = img.GetHeight();  
  66. 66.         }  
  67. 67.         if (img.GetBPP() == 32) //确认该图像包含Alpha通道  
  68. 68.         {  
  69. 69.             int i;  
  70. 70.             int j;  
  71. 71.             for (i=0; i<img.GetWidth(); i++)  
  72. 72.             {  
  73. 73.                 for (j=0; j<img.GetHeight(); j++)  
  74. 74.                 {  
  75. 75.                     byte *pByte = (byte *)img.GetPixelAddress(i, j);  
  76. 76.                     pByte[0] = pByte[0] * pByte[3] / 255;  
  77. 77.                     pByte[1] = pByte[1] * pByte[3] / 255;  
  78. 78.                     pByte[2] = pByte[2] * pByte[3] / 255;  
  79. 79.                 }  
  80. 80.             }  
  81. 81.         }  
  82. 82.   
  83. 83.         pDc->SetStretchBltMode(HALFTONE);  
  84. 84.         img.Draw(pDc->m_hDC,nDPx,nDPy,nDw,nDh);  
  85. 85.         //img.TransparentBlt(pDc->m_hDC,nDPx,nDPy,nDw,nDh,RGB(255, 255, 255));  
  86. 86.     }  
  87. 87.     catch (...)  
  88. 88.     {  
  89. 89.         return false;  
  90. 90.     }  
  91. 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两种