GDI操作PNG圖片

這兩天在做UI,以前做過一點,但是不太熟悉,於是乎就遇到很多的問題。
以前用BITMAP的時候,做的就是像素COPY,再多一點就是像素運算,這一切的東西都是有自己控制的。也就是說不參雜alpha通道。
話說今天我用PNG圖片,但是用到一個小圖片的時候,圖片總是不能正常顯示。代碼如下:
CImage image;//CImage類有自己的Dc
if (image.Load(imagePath)!=S_OK)
{
AfxMessageBox(_T("加載圖片失敗"));
}
image.BitBlt(m_dc.GetSafeHdc(),rect,CPoint(0,0),SRCCOPY);
在這種情況下進行位COPY,不能正常顯示。我就在琢磨這是什麼原因呢?想了半天,也想不到一個所以然來,於是不得不借助於網絡搜索,正好看到下面的一篇文章。
它是直接用CImage::Draw()直接進行繪圖的。下面是查詢的MSDN:
Draw performs the same operation as StretchBlt, unless the image contains a transparent color or alpha channel. In that case, Draw performs the same operation as either TransparentBlt or AlphaBlend as required. 
For versions of Draw that do not specify a source rectangle, the entire source image is the default. For the version of Draw that does not specify a size for the destination rectangle, the size of the source image is the default and no stretching or shrinking occurs. 

從上面看出,如果圖片是含有alpha通道,那麼這個圖片就應該用TransparentBlt or AlphaBlend這兩個函數,否則用StretchBlt,Bitblt。從這裏就可以看出我用Bitblt操作含有alpha通道的圖片是錯誤的.

正確方式:

	CImage image;//CImage類有自己的Dc
	if (image.Load(imagePath)!=S_OK)
	{
		AfxMessageBox(_T("加載圖片失敗"));
	}
	CRect src(0,0,image.GetWidth(),image.GetHeight());
	image.AlphaBlend(m_dc.GetSafeHdc(),rect,src);


下面轉載:

先看看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的對象
PNG圖片的透明背景總是一片白色,後來才發現這其實是微軟GDI+的設計問題,PNG圖片是ARGB,使用GDI+載入圖片的時候,GDI+會默認已經進行了預剩運算(PARGB),即每象素的實際值是已經和ALPHA值按比例相乘的結果,實際上它根本就沒有做預乘,在使用透明圖片的象素ALPHA通道的時候,CImage內部正是調用的AlphaBlend,沒有預乘的圖當作預乘的圖片處理的結果就是這相當於一張和純白背景進行了預乘,所以圖象總是出現白色背景。
最後的解決方法,寫一個小程序對PNG圖片每個象素進行預乘運算,然後保存成PNG圖片,實際效果良好。
具體方法如下:
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++)
		{
			//TT:一直在推測一段代碼的意思,既然系統已經支持了alpha,應該系統自己計算,爲什麼還要我們計算呢?真心不知道。
                        //我知道這段代碼是計算透明度的方法
			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);

代碼中內部的框架是對圖像的再次處理,對原來進行了修正,這樣得到的更加正常,代碼實測如下



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章