从位图和透明度颜色创建蒙版 - Windows GDI
Posted
技术标签:
【中文标题】从位图和透明度颜色创建蒙版 - Windows GDI【英文标题】:Create a mask from a bitmap and transparency color - Windows GDI 【发布时间】:2020-04-12 19:15:33 【问题描述】:这是我过去两天尝试调试的代码:
#include <windows.h>
HBITMAP createImageMask(HBITMAP bitmapHandle, const COLORREF transparencyColor)
// For getting information about the bitmap's height and width in this context
BITMAP bitmap;
// Create the device contexts for the bitmap and its mask
HDC bitmapGraphicsDeviceContext = CreateCompatibleDC(NULL);
HDC bitmapMaskGraphicsDeviceContext = CreateCompatibleDC(NULL);
// For the device contexts to re-select the initial object they initialized with
// and de-select the bitmap and mask
HGDIOBJ bitmapDummyObject;
HGDIOBJ bitmapMaskDummyObject;
// The actual mask
HBITMAP bitmapMaskHandle;
// 1. Generate the mask.
GetObject(bitmapHandle, sizeof(BITMAP), &bitmap);
bitmapMaskHandle = CreateBitmap(bitmap.bmWidth, bitmap.bmHeight, 1, 1, NULL);
// 2. Setup the device context for the mask (and the bitmap)
// — also get the initial selected objects in the device contexts.
bitmapDummyObject = SelectObject(bitmapGraphicsDeviceContext, (HGDIOBJ) (HBITMAP) bitmapHandle);
bitmapMaskDummyObject = SelectObject(bitmapMaskGraphicsDeviceContext, (HGDIOBJ) (HBITMAP) bitmapMaskHandle);
// 3. Set the background color of the mask.
SetBkColor(bitmapGraphicsDeviceContext, transparencyColor);
// 4. Copy the bitmap to the mask and invert it so it blends with the background color.
BitBlt(bitmapMaskGraphicsDeviceContext, 0, 0, bitmap.bmWidth, bitmap.bmHeight, bitmapGraphicsDeviceContext, 0, 0, SRCCOPY);
BitBlt(bitmapGraphicsDeviceContext, 0, 0, bitmap.bmWidth, bitmap.bmHeight, bitmapMaskGraphicsDeviceContext, 0, 0, SRCINVERT);
// 5. Select the bitmaps out before deleting the device contexts to avoid any issues.
SelectObject(bitmapGraphicsDeviceContext, bitmapDummyObject);
SelectObject(bitmapMaskGraphicsDeviceContext, bitmapMaskDummyObject);
// Clean-up
DeleteDC(bitmapGraphicsDeviceContext);
DeleteDC(bitmapMaskGraphicsDeviceContext);
// Voila!
return bitmapMaskHandle;
它会创建一个位图句柄 (HBITMAP
),并且不会产生任何错误(来自 GetLastError
函数)。
问题:它不会生成我应用到它的位图的单色版本, 而是创建一个仅填充黑色的位图。
那么代码是怎么回事,我做错了什么? 或者如何正确创建位图蒙版?
(如果可能,我正在尝试不使用 GDI+ 或其他库)
这是透明颜色为红色的图像 (RGB(255, 0, 0)
):
这是图像掩码(预期结果和实际结果分别(从左到右)):
此处参考: theForger’s Win32 API Programming Tutorial - Transparent Bitmaps
【问题讨论】:
看来代码来自winprog.org/tutorial/transparency.html。您是否确保“调用此函数时未将位图选择到另一个 HDC”? @GSerg:在我的代码中,位图是从一个文件中加载的,没有引用任何 HDC。然后在掩码之前使用函数createImageMask
创建掩码位图,然后将位图选择到 HDC 中。
bitmapMaskHandle
在您删除该设备上下文时仍被选中到 bitmapMaskGraphicsDeviceContext
。我认为现代版本的 GDI 已经对此非常宽容,但从技术上讲,它违反了 API。您应该在删除 DC 之前选择它。另外,向我们展示绘画代码。您的蒙版是单色位图,尝试显示时很容易出错。
@AdrianMcCarthy:通过“在删除之前选择它退出...”你是指SelectObject(hdc, myMask)
然后SelectObject(hdc, someDummyObject)
吗?
SelectObject
返回旧对象。保存它,然后在完成后重新选择它。
【参考方案1】:
此代码有效,尽管它并没有完全按照您的想法执行。如果您没有看到任何输出,则无论问题出在这一功能之外。
这段代码所做的是设置两个位图供旧的 Win32 技术使用来绘制精灵,在该技术中,您使用不同的光栅操作代码对 BitBlt 进行两次调用,一个绘制蒙版,一个绘制精灵,这样不会绘制精灵的背景。请注意,它既创建了掩码,也更改了源位图。 (“const HBITMAP bitmapHandle”中的“const”实际上并没有做任何事情。位图句柄就像一个资源 ID,指的是由 Windows 管理的位图,而 C++ 对此一无所知。制作一个 const 并不意味着位图它所指的不能被改变。)如果你看一下代码,最终的 BitBlit 会进入源位图,而不是掩码。此调用的作用是将源位图中的关键颜色涂黑,这是使用 rop 代码和两个 blit 绘制 sprite 所必需的。
顺便说一句,这种技术是一种非常古老的方法,被在 API 中引入 MaskBlt 所取代,这将在一次调用中完成您想要的操作。但更进一步,MaskBlt 在这一点上已经过时了。您可能想为游戏或类似游戏的东西绘制精灵。几乎可以肯定,您真正想要的是使用每像素 alpha 加载 PNG 并使用 alpha 合成来绘制它们。您可以使用GDI+ 或FreeImage 等开源图形库来完成此操作。
在任何情况下,以下是演示此掩码代码实际工作的最少代码。只需更改以下来源,使“D:\test\hex_badge.bmp”成为您问题中具有该六角位图的任何位置的路径。
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
HBITMAP g_bmp;
HBITMAP g_bmpMask;
HBITMAP createImageMask( HBITMAP bitmapHandle, const COLORREF transparencyColor)
// For getting information about the bitmap's height and width in this context
BITMAP bitmap;
// Create the device contexts for the bitmap and its mask
HDC bitmapGraphicsDeviceContext = CreateCompatibleDC(NULL);
HDC bitmapMaskGraphicsDeviceContext = CreateCompatibleDC(NULL);
// The actual mask
HBITMAP bitmapMaskHandle;
// 1. Generate the mask.
GetObject(bitmapHandle, sizeof(BITMAP), &bitmap);
bitmapMaskHandle = CreateBitmap(bitmap.bmWidth, bitmap.bmHeight, 1, 1, NULL);
// 2. Setup the device context for the mask (and the bitmap).
SelectObject(bitmapGraphicsDeviceContext, bitmapHandle);
SelectObject(bitmapMaskGraphicsDeviceContext, bitmapMaskHandle);
// 3. Set the background color of the mask.
SetBkColor(bitmapGraphicsDeviceContext, transparencyColor);
// 4. Copy the bitmap to the mask and invert it so it blends with the background color.
BitBlt(bitmapMaskGraphicsDeviceContext, 0, 0, bitmap.bmWidth, bitmap.bmHeight, bitmapGraphicsDeviceContext, 0, 0, SRCCOPY);
BitBlt(bitmapGraphicsDeviceContext, 0, 0, bitmap.bmWidth, bitmap.bmHeight, bitmapMaskGraphicsDeviceContext, 0, 0, SRCINVERT);
// Clean-up
DeleteDC(bitmapGraphicsDeviceContext);
DeleteDC(bitmapMaskGraphicsDeviceContext);
// Voila!
return bitmapMaskHandle;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
MSG msg = 0 ;
WNDCLASS wc = 0 ;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"minwindowsapp";
g_bmp = (HBITMAP)LoadImage(hInstance, L"D:\\test\\hex_badge.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
g_bmpMask = createImageMask(g_bmp, RGB(255, 0, 0));
if (!RegisterClass(&wc))
return 1;
if (!CreateWindow(wc.lpszClassName,
L"Minimal Windows Application",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL))
return 2;
while (GetMessage(&msg, NULL, 0, 0) > 0)
TranslateMessage(&msg);
DispatchMessage(&msg);
return 0;
LRESULT HandleWmPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
PAINTSTRUCT ps;
HDC hdcScr = GetDC(NULL);
HDC hdcBmp = CreateCompatibleDC(hdcScr);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcBmp, g_bmp);
HDC hdcMask = CreateCompatibleDC(hdcScr);
HBITMAP hbmOldMask = (HBITMAP) SelectObject(hdcMask, g_bmpMask );
HDC hdc = BeginPaint(hWnd, &ps);
BitBlt(hdc, 0, 0, 184, 184, hdcMask, 0, 0, SRCCOPY);
BitBlt(hdc, 184, 0, 184, 184, hdcBmp, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
SelectObject(hdcMask, hbmOldMask);
DeleteDC(hdcMask);
SelectObject(hdcBmp, hbmOld);
DeleteDC(hdcBmp);
ReleaseDC(NULL, hdcScr);
return 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_PAINT:
return HandleWmPaint(hWnd, wParam, lParam);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
输出如下:
我不确定您到底为什么没有得到输出,但很可能您没有成功加载位图,或者您没有成功地在屏幕上绘画。
【讨论】:
出于挫败感,我实际上复制并粘贴了您的答案中的示例代码,但结果仍然与以前相同(我认为我的 Windows 坏了) 我会尝试另一种方法,感谢@jwezorek 的帮助。 其实我很久以前写过一篇关于使用 FreeImage 来做这类事情的博文。 jwezorek.com/2012/02/blitting-with-per-pixel-in-alpha-in-win32 立即查看以上是关于从位图和透明度颜色创建蒙版 - Windows GDI的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的图片加上图层蒙版后,用画笔擦出半透明效果时候,擦出来的都是黑白的小方格,怎么才能擦出白的