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

Create a mask from a bitmap and transparency color - 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)) 的图像:

这是图像遮罩(分别是预期结果和实际结果 (left-to-right)):


此处引用: theForger’s Win32 API Programming Tutorial - Transparent Bitmaps

此代码有效,尽管它并没有完全按照您的想法进行操作。如果您没有看到任何输出,则无论问题出在这个函数之外。

这段代码正在做的是设置两个位图,供旧的 Win32 技术使用,用于绘制精灵,在该技术中,您使用不同的光栅操作代码两次调用 BitBlt,一次绘制蒙版,一次绘制精灵这样精灵的背景就不会被绘制。请注意,它 既创建了掩码又更改了源位图 。 ("const HBITMAP bitmapHandle" 中的 "const" 并没有真正做任何事情。位图句柄就像一个资源 ID,引用由 Windows 以 C++ 所不知道的方式管理的位图。使一个 const并不意味着它所引用的位图不能更改。)如果您查看代码,最终的 BitBlit 位图传输到源位图中,而不是掩码。这个调用所做的是涂黑源位图中的关键颜色,这是使用 rop 代码和两个 blits 绘制精灵所需要的。

顺便说一句,这种技术是一种非常古老的方法,被 MaskBlt into the API, which will do what you want in one call. But even further, MaskBlt at this point is out-dated. You probably want to paint sprites for a game or something game-like. Almost certainly What you actually want is to load PNGs with per-pixel alpha and paint them with alpha compositing. You can do this with GDI+ or with an open source graphics library such as 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;

输出如下:

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