6

I am trying to overlay an image on top of my screen with GDI+ and C++. I have successfully drawn an image to a non-transparent window, but I only want the background of my window to be transparent, not the entire window.

Here is my code:

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

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM param, LPARAM lparam);
void draw(HDC hdc);

int WINAPI WinMain(HINSTANCE currentInstance, HINSTANCE previousInstance, PSTR cmdLine, INT cmdCount) {
    // Initialize GDI+
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    // Register the window class
    const char* CLASS_NAME = "myWin32WindowClass";
    WNDCLASS wc{};
    wc.hInstance = currentInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpfnWndProc = WindowProcessMessages;
    RegisterClass(&wc);

    // Create the window
    //CreateWindow(CLASS_NAME, "Drawing Image",
    //  WS_POPUP | WS_VISIBLE,            // Window style
    //  CW_USEDEFAULT, CW_USEDEFAULT,                // Window initial position
    //  800, 600,                                    // Window size
    //  nullptr, nullptr, nullptr, nullptr);

    CreateWindowEx(WS_EX_LAYERED,  // Extended window style
        CLASS_NAME, "Drawing Image",
        WS_POPUP | WS_VISIBLE,     // Window style
        CW_USEDEFAULT, CW_USEDEFAULT,         // Window position
        800, 600,                             // Window size
        nullptr, nullptr, currentInstance,
        nullptr);

    // Window loop
    MSG msg{};
    while (GetMessage(&msg, nullptr, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;

}

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM param, LPARAM lparam) {
    HDC hdc;
    PAINTSTRUCT ps;
    SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_COLORKEY | LWA_ALPHA);

    switch (msg) {
    case WM_PAINT:
        SetLayeredWindowAttributes(hwnd, 0, 128, LWA_ALPHA);
        hdc = BeginPaint(hwnd, &ps);
        draw(hdc);
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hwnd, msg, param, lparam);
    }

}

void draw(HDC hdc) {
    // MessageBox(nullptr, "Draw function called", "Debug", MB_OK);
    Gdiplus::Graphics gf(hdc);

    Gdiplus::Bitmap bmp(L"image.png");
    gf.DrawImage(&bmp, 430, 10);
}

As far as I can tell, the SetLayeredWindowAttributes of the window alpha affects the entire window.

joeymalvinni
  • 343
  • 9

1 Answers1

2

Two years ago, I wrote an answer to a similar question.

It is also based on Koro's idea.

I modified the previous code sample to show the window effect you want. (only for test)

#include <ObjIdl.h>
#include <Windows.h>
#include <gdiplus.h>
#include <gdiplusheaders.h>

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

#define MAX_WIDTH 800
#define MAX_HEIGHT 600

using namespace std;

void DrawImg(HWND hwnd, HDC hdc);

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,
                         LPARAM lParam) {
  if (message == WM_DESTROY) {
    PostQuitMessage(0);
  }
  return DefWindowProc(hwnd, message, wParam, lParam);
};

HINSTANCE hinst;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, PSTR szCmdLine,
                   int iCmdShow) {
  HWND hWnd;
  GdiplusStartupInput gdiplusStartupInput;
  ULONG_PTR gdiplusToken;

  // Initialize GDI+
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
  hinst = GetModuleHandle(NULL);
  // create a window class:
  WNDCLASS wc = {};
  wc.lpfnWndProc = WndProc;
  wc.hInstance = hinst;
  wc.lpszClassName = L"win32";

  // register class with operating system:
  RegisterClass(&wc);

  // create and show window:
  hWnd = CreateWindowExW(WS_EX_LAYERED | WS_EX_TOPMOST, L"win32", L"WinSoup",
                         WS_POPUP, 0, 0, 1000, 500, nullptr, nullptr, hInstance,
                         nullptr);

  if (hWnd == NULL) {
    return 0;
  }

  DrawImg(hWnd, GetDC(hWnd));

  ShowWindow(hWnd, SW_SHOW);

  MSG msg = {};

  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

void DrawImg(HWND hwnd, HDC hdc) {
  Bitmap softwareBitmap(MAX_WIDTH, MAX_HEIGHT, PixelFormat32bppARGB);
  Graphics g(&softwareBitmap);

  g.Clear(Gdiplus::Color(20, 0, 0, 0));  // 20: alpha value

  Gdiplus::Bitmap bmp2(L"image.png");
  g.DrawImage(&bmp2, 430, 10);

  HBITMAP bmp;
  softwareBitmap.GetHBITMAP(Color(0, 0, 0, 0), &bmp);

  HDC memdc = CreateCompatibleDC(hdc);
  HGDIOBJ original = SelectObject(memdc, bmp);

  BLENDFUNCTION blend = {0};
  blend.BlendOp = AC_SRC_OVER;
  blend.SourceConstantAlpha = 255;
  blend.AlphaFormat = AC_SRC_ALPHA;
  POINT ptLocation = {200, 300};
  SIZE szWnd = {MAX_WIDTH, MAX_HEIGHT};
  POINT ptSrc = {0, 0};
  UpdateLayeredWindow(hwnd, hdc, &ptLocation, &szWnd, memdc, &ptSrc, 0, &blend,
                      ULW_ALPHA);
  SelectObject(hdc, original);

  DeleteObject(bmp);
  DeleteObject(memdc);
}

enter image description here

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • 1
    Thank you so much! Asides from adding the `WS_EX_TRANSPARENT` window style to make the window click-through, it worked perfectly. – joeymalvinni Dec 22 '22 at 23:18