【问题标题】:Transparency in GDI DCsGDI DC 的透明度
【发布时间】:2015-05-04 22:55:25
【问题描述】:

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

#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 都可以,我使用了这个:

现在的问题是,如何才能在移动窗口(单击/拖动)时进行背景更新?我希望能够将位图放入一个透明窗口中,这样它就可以覆盖在事物之上,但它似乎只是抓住了它背后的像素。

如果可能,我正在尝试在没有 GDI+ 或其他库的情况下执行此操作。

【问题讨论】:

  • 这是一只非常合适的独角兽! +1(另外,似乎不是骗子。)
  • 使用UpdateLayeredWindow API。没有任何实际的选择。

标签: c++ winapi gdi


【解决方案1】:

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);
}

【讨论】:

    猜你喜欢
    • 2010-11-23
    • 2022-08-11
    • 2012-03-27
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-12-03
    • 2012-03-15
    • 1970-01-01
    相关资源
    最近更新 更多