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

77 Views Asked by At

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

2

There are 2 best solutions below

0
user23481463 On BEST ANSWER

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

0
Torrecto - MSFT On

For this, sending WM_NCLBUTTONDOWN message can solve them all. WM_NCLBUTTONDOWN can allow you drag the image on client area.

   case WM_LBUTTONDOWN:  
    {
        x = LOWORD(lParam);
        y = HIWORD(lParam);
        if (x < imageWidth && y < imageHeight)
        {
            SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
        }
        break;
    }

You can drag the image as following the position of mouse correctly. And it will not leave any trasparent area . If the area is empty, just show the backgorund of window.

This is full code:

#include <windows.h>
#include <gdiplus.h>
#include<windowsx.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 ;
int imageHeight ;
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\\Administrator\\Desktop\\bp.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,
        500,
        150,
        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:
    {
        int x = LOWORD(lParam);
        int y = HIWORD(lParam);   
        if (x < imageWidth && y < imageHeight)
        {
            SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
        }
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}