从位图和透明度颜色创建蒙版 - Windows GDI

标签 c windows winapi 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)):

这是图像蒙版(分别是预期结果和实际结果(从左到右)):


引用文献: theForger’s Win32 API Programming Tutorial - Transparent Bitmaps

最佳答案

这段代码可以工作,尽管它并没有完全按照您的想法进行。如果您没有看到任何输出,则无论问题是什么,都属于该函数的外部问题。

这段代码的作用是设置两个位图,供旧的 Win32 技术使用来绘制 Sprite ,其中使用不同的光栅操作代码对 BitBlt 进行两次调用,一次绘制 mask ,一次绘制 Sprite ,这样 Sprite 的背景将不会被绘制。请注意,它既创建了蒙版,又更改了源位图。 (“const HBITMAP bitmapHandle”中的“const”实际上并没有做任何事情。位图句柄就像一个资源 ID,以 C++ 不知道的方式引用 Windows 管理的位图。使一个 const 并不意味着该位图它所指的内容不能更改。)如果您查看代码,您会发现最终的 BitBlit 是位图传输到源位图,而不是掩码。此调用的作用是将源位图中的关键颜色涂黑,这是使用 rop 代码和两个 blit 绘制 Sprite 所需的操作。

顺便说一下,这种技术是一种非常古老的方法,已被 MaskBlt 的引入所取代。进入 API,它将在一次调用中完成您想要的操作。但更进一步,MaskBlt 在这一点上已经过时了。您可能想为游戏或类似游戏的东西绘制 Sprite 。几乎可以肯定,您真正想要的是使用每像素 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;

输出如下:

enter image description here

我不确定为什么您没有获得输出,但很可能您没有成功加载位图,或者没有成功绘制到屏幕上。

关于从位图和透明度颜色创建蒙版 - Windows GDI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59422456/

相关文章:

仅将字符串的第一个单词转换为小写

c - 我如何改进这个简单的待办事项列表管理器的程序设计?

c - objdump——文件被截断

c++ - 当 "required"运行时库不存在时显示(自定义)错误?

java - OS X 上 Windows 环境下的测试程序

c++ - ChangeServiceConfig2 使用GetProcAddress 支持多版本windows

c++ - 在 C++ 和 C 之间传递数据

windows和linux的php md5_file区别

C++ MessageBox (Windows.h) - 它有什么作用,我怎样才能让它不可见(或等效)?

c++ - 如何在 C++ 中以编程方式删除 Windows 服务