在 Win32 下,通过执行以下操作从位图生成单色位掩码以供透明度使用是一种常见技术:
SetBkColor(hdcSource, clrTransparency);
VERIFY(BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcSource, 0, 0, SRCCOPY));
假设 hdcSource 是保存源图像的内存 DC,hdcMask 是保存相同大小的单色位图的内存 DC(因此两者都是 32x32,但源是 4 位颜色,而目标是 1 位单色)。
但是,当源是 32 位颜色 + alpha 时,这对我来说似乎失败了。 我没有在 hdcMask 中获得单色位图,而是获得全黑的蒙版。 没有位设置为白色 (1)。 而这适用于 4 位颜色源。
我的 search-foo 失败了,因为我似乎找不到任何对此特定问题的引用。
我发现这确实是我的代码中的问题:即,如果我使用 16 色(4 位)的源位图,它就可以工作;如果我使用 32 位图像,它会生成全黑蒙版。
对于 32 位彩色图像,我应该使用替代方法吗? Alpha 通道是否存在覆盖上述技术正常行为的问题?
感谢您提供的任何帮助!
附录:我仍然无法找到一种技术来为我的 GDI+ 生成的源位图创建有效的单色位图。
我通过根本不生成单色位掩码在一定程度上缓解了我的特殊问题,而是使用TransparentBlt(),这似乎是正确的(但我不知道他们在内部做了什么,这有什么不同)这使他们能够正确遮盖图像)。
拥有一个非常好的、有效的函数可能会很有用:
HBITMAP CreateTransparencyMask(HDC hdc, HBITMAP hSource, COLORREF crTransparency);
无论 hSource 的颜色深度如何,它始终创建有效的透明蒙版。
如果有 Alpha 通道就无法做到这一点。 COLORREF 将前 8 位用于多种目的,包括指定较低的 3 个字节是否是当前调色板的颜色表索引或 RGB 三元组。因此,除了 clrTransparency 的高字节中的 0x00 之外,您不能指定任何内容。
如果您有一个 Alpha 位图,那么对于仍然“不知道”Alpha 通道的 GDI,没有明智的方法来实际比较 24 位 BkColor 与位图中的 32 位像素。
我希望 GDI 将 32bpp 位图中的 Alpha 通道视为“保留”,并且仅成功比较保留通道为零的像素。也就是说,无论如何,您的蒙版颜色必须完全透明才有机会成功。 (并且,如果您制作了合法的预乘位图,则意味着 RGV 值也将为零,而不是限制您对遮罩颜色的选择:P)
可以做:)
正如上面“Chris Becke”所指出的,只有当保留的 Alpha 通道为零时,GDI 才能进行比较。
从 BITMAP::GetHBITMAP() 获取的 HBITMAP 返回一个 HBITMAP,其中 Alpha 通道全部设置为 0xFF。
这必须是 0x00 SetBkColor() 比较工作。
因此,Soln:循环遍历每个像素并将 Alpha 分量设置为零。
Bitmap img(L"X.bmp");
HBITMAP hBM;
img.GetHBITMAP(Color::White, &hBM);
BITMAP bm;
GetObject(g_hbmBall, sizeof(BITMAP), &bm);
for(UINT i = 0, n = -1; i < bm.bmHeight; i++)
for(UINT j = 0; j < bm.bmWidth; j++)
{
n += 4; // Once per Pixel of 4 Bytes
((LPBYTE)bm.bmBits)[n] = 0;
}
// Now SetBkColor and BitBlt will work as expected
对我有用的方法是先将位图从 32 位转换为 24 位。
1. CreateCompatibleDC
2. CreateDIBSection with 24 as the biBitCount.
3. SelectObject
4. BitBlt from 32bit DC to 24 bit. This removes alpha.
5. BitBlt from 24 bit DC to the monochrome DC works as expected.
在我的机器上,这比 Ujjwal 答案中的双循环执行得更快。
另一种方法是自己扫描像素并根据源颜色(或源 Alpha 与阈值)生成单色位图。
请注意,如果您使用的是 GDI+,那么根据操作的不同,像素可能已被抗锯齿,导致它们都无法与您的“透明”颜色完全匹配。
是的,您可以仅使用 GDI 调用从 32bpp 位图中的 Alpha 通道构建单色蒙版,而无需进行单独的像素操作。 请参阅下面的代码:
HBITMAP Alpha2Mask(HBITMAP hBmpAlpha)
{
HBITMAP hBmpMask = 0;
BITMAP bmp;
GetObject(hBmpAlpha, sizeof(BITMAP), &bmp);
if (bmp.bmBitsPixel == 32)
{
HDC hDCsrc = CreateCompatibleDC(0);
HBITMAP hBmpOldSrc = (HBITMAP)SelectObject(hDCsrc, hBmpAlpha);
AndRGBArect(hDCsrc, 0, 0, bmp.bmWidth, bmp.bmHeight, { 0x00, 0x00, 0x00, 0xFF }); //Clear the Red, Green and Blue components (making them black) and leave the Alpha unchanged
HDC hDCdest = CreateCompatibleDC(hDCsrc);
HBITMAP hBmpDest = CreateCompatibleBitmap(hDCdest, bmp.bmWidth, bmp.bmHeight);
HBITMAP hBmpOldDst = (HBITMAP)SelectObject(hDCdest, hBmpDest);
//SelectObject(hDCdest, GetStockObject(DEFAULT_PALETTE));
//PatBlt(hDCdest, 0, 0, bmp.bmWidth, bmp.bmHeight, WHITENESS); //Paint hBmpDest all white;
OrRGBArect(hDCdest, 0, 0, bmp.bmWidth, bmp.bmHeight, { 0xFF, 0xFF, 0xFF, 0xFF }); //Paint hBmpDest all white and set its Alpha as opaque
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.SourceConstantAlpha = 255;
bfn.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hDCdest, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCsrc, 0, 0, bmp.bmWidth, bmp.bmHeight, bfn); //After this, the hBmpDest contains the per-pixel Alpha conveted to grayscale RGB.
hBmpMask = CreateBitmap(bmp.bmWidth, bmp.bmHeight, 1, 1, NULL);
SelectObject(hDCsrc, hBmpMask); //Reuse the hDCsrc as the final output DC (a deceptive name now). Push out the hBmpAlpha
SetBkColor(hDCsrc, RGB(0xFF, 0xFF, 0xFf)); //Set the background to white
BitBlt(hDCsrc, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCdest, 0, 0, SRCCOPY); //Everything with the background colour ends up white in the mask (meaning "transparent") while everythig else ends up black (meaning "opaque"). Ignores per-pixel Alphas.
hBmpMask = (HBITMAP)SelectObject(hDCsrc, hBmpOldSrc); //Push out hBmpMask
DeleteObject(SelectObject(hDCdest, hBmpOldDst)); //Push out and delete hBmpDest
DeleteDC(hDCdest);
DeleteDC(hDCsrc);
}
return hBmpMask;
}
上面的函数需要下面 2 个辅助函数:
void AndRGBArect(HDC hdc, int x, int y, int cx, int cy, RGBQUAD rgba)
{
BITMAPINFO bi = {};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 1;
bi.bmiHeader.biHeight = 1;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB; // BI_ALPHABITFIELDS;
StretchDIBits(hdc, x, y, cx, cy, 0, 0, 1, 1, &rgba, &bi, DIB_RGB_COLORS, SRCAND);
}
void OrRGBArect(HDC hDC, int x, int y, int cx, int cy, RGBQUAD rgba)
{
BITMAPINFO bi = {};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 1;
bi.bmiHeader.biHeight = 1;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB; // BI_ALPHABITFIELDS;
StretchDIBits(hDC, x, y, cx, cy, 0, 0, 1, 1, &rgba, &bi, DIB_RGB_COLORS, SRCPAINT);
}