1. 程式人生 > >vc++載入透明png圖片方法——GDI+和CImage兩種

vc++載入透明png圖片方法——GDI+和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);

程式碼中內部的框架是對影象的再次處理,對原來進行了修正,這樣得到的更加正常,程式碼實測如下
繪圖後效果
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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重繪時可能會把之前的繪圖全部清除