将 HBITMAP 绘制到分层窗口上。怎么了?

Posted

技术标签:

【中文标题】将 HBITMAP 绘制到分层窗口上。怎么了?【英文标题】:Draw HBITMAP onto layered window. What's wrong? 【发布时间】:2012-09-27 17:14:24 【问题描述】:

大家好,大家好,

我的最终目标是在屏幕上绘制一个包含 alpha 的 PNG 文件 - 这意味着不是在自己的窗口中,而是在桌面上的某个地方。 将 PNG 加载到 HBITMAP 中的部分现在可以工作(以不同的方式进行测试),但我无法将它包括 alpha 来绘制。

据我所知,最好的方法是使用 alyered windows。所以我做了很多工作来重做几个例子和小教程。

以下代码编译没有问题,也没有提示任何消息(这意味着永远不会调用 showError("#") 函数)。

然而屏幕上没有任何东西可见:/

抱歉这么长...希望有人愿意至少快点看一下..

LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam);


int main(HINSTANCE hInstance)



    WNDCLASSEX WndClass;
    char sClassName[]  = "mainClass";
    WndClass.cbSize     = sizeof(WNDCLASSEX);
    WndClass.style      = NULL;
    WndClass.lpfnWndProc   = WndProc;//WndProc;
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hInstance  = hInstance;
    WndClass.hIcon      = NULL;
    WndClass.hCursor    = LoadCursor(NULL, IDC_ARROW);
    WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    WndClass.lpszMenuName  = NULL;
    WndClass.lpszClassName = sClassName;
    WndClass.hIconSm    = LoadIcon(NULL, IDI_APPLICATION);
    if (RegisterClassEx(&WndClass) == 0) showError("-1");





    HWND screen = CreateWindowEx(WS_EX_LAYERED,//WS_EX_LEFT
        "mainClass",
        "UpdateLayeredWind",
        WS_DISABLED | WS_VISIBLE,
        200,200,260,260,
        NULL /*eventuelly, GM window*/,
        NULL,
        hInstance,
        NULL);  


    if (screen == NULL) showError("0");




        HBITMAP img = LoadImageResource("D://ThreadDraw/ThreadDraw-test/ThreadDraw/test.png");
            if (img == NULL) showError("1");






    BLENDFUNCTION blend = 0;

    blend.AlphaFormat = AC_SRC_ALPHA;
    blend.SourceConstantAlpha = 155;

    POINT ptPos = 200,300;
    SIZE sizeWnd = 260,260;
    POINT ptPos2 = 200,300;


    ShowWindow(screen, SW_SHOW);



    while (1)
    


        PAINTSTRUCT             ps;
        HDC                     hdc;
        BITMAP                  bitmap;
        HDC                     hdcMem;
        HGDIOBJ                 oldBitmap;

        hdc = BeginPaint(screen, &ps);

        hdcMem = CreateCompatibleDC(hdc);
        oldBitmap = SelectObject(hdcMem, img);

        GetObject(img, sizeof(bitmap), &bitmap);


        if (SetLayout(hdc,LAYOUT_RTL) == GDI_ERROR)
            showError("5");



            if (!BitBlt(hdc, 0, 0, 64, 64, hdcMem, 0, 0, SRCCOPY))
                showError("4");



            if (!UpdateLayeredWindow(screen,hdcMem,&ptPos,&sizeWnd,hdc,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA))//ULW_OPAQUE))
            showError("2");



        EndPaint(screen, &ps);

        SelectObject(hdcMem, oldBitmap);
        DeleteDC(hdcMem);


        Sleep(10);

    



    return 0;




LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 

    switch(Message) 
    
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    
    return 0;

顺便说一句,如果我在 UpdateLayeredWindow 中使用 ULW_OPAQUE 而不是 ULW_ALPHA,则会出现一个大小合适的黑色窗口,因此认为问题必须与 PAINTSTRUKT 或 BitBlt 函数相关。但我尝试了很多方法没有任何变化。

希望有人可以提供帮助。非常感谢您!

【问题讨论】:

【参考方案1】:

这大多是错误的。您的代码应该:

使用CreateWindowEx 创建分层窗口。 使用UpdateLayeredWindow 附加位图。 显示带有ShowWindow 的窗口。 Windows 将负责绘制分层窗口,因此您无需处理 WM_PAINT 或调用 BeginPaint。 进入消息循环。

就是这样。

如果您使用的是 Visual Studio,请创建一个新的 Win32 项目,它将为您创建一个带有消息循环的新项目。

更新

这是一个创建透明分层窗口的示例程序。它需要一个函数来将 PNG 作为透明位图加载。而且它没有错误检查。

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

    LPCTSTR szWindowClass = _T("TransparentClass");

    // Register class
    WNDCLASSEX wcex = 0;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc    = DefWindowProc;
    wcex.hInstance      = hInstance;
    wcex.lpszClassName  = szWindowClass;

    RegisterClassEx(&wcex);

    HWND hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, 0, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    int width;
    int height;
    HBITMAP hbmp = LoadPng(L"sample.png", &width, &height);

    HDC hdcScreen = GetDC(0);
    HDC hdc = CreateCompatibleDC(hdcScreen);
    ReleaseDC(0, hdcScreen);
    HBITMAP hbmpold = (HBITMAP)SelectObject(hdc, hbmp);

    POINT dcOffset = 0, 0;
    SIZE size = width, height;
    BLENDFUNCTION bf;
    bf.BlendOp = AC_SRC_OVER;
    bf.BlendFlags = 0;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;
    UpdateLayeredWindow(hWnd, 0, 0, &size, hdc, &dcOffset, 0, &bf, ULW_ALPHA);
    SelectObject(hdc, hbmpold);
    DeleteDC(hdc);
    DeleteObject(hbmp);

    ShowWindow(hWnd, SW_SHOW);

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    

    return (int)msg.wParam;

另一个更新

这里有一些代码将红色、绿色和蓝色值预乘以 alpha。它假定splash_image 指向大小为width*height 的32bpp ARGB 数据。

LPBYTE bits = (LPBYTE)splash_image;
int size = width * height;
for (int pixel = 0; pixel != size; ++pixel)

    bits[0] = bits[0] * bits[3] / 255;
    bits[1] = bits[1] * bits[3] / 255;
    bits[2] = bits[2] * bits[3] / 255;
    bits += 4;

【讨论】:

非常感谢您的耐心等待!我担心它并没有真正帮助我。我已经尝试了很多有和没有循环的方法以及不同的顺序..没有任何成功。尤其是附件部分对我来说很难得到。这是正确的方法吗? HDC hdcMem = CreateCompatibleDC(GetDC(screen)); HGDIOBJ oldBitmap = SelectObject(hdcMem, img); UpdateLayeredWindow(screen,NULL,&ptPos,&sizeWnd,hdcMem,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA);我在哪里可以更准确地了解这一点?另外,请注意我正在编写一个 DLL,因此我真的不需要消息循环.. 同意,如果您在 DLL 中,您可以省去消息循环,但您绝对不想要无限循环。我已经用一个完整的程序更新了我的答案,除了加载 PNG 的代码。 再次非常非常感谢您!我想我现在明白了,而且效果很好!嗯..几乎。有一个尴尬的问题。即使完全不透明似乎也显示为透明(bf.SourceConstantAlpha 为 255).. 或者更确切地说与背景混合。在黑暗的背景下,它看起来应该如此。在白色背景上,颜色几乎消失了。可能是什么问题?这不是加载脚本,因为我还尝试了内置函数,方法是编写 HBITMAP hbmp = (HBITMAP)LoadImage(...) 来加载一个简单的 BMP 文件——结果相同——>图像在明亮的背景下是透明的... GDI 大多不理解透明度。如果您使用 LoadImage 加载不透明位图,它不一定正确设置透明度(不透明),因为 GDI 假定您将使用不关心透明度的功能的图像。这段代码对我来说运行良好,表明问题出在您的源 PNG 或加载它的函数上。请注意,UpdateLayeredWindow 需要一个带有预乘 alpha 的位图。 我已经想到了这种可能性,现在我尝试了一个 rpemultiplied PNG_file(我有一个工具),现在颜色似乎更正确了。但仅限于 black 背景!背景越亮,图片越透明!是什么原因造成的?听起来 HBITMAP 本身不会被错误加载...这不会发生在您的计算机上吗?

以上是关于将 HBITMAP 绘制到分层窗口上。怎么了?的主要内容,如果未能解决你的问题,请参考以下文章

使用分层窗口时未绘制下方的窗口

用C++在Win32中用LoadImage()绘制HBITMAP的二维数组

如何使用 GDI 将 RGB 位图绘制到窗口?

如何通过 Bitmap::GetHBITMAP 将位图转换为带有 alpha 的 HBITMAP?

power bi怎么做散点图啊?如何利用power bi绘制散点图??

从像素缓冲区创建HBITMAP然后渲染