我需要创建一个自定义控件来显示带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函数).
非透明区域正在正确绘制,但我得到白色位图应该是透明的.
任何帮助???
这是截图..不容易看到,但蓝色圆圈周围有一个白色矩形: 替代文字http://img385.imageshack.us/img385/7965/alphamh8.png
好.我知道了!我必须预先乘以每个像素的alpha值.有人可以建议这样做的优化方法吗?
对于未来的谷歌用户,这是一个有效的预乘功能.请注意,这取自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); }
我通常这样做的方法是通过DIBSection - 一个独立于设备的位图,你可以直接修改像素.不幸的是,没有任何MFC支持DIBSections:你必须使用Win32函数CreateDIBSection()来使用它.
首先将位图加载为32位RGBA(即每个像素四个字节:一个红色,一个绿色,一个蓝色,一个用于alpha通道).在控件中,创建适当大小的DIBSection.然后,在油漆程序中
将位图数据复制到DIBSection的位图数据中,使用alpha通道字节将位图图像与背景颜色混合.
创建设备上下文并在其中选择DIBSection.
使用BitBlt()从新设备上下文复制到绘图设备上下文.
只需查看alpha通道值,您就可以根据原始位图数据创建一个遮罩 - 我不确定您在这里要求的是什么.