Search code examples
c++winapidraggablegdi+

WinAPI 32 and GDI+ move window when draggin a PNG image with some region with trasparency


I wrote a program that creates a window with the WS_POPUP attribute, in order to load a PNG image for background with transparent part,

first of all in the code contained in case WM_MOUSEMOVE: I use the SetWindowPos function to move the window, but this function moves the window to the top left corner, this bothers me a bit and I would like the image to follow the mouse correctly.

then, the PNG image have a trasparent part that show correcly, but it's as if it took what's under the window and copied it into the window itself.

Anyway this is the code i wrote.

#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;

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

Image* image; // Declare the image pointer globally
int imageWidth = 0;
int imageHeight = 0;
bool isDragging = false;
POINT dragStartPos;
POINT prevMousePos;
POINT windowStartPos;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Load the PNG image
    image = Image::FromFile(L"C:\\Users\\hacktooth\\Downloads\\bg.png"); // Replace with your image path
    if (image) {
        imageWidth = image->GetWidth();
        imageHeight = image->GetHeight();
    }

    // Create the window class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = NULL; //(HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = L"WindowClass";
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

    // Register the window class
    RegisterClassEx(&wcex);

    // Create the application window
    HWND hWnd = CreateWindow(
        L"WindowClass",
        L"PNG Image",
        WS_POPUP,
        NULL,
        NULL,
        imageWidth, // Set the window width to image width
        imageHeight, // Set the window height to image height
        NULL,
        NULL,
        hInstance,
        NULL
    );

    // Display the window
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

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

    // Clean up GDI+
    if (image) {
        delete image;
    }
    GdiplusShutdown(gdiplusToken);

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    PAINTSTRUCT ps;
    HDC hdc;
    Graphics* graphics; // Declare the Graphics pointer
    POINT offset{}; //mouse coord offsets

    switch (message) {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        // Draw the image
        graphics = new Graphics(hdc);
        graphics -> DrawImage(image, 0, 0, imageWidth, imageHeight); // Scale image to fit window
        
        delete graphics; // Cleanup the Graphics object

        EndPaint(hWnd, &ps);
        break;

    case WM_LBUTTONDOWN:
        isDragging = true;

        offset.x = (int)(short)LOWORD(lParam); 
        offset.y = (int)(short)HIWORD(lParam);

        SetCapture(hWnd);
        break;

    case WM_LBUTTONUP:
        ReleaseCapture();
        isDragging = false;
        break;

    case WM_MOUSEMOVE:
        if (isDragging)
        {
            RECT mainWindowRect;
            POINT pos;
            short windowWidth, windowHeight;

            pos.x = (int)(short)LOWORD(lParam);
            pos.y = (int)(short)HIWORD(lParam);

            GetWindowRect(hWnd, &mainWindowRect);
            windowHeight = mainWindowRect.bottom - mainWindowRect.top;
            windowWidth = mainWindowRect.right - mainWindowRect.left;

            ClientToScreen(hWnd, &pos);            
            //MoveWindow(hWnd, pos.x, pos.y, windowWidth, windowHeight, TRUE);
            SetWindowPos(hWnd, NULL, pos.x - offset.x, pos.y - offset.y, windowWidth, windowHeight, 0);
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

VIDEO : text

I try with MOVEWINDOW function : //MoveWindow(hWnd, pos.x, pos.y, windowWidth, windowHeight, TRUE); same result... I try with a second mouse coord setted in WM_LBUTTONDOWN same result


Solution

  • I want to share how to enable trasparency on a windows with PNG image using WINAPI and GDI+

    First of all we need to set a Extended control in CreateWindowsEx with the first attribute defined with WS_EX_LAYERED

    // Create the application window
    CreateWindowEx(
        WS_EX_LAYERED,
        L"WindowClass",
        L"CAPTION",
        WS_POPUP,
        500, //x
        500, //y
        Width, 
        Height, 
        NULL,
        NULL,
        hInstance,
        NULL
    );
    

    Now very important is to set a background color on your window class remember to create window class before CreateWindowEx function example:

        // Create the window class
        WNDCLASSEX wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = hInstance;
        wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    --->    wcex.hbrBackground = CreateSolidBrush(RGB(1, 1, 1)); 
        wcex.lpszMenuName = NULL;
        wcex.lpszClassName = L"WindowClass";
        wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
    

    Finally under CreateWindowEx function you must use SetLayeredWindowAttributes()

    BOOL SetLayeredWindowAttributes(
      [in] HWND     hwnd,
      [in] COLORREF crKey,
      [in] BYTE     bAlpha,
      [in] DWORD    dwFlags
    );
    

    This function allow to set a trasparency using KEY COLOR "like a green screen", set opacity at your window or blend opacity + key color, than you must declare the color you use as key using COLORREF* To generate a COLORREF, we use the RGB macro.

    COLOR KEY

        COLORREF transparentColor = RGB(1, 1, 1);
        SetLayeredWindowAttributes(hWnd, transparentColor, NULL, LWA_COLORKEY);
    

    SET OPACITY

        COLORREF transparentColor = RGB(1, 1, 1);
        SetLayeredWindowAttributes(
        hWnd,
        transparentColor, 
        50, //OPACITY AMOUNT
        LWA_ALPHA);
    
    

    OPACITY + KEY COLOR

        COLORREF transparentColor = RGB(1, 1, 1);
        SetLayeredWindowAttributes(
        hWnd,
        transparentColor, //RGB STRUCTURE FOR COLOR KEY
        50, //OPACITY VALUE
        LWA_ALPHA | LWA_COLORKEY //BLEND OPACITY + COLOR KEY
        );
    
    

    More info at Microsoft Documentation

    VIDEO