如何绘制 32 位 Alpha 通道位图?

发布于 2024-07-08 13:21:34 字数 1384 浏览 12 评论 0原文

我需要创建一个自定义控件来显示带有 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 中(我还尝试了 AlphaBlend CDC 对象的函数)。

非透明区域已正确绘制,但位图应该透明的地方却变成白色。

有什么帮助吗???

这是一个屏幕截图..不太容易看到,但蓝色圆圈周围有一个白色矩形: 替代文本 http://img385.imageshack.us/img385/7965/alphamh8.png

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

I need to create a custom control to display bmp images with alpha channel. The background can be painted in different colors and the images have shadows so I need to truly "paint" the alpha channel.

Does anybody know how to do it?

I also want if possible to create a mask using the alpha channel information to know whether the mouse has been click on the image or on the transparent area.

Any kind of help will be appreciated!

Thanks.

Edited(JDePedro): As some of you have suggested I've been trying to use alpha blend to paint the bitmap with alpha channel. This just a test I've implemented where I load a 32-bit bitmap from resources and I try to paint it using AlphaBlend function:

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();
}

This is just a test so I put the code in the OnPaint of the dialog (I also tried the AlphaBlend function of the CDC object).

The non-transparent areas are being painted correctly but I get white where the bitmap should be transparent.

Any help???

This is a screenshot..it's not easy to see but there is a white rectangle around the blue circle:
alt text http://img385.imageshack.us/img385/7965/alphamh8.png

Ok. I got it! I have to pre-multiply every pixel for the alpha value. Someone can suggest the optimized way to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

旧瑾黎汐 2024-07-15 13:21:34

对于未来的谷歌用户,这里有一个有效的预乘函数。 请注意,这是取自 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);
}

For future google users, here is a working pre-multiply function. Note that this was taken from 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);
}

So then your OnPaint becomes:

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();
}

And the loading of the image (in the constructor of your control):

if ((HBITMAP)m_Image == NULL) {
    m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}
深爱不及久伴 2024-07-15 13:21:34

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

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

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

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

The way I usually do this is via a DIBSection - a device independent bitmap that you can modify the pixels of directly. Unfortunately there isn't any MFC support for DIBSections: you have to use the Win32 function CreateDIBSection() to use it.

Start by loading the bitmap as 32-bit RGBA (that is, four bytes per pixel: one red, one green, one blue and one for the alpha channel). In the control, create a suitably sized DIBSection. Then, in the paint routine

  • Copy the bitmap data into the DIBSection's bitmap data, using the alpha channel byte to blend the bitmap image with the background colour.
  • Create a device context and select the DIBSection into it.
  • Use BitBlt() to copy from the new device context to the paint device context.

You can create a mask given the raw bitmap data simply by looking at the alpha channel values - I'm not sure what you're asking here.

顾冷 2024-07-15 13:21:34

您需要与背景颜色进行 alpha 混合,然后取出 alpha 通道进行绘制它到控件。

Alpha 通道应该仅位于图像的每 4 个字节中。 您可以直接将其用于掩码,也可以将每 4 个字节复制到新的掩码图像中。

You need to do an alpha blend with your background color, then take out the alpha channel to paint it to the control.

The alpha channel should just be every 4th byte of your image. You can use that directly for your mask, or you can just copy every 4th byte to a new mask image.

大姐,你呐 2024-07-15 13:21:34

使用 AlphaBlend 函数 绘制它非常容易。

至于你的面具,你需要 获取这些位位图的 并检查您感兴趣的每个像素的 Alpha 通道字节。

Painting it is very easy with the AlphaBlend function.

As for you mask, you'll need to get the bits of the bitmap and examine the alpha channel byte for each pixel you're interested in.

可爱咩 2024-07-15 13:21:34

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

正确设置此数组后,您可以像这样计算所需的值:

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

An optimised way to pre-multiply the RGB channels with the alpha channel is to set up a [256][256] array containing the calculated multiplication results. The first dimension is the alpha value, the second is the R/G/B value, the values in the array are the pre-multiplied values you need.

With this array set up correctly, you can calculate the value you need like this:

R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];
梦里梦着梦中梦 2024-07-15 13:21:34

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

首先使用 ::LoadImage( .. LR_CREATEDIBSECTION ..) 而不是 CBitmap::LoadBitmap。 第二,您必须将位图中每个像素的 RGB 值“预乘”到它们各自的 A 值。 这是 AlphaBlend 函数的要求,请参阅 此 MSDN 页面

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

You are on the right track, but need to fix two things.

First use ::LoadImage( .. LR_CREATEDIBSECTION ..) instead of CBitmap::LoadBitmap. Two, you have to "pre-multiply" RGB values of every pixel in a bitmap to their respective A value. This is a requirement of AlphaBlend function, see AlphaFormat description on this MSDN page. T

The lpng has a working code that does the premultiplication of the DIB data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文