HBITMAP如何添加透明度/alpha通道

3
我正在尝试为hbitmap对象添加透明度,但它从未绘制过任何内容 :/
这是我用来绘制句柄的代码。
HDC hdcMem = CreateCompatibleDC(hDC);
    HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, m_hBitmap);

    BLENDFUNCTION blender = {AC_SRC_OVER, 0, (int) (2.55 * 100), AC_SRC_ALPHA}; // blend function combines opacity and pixel based transparency
    AlphaBlend(hDC, x, y, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, blender);

    SelectObject(hdcMem, hbmOld);
    DeleteDC(hdcMem);

以下是应该添加 alpha 通道到 hbitmap 的代码:

BITMAPINFOHEADER bminfoheader;
    ::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
    bminfoheader.biSize        = sizeof(BITMAPINFOHEADER);
    bminfoheader.biWidth       = m_ResX;
    bminfoheader.biHeight      = m_ResY;
    bminfoheader.biPlanes      = 1;
    bminfoheader.biBitCount    = 32;
    bminfoheader.biCompression = BI_RGB;

    HDC windowDC = CreateCompatibleDC(0);
    unsigned char* pPixels = new unsigned char[m_ResX * m_ResY * 4];

    GetDIBits(windowDC, m_hBitmap, 0, m_ResY, pPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // load pixel info

    // add alpha channel values of 255 for every pixel if bmp
    for (int count = 0; count < m_ResX * m_ResY; count++)
    {
        pPixels[count * 4 + 3] = 255; <----  here i've tried to change the value to test different transparency, but it doesn't change anything
    }
    SetDIBits(windowDC, m_hBitmap, 0, GetHeight(), pPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS); // save the pixel info for later manipulation

    DeleteDC(windowDC);

编辑:

这是我创建位图的代码,稍后在其他代码中填充像素数据。

m_hBuffer = CreateBitmap( m_ResX, m_ResY, 1, 32, nullptr );

我认为你必须使用GDI+来处理alpha通道。旧的GDI库不支持32位图像。请参考:https://dev59.com/u2865IYBdhLWcg3wLrhE - Mordachai
1
@Mordachai - 这对大多数 GDI 函数都是正确的 - 比如 BitBlt - 但 AlphaBlend(在 Win2K 中添加)被记录为明确支持源图像中的每个像素的 alpha。 - BrendanMcK
@joeco,你尝试过进行大量调试以缩小问题可能出现的范围吗?例如,首先尝试使用BitBlt,只是为了确保其余的代码和逻辑是正确的。还可以尝试更改alpha更改代码,将所有G和B通道设置为与R通道相同-只是为了确保您的位图操作代码是正确的。顺便说一下,注意AlphaBlend期望源位图R / G / B为“预乘”,因此如果您添加了alpha通道,则可能需要在该阶段适当地调整R / G / B。 - BrendanMcK
@BrendanMcK,我已经尝试编辑像素的其他信息,但实际上什么也没发生,有人知道为什么吗? :/ 注意:如果我更改SourceConstantAlpha,那么我的图像确实会变成透明的,但完全透明。 - ColmanJ
@joeco - 谜团解开了 - 我想;请看下面的可能答案! - BrendanMcK
@BrendanMcK 在我上一条评论中,SourceConstantAlpha 只在我不使用 AC_SRC_ALPHA 时起作用,可能是我忘记说的重要事情。 - ColmanJ
1个回答

3

这是一个有趣的问题!

猜猜这个会输出什么?

#include <stdio.h>

int main()
{
    printf("%d\n", (int) (2.55 * 100));
    return 0;
}

答案是254,而不是255。这里发生了两件事情:
1.浮点数通常是不精确的 - 2.55这个数在二进制值中并不能完美地表示为2.55 - 可能是类似于2.5499963...这样的值(我是随意编造的,但你应该明白我的意思 - 实际上这个数字被表示成2的分数之和 - 因为二进制是基于2的 - 所以像.5或.25这样的数字可以被完美地表示,但大多数其他数字都会被表示为近似值。你通常不会注意到这一点,因为浮点打印程序将会将其转换回十进制进行显示,这实际上引入了另一个不精确性,最终会抵消掉第一个不精确性:所以你看到的赋值或输出值与存储在内存中的表示值并不完全相同)。
2.强制转换为整数后会截断 - 即向下舍入。
将它们结合起来,你的(2.55 * 100)得到的是254,而不是255 - 而你需要的是魔法值255来使每个像素的透明度正常工作。
所以教训是坚持使用纯整数。或者,如果你真的需要从浮点数转换为整数,请注意正在发生的事情,并解决它(例如:添加0.5,然后截断 - 或使用类似的技巧)。
顺便说一句,这是其中一种情况,逐行检查代码并在每个步骤检查所有输入和输出(调试时你永远不会太多疑!)可能会显示出问题;就在你进入AlphaBlend之前,在将鼠标悬停在该参数上时,你应该看到254,并意识到有些问题。

我必须承认,我不知道浮点数的那个问题,但它并没有解决我的问题:(当我将值设置为255时,它仍然不会绘制任何东西。我再次在msdn上检查了一下,它说:“当您只想使用每像素alpha值时,请将SourceConstantAlpha值设置为255(不透明)。”,所以如果我理解正确,它说当你只想使用每像素alpha时,它必须是255,这意味着你应该能够同时使用两者,对吗? - ColmanJ
可能这是问题的一部分。很有可能你在这里有几个漏洞。如果需要,现在是逐行查看代码并确认哪些部分绝对可行的时候了。你是否从所有API中得到成功返回值?如果使用普通的BitBlt而不是AlphaBlend,代码是否工作?使用BitBlt,修改比如蓝色通道,你是否看到了预期的结果? - BrendanMcK
我很确定问题出在我的代码中,当我尝试添加/更改 alpha 通道时。BitBlt 的工作方式像应该的那样,或者它只是显示我的图像,但当我尝试更改蓝色或所有通道的值时,视觉上没有任何变化。我已经检查了返回值,并且没有任何方法失败。 - ColmanJ
嗯,你在SetDIBits中有GetHeight(),但在GetDIBits中有m_ResY - 它们是相同的值吗?可能你设置的位比你想象的少?改变任何通道都不起作用的事实表明,获取、修改或设置之一不起作用。你可以使用已知的RGB值踩坏所有像素来测试隔离的设置部分;或者手动检查一些数组以检查获取部分。 - BrendanMcK
m_ResY和GetHeight()完全相同,我还检查了SetDIBits和GetDiBits的返回值,它们显示我得到了与我更改的扫描线数量相同的扫描线。但是当我查看指针时,它全部为零oO。 - ColmanJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接