3

Hello and good day to everyone,

my final target is to draw a PNG file including alpha onto the screen - that means not into an own window but just somewhere on the desktop. The part to load PNG's into a HBITMAP works now (tested that in a diffrent way) but I don't manage to draw it including alpha.

As far as I've heard the best way to do this would be using alyered windows. So I wroked a lot to redo a couple of examples and tiny tutorials.

The following code compiles without problems and there do not prompt any messages (that means the showError("#") function is never called).

Yet there is nothing visible on the screen :/

Sorry that it is so long... Hope someone would like to look at it at least quickly..

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

By the way, if I use ULW_OPAQUE instead of ULW_ALPHA in UpdateLayeredWindow, then a right sized, black window appears so think the issue has to be something minimal related to the PAINTSTRUKT or BitBlt function.. Yet I tried a lot of ways without any change.

Hope someone can help. Thank you very much in advance!

arx
  • 16,686
  • 2
  • 44
  • 61
DragonGamer
  • 834
  • 3
  • 9
  • 27

1 Answers1

5

This is mostly wrong. Your code should:

  • Create the layered window with CreateWindowEx.
  • Attach the bitmap to it with UpdateLayeredWindow.
  • Show the window with ShowWindow. Windows will take care of painting the layered window, so you don't need to handle WM_PAINT or call BeginPaint.
  • Enter a message loop.

And that's it.

If you're using Visual Studio, create a new Win32 Project and it will create a new project with a message loop for you.

Update

Here's a sample program that creates a transparent layered window. It needs a function to load a PNG as a transparent bitmap. And it has no error checking.

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

Another Update

Here's some code to premultiply the red, green and blue values by alpha. It assumes that splash_image points to 32bpp ARGB data of size width*height.

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;
}
arx
  • 16,686
  • 2
  • 44
  • 61
  • 1
    Thank you very much for your patiece! I fear though that it's not really helping me. I've tried really a lot of ways with and without the loop and in diffrent orders.. without any sucess. Especially the attaching part is hard for me to get. Is this the right way? HDC hdcMem = CreateCompatibleDC(GetDC(screen)); HGDIOBJ oldBitmap = SelectObject(hdcMem, img); UpdateLayeredWindow(screen,NULL,&ptPos,&sizeWnd,hdcMem,&ptPos2,RGB(255,255,255),&blend,ULW_ALPHA); Where could I read more exactly about this? Also, notice please that I'm writing a DLL therefore I don't really need a message loop.. – DragonGamer Sep 28 '12 at 17:39
  • 1
    Agreed, if you're in a DLL you can dispense with the message loop, but you definitely don't want the infinite loop you've got. I've updated my answer with a complete program, except for code to load the PNG. – arx Sep 28 '12 at 18:53
  • 1
    Thank you very, very much again! Think I udnerstood now and it works well! Well.. almost. There is an awkward issue. Even completly opaque seem to be displayed transparent (bf.SourceConstantAlpha is at 255).. or rather blended with the background. On a dark background it looks like it should. On a white background the colors almost disappear. What could be the issue? It's not the loading script because I've also tried the built in Function by writing HBITMAP hbmp = (HBITMAP)LoadImage(...) to load a simple BMP file - with the same result --> the image is transparent on bright backgrounds... – DragonGamer Sep 28 '12 at 23:35
  • 1
    GDI mostly doesn't understand transparency. If you load an opaque bitmap with LoadImage, it doesn't necessarily set the transparency correctly (to opaque) because GDI assumes that you'll be using the image with functions that don't care about transparency. This code is working fine for me, suggesting that the problem lies in either your source PNG or your function to load it. Note that UpdateLayeredWindow requires a bitmap with premultiplied alpha. – arx Sep 29 '12 at 07:44
  • 1
    I thought at that possibility already and now I've tried a rpemultiplied PNG_file (I've a tool for that) and now the colors seem to be more right. But only on _black_ background! The brighter the background is, the more transparent the picture appears to be! What could cause that? It doesn't sound like the HBITMAP itself would be loaded wrongly... Does this not happen on your computer? – DragonGamer Sep 29 '12 at 11:28
  • It works fine for me. The only thing I can think of that would cause your bitmap to behave inconsistently on different coloured backgrounds is incorrect pre-multiplied alpha. It's not something you should be doing do the PNG. It's something you need to do after the PNG is loaded. – arx Sep 29 '12 at 22:41
  • Oh okey. I have had to do with rpemultiplikation in a script langauge environment and tehre it suficed to premultiply the filed before loading them.. However, could you give me a hint how to find a code for doing this? Apperently it's a pretty unusual task to premultiply a hbitmap because there is not much information about :/ – DragonGamer Oct 01 '12 at 17:34
  • Sorry, pressed enter accidentally before nad could not edit anymore >< I've found only one free code: http://pastie.org/4892352 But it brings up adress violation errors unfortunately (no custem or compilinge rrors though) :/ Could the reason the loading of the HBITMAP be the issue? Again thank you for your patiece! ._. – DragonGamer Oct 01 '12 at 17:41
  • I've added some code to do this. You can insert it into your bitmap loader after `splash_image` is initialised. – arx Oct 01 '12 at 18:34
  • Slowly but sure I'm about to give up this damn thing... >< Thank you again so much though. Your code worked without errors and it apperently enables diffrent alpha values now (the result looks NOW how it should, on black background. I said wrong before). Yet this blending effect is still there! On white background, any image is completly invisible... Should I open a new thread for that certain problem here? – DragonGamer Oct 02 '12 at 18:17
  • Have you tried a different PNG? e.g. Take any random opaque bitmap, use mspaint to save it as a PNG and use that. If that doesn't tell you anything useful then, yes, you might as well start a new question. – arx Oct 02 '12 at 19:40
  • Ohh thank you for that idea! Well, now I'm completly confused, but happy xD Lol. The statistics: A png saved in with Paint Shop Pro 7 (yes, very old, but usueful) causes this awkward blending effect. A png saved with Paint (the one of Win7) does not seem to load right. The image is filed with crap, yet there is no blending effect! Then I tried a png saved from Game Maker's sprite editor (GM is the tool Im making this DLL for, to enable threaded drawing to it). And then it works perfectly! O-o In other words: there are min three dif. ways of saving a png.. Ftw >< At least its solved. Thank you! – DragonGamer Oct 02 '12 at 22:12