GDI DC的透明度

时间:2015-03-04 03:32:09

标签: c++ winapi gdi

我有"简单"在屏幕上绘制一个具有透明度的位图的目标。这一点并不那么难:

#include <windows.h>
#include "BBKG.h"

HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int wH = 156;
static int wW = 166;

HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
    HDC mem0, mem1;
    HBITMAP hbmMask;
    BITMAP bm;
    GetObject(hbmColour, sizeof(BITMAP), &bm);
    hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
    mem0 = CreateCompatibleDC(0);
    mem1 = CreateCompatibleDC(0);
    SelectObject(mem0, hbmColour);
    SelectObject(mem1, hbmMask);
    SetBkColor(mem0, crTransparent);
    BitBlt(mem1, 0, 0, bm.bmWidth, bm.bmHeight, mem0, 0, 0, SRCCOPY);
    BitBlt(mem0, 0, 0, bm.bmWidth, bm.bmHeight, mem1, 0, 0, SRCINVERT);
    DeleteDC(mem0);
    DeleteDC(mem1);

    return hbmMask;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    hInst = hInstance;
    MSG  msg;
    HWND hwnd;
    WNDCLASSW wc;

    wc.style = 0;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.lpszClassName = L"nope";
    wc.hInstance = hInst;
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpfnWndProc = WndProc;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassW(&wc);
    hwnd = CreateWindowW(wc.lpszClassName, L"",
        WS_VISIBLE | WS_POPUP|  WS_EX_TRANSPARENT,
        100, 100, wW, wH, NULL, NULL, hInst, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0)) {
        //Workaround for focusables stealing my Esc key
        if (msg.message == WM_KEYDOWN){
            if (msg.wParam == VK_ESCAPE) {
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            }
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam)
{
    static int px;
    static int py;
    static HBITMAP bhbm;
    static RECT nRect = { 0, 0, wW, wH };

    switch (msg)
    {
    case WM_CREATE:
    {
        HWND bb = CreateWindowW(L"STATIC", L"",
            WS_VISIBLE | WS_CHILD ,
            0, 0, wW, wH,
            hwnd, (HMENU)11, hInst, NULL);
        //SetTimer(hwnd, 1, 80, NULL);

        return 0;
    }
    case WM_PAINT: {
        //Vars
        RECT wRect;
        if (GetUpdateRect(hwnd, &wRect, FALSE) == 0) {
            return 0; //Nothing to paint
        }
        PAINTSTRUCT gps;
        PAINTSTRUCT ps;
        BeginPaint(hwnd, &gps);
        HWND bb = GetDlgItem(hwnd, 11);
        HDC bbhdc = BeginPaint(bb, &ps);
        HDC mdc = CreateCompatibleDC(bbhdc);

        //Load Image
        BITMAP pBM;
        HBITMAP pHBM = (HBITMAP)LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
        HBITMAP pMBM = CreateBitmapMask((HBITMAP)pHBM, 0x00000000);
        GetObject(pHBM, sizeof(pBM), &pBM);

        //Paint
        HBITMAP oldBM = (HBITMAP)SelectObject(mdc, pMBM);
        BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCAND);
        SelectObject(mdc, pHBM);
        BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCPAINT);

        //Cleanup
        SelectObject(mdc, oldBM);
        DeleteObject(pHBM);
        DeleteObject(pMBM);
        DeleteDC(mdc);
        EndPaint(bb, &ps);
        EndPaint(hwnd, &gps);
        return 1;
    }
    case WM_ERASEBKGND: {
        return 0;
    }
    case WM_DESTROY:
    {
        DeleteObject(bhbm);
        PostQuitMessage(0);
        return 0;
    }
    case WM_LBUTTONDOWN:
        SetCapture(hwnd);
        px = LOWORD(lParam);
        py = HIWORD(lParam);
        return 1;
    case WM_LBUTTONUP:
    {
        ReleaseCapture();
        return 1;
    }
    case WM_MOUSEMOVE:
    {
        if (GetCapture() == hwnd)
        {
            RECT rcWindow;
            GetWindowRect(hwnd, &rcWindow);
            SetWindowPos(hwnd, NULL, rcWindow.left + LOWORD(lParam) - px, rcWindow.top + HIWORD(lParam) - py, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
        break;
    }
    }
    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

使用任何带有黑色边框的通用bmp都可以,我使用了这个:

pony

现在的问题是,如何在移动窗口(单击/拖动)背景更新时进行设置?我希望有一些东西,比如把位图放到一个透明的窗口中,以便它叠加在事物的顶部,但它似乎只是抓住它背后的像素。

如果可能,我试图在没有GDI +或其他库的情况下尝试这样做。

1 个答案:

答案 0 :(得分:3)

CreateWindow()不接受扩展窗口样式,例如WS_EX_TRANSPARENT(这就是其名称中包含EX的原因)。您必须改为使用CreateWindowEx()

hwnd = CreateWindowExW(WS_EX_TRANSPARENT,
    wc.lpszClassName, L"",
    WS_VISIBLE | WS_POPUP,
    100, 100, wW, wH, NULL, NULL, hInst, NULL);

更好的选择是使用WS_EX_LAYERED扩展样式创建layered window(另请参阅this)。然后,您可以使用UpdateLayeredWindow()函数为窗口提供位图和透明颜色(您也可以指定alpha)。让窗口管理为您透明地绘制位图的所有艰苦工作。

您的WndProc()也可以回复WM_NCHITTEST消息,告诉操作系统应该将窗口上的所有点击视为用户点击窗口的标题栏。让窗口为您处理鼠标跟踪和自动定位。

尝试更像这样的事情:

#include <windows.h>

HINSTANCE hInst;
static int wH = 156;
static int wW = 166;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    hInst = hInstance;

    WNDCLASSW wc = {0};    
    wc.lpszClassName = L"nope";
    wc.hInstance = hInst;
    wc.lpfnWndProc = WndProc;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassW(&wc);
    HWND hwnd = CreateWindowEx(WS_EX_LAYERED,
        wc.lpszClassName, L"",
        WS_POPUP, 100, 100, wW, wH, NULL, NULL,
        hInst, NULL);

    HBITMAP hBmp = (HBITMAP) LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
    HDC hdcScreen = GetDC(0);
    HDC hdcBmp = CreateCompatibleDC(hdcScreen);
    HBITMAP oldBM = (HBITMAP) SelectObject(hdcBmp, hBmp);

    POINT pt = {0};
    UpdateLayeredWindow(hwnd,
        hdcScreen,
        NULL, NULL,
        hdcBmp, &pt,
        RGB(0, 0, 0), // black
        NULL, ULW_COLORKEY
    );

    SelectObject(hdcBmp, oldBM);
    DeleteDC(hdcBmp);
    ReleaseDC(0, hdcScreen);
    DeleteObject(hBmp);

    ShowWindow(hwnd, nCmdShow);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        //Workaround for focusables stealing my Esc key
        if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) {
            SendMessage(hwnd, WM_CLOSE, 0, 0);
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam)
{    
    switch (msg)
    {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }

        case WM_NCHITTEST:
        {
            return HTCAPTION;
        }
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}