GDI操作PNG圖片
阿新 • • 發佈:2019-01-22
這兩天在做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.
方法1:
1.GDI+畫透明圖層(alpha)的png圖片
stdafx加入如下:
開始初始化:
在app類的聲明裡(.h)加入:
顯示圖片的過程如下
這是用GDI+來顯示圖片。
2.CImage繪製帶alpha透明圖層的png圖片
用MFC自帶的CImage也可以顯示,不過要稍微進行轉換才能得到正常的帶α通道的png圖片!
在畫圖前進行一次轉換,其中Image是CImage的物件
PNG圖片的透明背景總是一片白色,後來才發現這其實是微軟GDI+的設計問題,PNG圖片是ARGB,使用GDI+載入圖片的時候,GDI+會預設已經進行了預剩運算(PARGB),即每象素的實際值是已經和ALPHA值按比例相乘的結果,實際上它根本就沒有做預乘,在使用透明圖片的象素ALPHA通道的時候,CImage內部正是呼叫的AlphaBlend,沒有預乘的圖當作預乘的圖片處理的結果就是這相當於一張和純白背景進行了預乘,所以圖象總是出現白色背景。
最後的解決方法,寫一個小程式對PNG圖片每個象素進行預乘運算,然後儲存成PNG圖片,實際效果良好。
具體方法如下:
程式碼中內部的框架是對影象的再次處理,對原來進行了修正,這樣得到的更加正常,程式碼實測如下
以前用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);
程式碼中內部的框架是對影象的再次處理,對原來進行了修正,這樣得到的更加正常,程式碼實測如下