我需要创建一个自定义控件来显示带alpha通道的bmp图像。背景可以用不同的颜色绘制,图像有阴影,所以我需要真正“绘制”alpha通道。
有人知道怎么做吗?
我还希望尽可能使用Alpha通道信息创建一个遮罩,以了解鼠标是否已单击图像或透明区域。
任何形式的帮助将不胜感激!
感谢。
已编辑(JDePedro):正如你们中的一些人所说,我一直在尝试使用alpha混合来绘制带alpha通道的位图。这只是我实现的一个测试,我从资源加载一个32位位图,我尝试使用AlphaBlend函数绘制它:
void CAlphaDlg::OnPaint()
{
CClientDC dc(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP BitMap;
bitmap.GetBitmap(&BitMap);
int nWidth = BitMap.bmWidth;
int nHeight = BitMap.bmHeight;
CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);
BLENDFUNCTION m_bf;
m_bf.BlendOp = AC_SRC_OVER;
m_bf.BlendFlags = 0;
m_bf.SourceConstantAlpha = 255;
m_bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf);
dcMem.SelectObject(pOldBitmap);
CDialog::OnPaint();
}
这只是一个测试,所以我将代码放在对话框的OnPaint中(我也尝试了CDC对象的AlphaBlend函数)。
正确地绘制了非透明区域,但是在位图应该透明的地方我会变白。
任何帮助???
这是截图..不容易看到,但蓝色圆圈周围有一个白色矩形: alt text http://img385.imageshack.us/img385/7965/alphamh8.png
确定。我知道了!我必须预先乘以每个像素的alpha值。有人可以建议这样做的优化方法吗?
答案 0 :(得分:9)
对于未来的Google用户,这是一个有效的预乘功能。请注意,这取自http://www.viksoe.dk/code/alphatut1.htm。
inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if( pBitData == NULL ) return;
LPBYTE pData = pBitData;
::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
for( int y = 0; y < bm.bmHeight; y++ ) {
for( int x = 0; x < bm.bmWidth; x++ ) {
pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
pData += 4;
}
}
::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
::LocalFree(pBitData);
}
那么你的OnPaint就变成了:
void MyButton::OnPaint()
{
CPaintDC dc(this);
CRect rect(0, 0, 16, 16);
static bool pmdone = false;
if (!pmdone) {
PremultiplyBitmapAlpha(dc, m_Image);
pmdone = true;
}
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
HDC src_dc = m_Image.GetDC();
::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
m_Image.ReleaseDC();
}
加载图像(在控件的构造函数中):
if ((HBITMAP)m_Image == NULL) {
m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}
答案 1 :(得分:5)
我通常这样做的方法是通过DIBSection - 一个独立于设备的位图,你可以直接修改像素。不幸的是,没有任何MFC支持DIBSections:你必须使用Win32函数CreateDIBSection()来使用它。
首先将位图加载为32位RGBA(即每个像素四个字节:一个红色,一个绿色,一个蓝色,一个用于alpha通道)。在控件中,创建适当大小的DIBSection。然后,在油漆程序中
只需查看alpha通道值,就可以根据原始位图数据创建一个遮罩 - 我不确定你在这里要问的是什么。
答案 2 :(得分:1)
您需要使用背景颜色进行alpha blend,然后取出Alpha通道将其绘制到控件中。
alpha通道应该只是图像的每个第4个字节。您可以直接将其用于蒙版,也可以将每4个字节复制到新的蒙版图像。
答案 3 :(得分:1)
使用AlphaBlend function绘制它非常容易。
至于你的掩码,你需要get the bits的位图,并检查你感兴趣的每个像素的alpha通道字节。
答案 4 :(得分:1)
将RGB通道与Alpha通道预乘的优化方法是设置包含计算的乘法结果的[256] [256]数组。第一个维度是alpha值,第二个维度是R / G / B值,数组中的值是您需要的预乘值。
正确设置此数组后,您可以像这样计算所需的值:
R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];
答案 5 :(得分:1)
你走在正确的轨道上,但需要解决两件事。
首先使用:: LoadImage(.. LR_CREATEDIBSECTION ..)而不是CBitmap :: LoadBitmap。二,你必须将位图中每个像素的RGB值“预乘”到它们各自的A值。这是AlphaBlend函数的要求,请参阅this MSDN page上的AlphaFormat描述。 Ť
lpng有一个工作代码,可以对DIB数据进行预乘。