我需要创建一个自定义控件来显示带有 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 值。有人可以建议优化的方法吗?

有帮助吗?

解决方案

我通常这样做的方法是通过 DIBSection - 一个与设备无关的位图,您可以直接修改其像素。不幸的是,MFC 不支持 DIBSections:您必须使用 Win32 函数 CreateDIBSection() 才能使用它。

首先将位图加载为 32 位 RGBA(即每个像素四个字节:一种是红色,一种是绿色,一种是蓝色,一种是 alpha 通道)。在控件中,创建一个适当大小的 DIBSection。然后,在绘画例程中

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

您可以简单地通过查看 Alpha 通道值来创建给定原始位图数据的蒙版 - 我不确定您在这里要问什么。

其他提示

对于未来的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);
}

您需要使用背景颜色 alpha混合,然后取出alpha通道将其绘制到控件上。

alpha通道应该只是图像的每个第4个字节。您可以直接将其用于蒙版,也可以将每个第4个字节复制到新的蒙版图像。

使用 AlphaBlend函数<绘制它非常容易/ A>

至于你的面具,你需要获取位图的位并检查您感兴趣的每个像素的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。二,你必须<!> quot; pre-multiply <!> quot;位图中每个像素的RGB值到它们各自的A值。这是AlphaBlend函数的要求,请参阅此MSDN页面。 Ť

lpng 有一个工作代码,可以对DIB数据进行预乘。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top