c - 从位图和透明度颜色创建蒙版 - Windows GDI
问题描述
这是我过去两天尝试调试的代码:
#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)
) 的图像:
这是图像掩码(分别是预期结果和实际结果(从左到右)):
解决方案
此代码有效,尽管它并没有完全按照您的想法执行。如果您没有看到任何输出,则无论问题出在这一功能之外。
这段代码所做的是设置两个位图供旧的 Win32 技术使用来绘制精灵,在该技术中,您使用不同的光栅操作代码对 BitBlt 进行两次调用,一个绘制蒙版,一个绘制精灵,以便精灵的背景不会被画。请注意,它既是创建掩码,也是更改源位图. (“const HBITMAP bitmapHandle”中的“const”实际上并没有做任何事情。位图句柄就像一个资源 ID,指的是由 Windows 管理的位图,而 C++ 对此一无所知。制作一个 const 并不意味着位图它所指的不能被改变。)如果你看一下代码,最终的 BitBlit 会进入源位图,而不是掩码。这个调用的作用是将源位图中的关键颜色涂黑,这是使用 rop 代码和两个 blit 绘制 sprite 所需要的。
顺便说一句,这种技术是一种非常古老的方法,被将MaskBlt引入 API 所取代,它可以在一次调用中完成您想要的操作。但更进一步,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;
输出如下:
我不确定为什么你没有得到输出,但很可能你没有成功加载位图或者你没有成功地绘制到屏幕上。
推荐阅读
- python - 使用 64 位训练的 Scikits-Learn RandonForest 并在 64 位 PyCharm 中打开时出错
- swift - 如何在保持 LiDAR 调试网格的同时禁用摄像头馈送?
- c - 多个目标但相同的依赖项
- python - Python Pandas - 将 2D df 转换为 3D
- java - java中将JSON数组字符串转换为列表的通用方法
- amazon-web-services - 用于 prod 的 k8s 高可用性配置边缘案例
- r - r:在特定条件下将日期添加到日期变量
- javascript - 你如何只映射索引的一部分?反应
- python - 如何在python中构建一个函数来解决:如果X公司的标签一年一次从0变为1,那么我们返回1,否则返回0?
- python - pandas.read_csv() 不加载特殊字符