Mfc 如何绘制32位alpha通道位图?

Mfc 如何绘制32位alpha通道位图?,mfc,alphablending,bmp,Mfc,Alphablending,Bmp,我需要创建一个自定义控件来显示带有alpha通道的bmp图像。背景可以用不同的颜色绘制,图像有阴影,所以我需要真正“绘制”alpha通道 有人知道怎么做吗 如果可能的话,我还想使用alpha通道信息创建一个遮罩,以了解鼠标是单击了图像还是单击了透明区域 任何形式的帮助都将不胜感激 谢谢 编辑(JDePedro):正如你们中的一些人所建议的,我一直在尝试使用alpha混合来使用alpha通道绘制位图。这只是我实现的一个测试,我从参考资料中加载了一个32位位图,并尝试使用AlphaBlend函数绘制

我需要创建一个自定义控件来显示带有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函数)

非透明区域被正确绘制,但位图应该是透明的地方我得到了白色

有什么帮助吗

这是一个屏幕截图。不容易看到,但蓝色圆圈周围有一个白色矩形:

嗯。我得到了它!我必须为alpha值预乘每个像素。有人可以建议优化的方法吗?

您需要对背景色进行调整,然后取出alpha通道将其绘制到控件上


alpha通道应该是图像的每4个字节。您可以直接将其用于掩码,或者只需将每4个字节复制到一个新的掩码图像。

我通常的做法是通过DIBSection(一种与设备无关的位图),您可以直接修改其像素。不幸的是,没有任何MFC对DibSection的支持:必须使用Win32函数CreateDIBSection()才能使用它

首先将位图加载为32位RGBA(即,每个像素四个字节:一个红色、一个绿色、一个蓝色和一个用于alpha通道)。在控件中,创建大小合适的分区。然后,在绘画程序中

  • 将位图数据复制到DIBSection的位图数据中,使用alpha通道字节将位图图像与背景颜色混合
  • 创建一个设备上下文并在其中选择DIBSection
  • 使用BitBlt()从新设备上下文复制到绘制设备上下文

只要查看alpha通道值,就可以在给定原始位图数据的情况下创建一个掩码-我不确定您在这里要问什么。

使用


至于遮罩,您需要检查位图的颜色,并检查您感兴趣的每个像素的alpha通道字节。

将RGB通道与alpha通道预乘法的优化方法是设置一个包含计算乘法结果的[256][256]数组。第一个维度是alpha值,第二个维度是R/G/B值,数组中的值是您需要的预乘值

正确设置此数组后,可以按如下方式计算所需的值:

R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];

你走在正确的轨道上,但需要解决两件事

首先使用::LoadImage(…LR_CREATEDIBSECTION..)代替CBitmap::LoadBitmap。第二,必须将位图中每个像素的RGB值“预乘”为各自的a值。这是AlphaBlend功能的一项要求,请参见上的AlphaFormat说明。T


具有对DIB数据进行预乘法的工作代码。

对于未来的google用户,这里有一个工作的预乘法函数。请注意,这是从

以及图像的加载(在控件的构造函数中):


嗨,马克,让我看看我是否理解你的建议。假设我从CStatic/CWnd派生控件,并添加一个CBitmap作为成员。然后每次绘制控件时(在OnPaint方法中),我都必须获得背景并与图像混合?(继续)我说得对吗?每件事我都得自己做吗?它似乎可以有一个高性能的成本…你(任何人)能告诉我最好的方法(更优化)做这件事吗?也许是一些戈德的文章,甚至是代码片段?谢谢。如果我希望透明图像显示在静态标签上怎么办?pStatic->ModifyStyle(0xF,SS_位图);pStatic->SetBitmap(位图);OnPaint()中的静态变量肯定是一个非常糟糕的主意。如果你有一个以上的按钮,这将失败。pmdone应该是每个MyButton的成员变量。然而,BLENDFUNCTION可能是静态的。或者更好:您可以在LoadFromResource()之后立即调用PremultiplyBitmapAlpha(),并将任何窗口的DC与GetWindowDC()一起使用。那么就不再需要变量pmdone了。显然,m_图像也应该是静态的。无需为每个按钮加载相同的位图。不,您不能在LoadFromResource()之后调用它(至少不是以这里显示的方式),因为您可能在创建任何窗口之前实例化了这些对象。值得怀疑的是,对64 KiB大数组的随机访问是否比执行计算快。特别是当操作可以如此完美地分布在向量单元上时。它当然会给CPU缓存带来不必要的压力,但回报很少(如果有的话)。
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);
}
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);
}