c++ basic GDI bitmap not working

VooDooChicken 40 Reputation points
2024-10-02T04:43:50.6+00:00

hi, I want to draw a bitmap, as simple as that. From the beginning, the first step was to create a basic desktop app

Since the app Visual Studio creates does not include a Window class definition, I declared the bitmap as a global variable

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name


// -- edit
Bitmap* bmap;

even though current GDI documentation says Bitmap is declared in gdiplusheaders.h, if I place

#include <gdiplusheaders.h>

it will show a lot of errors. What finally worked by copying from an example was

#include "framework.h"
#include "WindowsProject1.h"

// -- edit
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")

To initialize the bitmap, in the mWinMain function, before the message loop I placed this:

 // -- edit
 try {
     bmap = new Bitmap(L"test.png");
     //if (bmap->)
 }
 catch (int _exc) {
     return FALSE;
 }

I could not find any information on how to check if the bitmap was loaded correctly or not. The constructor does not throw an exception, and there was no difference whether I copied test.png in the source folder or not. I also could not find a property or a flag showing if the bitmap was able to load correctly in the bitmap object

I copied some lines from the GetPixel example, that is supposed to change pixels in a checker pattern, I was not able to see the patter either, so the bitmap may not render at all

To paint the bitmap, in the paint event I placed this:

case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        
        // TODO: Add any drawing code that uses hdc here...

        Graphics _gr(hWnd);
        _gr.DrawImage(bmap, 0, 0);


        EndPaint(hWnd, &ps);
    }
    break;

That's all. If there is a test.png file in the same folder as the program is supposed to create a bitmap from that file and render it, nothing complicated, but is not doing it. The window does not show any bitmap. I placed the file both in the same source folder and in the binary output folder, I tried to run it both ways and it does not show any bitmap. Any pointers?

Thanks

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,610 questions
0 comments No comments
{count} votes

Accepted answer
  1. RLWA32 45,396 Reputation points
    2024-10-02T06:27:47.6666667+00:00
    1 person found this answer helpful.
    0 comments No comments

3 additional answers

Sort by: Most helpful
  1. Castorix31 84,956 Reputation points
    2024-10-02T06:38:15.85+00:00

    You must initialize GdiPlus and you can test GetLastStatus

    Basic sample :

    #include <windows.h>
    #include <tchar.h>
    
    #include <gdiplus.h>
    using namespace Gdiplus;
    #pragma comment(lib, "gdiplus.lib")
    
    #pragma comment(linker,"\"/manifestdependency:type='win32' \
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
    
    HINSTANCE hInst;
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    int nWidth = 600, nHeight = 400;
    Gdiplus::Bitmap* bitmap = NULL;
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
    {
    	 GdiplusStartupInput gdiplusStartupInput;
    	 ULONG_PTR gdiplusToken;
    	 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
    	hInst = hInstance;
    	WNDCLASSEX wcex =
    	{
    		sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
    		LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, TEXT("WindowClass"), NULL,
    	};
    	if (!RegisterClassEx(&wcex))
    		return MessageBox(NULL, TEXT("Cannot register class !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    	int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
    	HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
    	if (!hWnd)
    		return MessageBox(NULL, TEXT("Cannot create window !"), TEXT("Error"), MB_ICONERROR | MB_OK);
    	ShowWindow(hWnd, SW_SHOWNORMAL);
    	UpdateWindow(hWnd);
    	MSG msg;
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	GdiplusShutdown(gdiplusToken);
    	return (int)msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	static HWND hWndButton = NULL, hWndStatic = NULL;
    	int wmId, wmEvent;
    	switch (message)
    	{
    	case WM_CREATE:
    	{
    		bitmap = new Gdiplus::Bitmap(L"Hulk.png");
    		if (bitmap->GetLastStatus() != Ok)
    		{
    			delete bitmap;
    			bitmap = NULL;
    		}
    		return 0;
    	}
    	break;
    	case WM_PAINT:
    	{
    		PAINTSTRUCT ps;
    		HDC hDC = BeginPaint(hWnd, &ps);
    		if (bitmap != NULL)
    		{			
    			Graphics graphics(hDC);
    			graphics.DrawImage(bitmap, 0, 0);
    		}
    		EndPaint(hWnd, &ps);
    	}
    	break;
    	case WM_DESTROY:
    	{
    		if (bitmap != NULL)
    		{
    			delete bitmap;
    			bitmap = NULL;
    		}
    		PostQuitMessage(0);
    		return 0;
    	}
    	break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    
    
    1 person found this answer helpful.
    0 comments No comments

  2. VooDooChicken 40 Reputation points
    2024-10-03T03:12:48.74+00:00

    thank you both, it seems it had to do with not initializing GDI.

    Initially I changed my wWinMain (for default app created by wizard) for this, it still did not work not sure why:

    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPWSTR    lpCmdLine,
        _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
    
        // TODO: Place code here.
    
        // Initialize global strings
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_WINDOWSPROJECT1, szWindowClass, MAX_LOADSTRING);
        MyRegisterClass(hInstance);
    
        // Perform application initialization:
        if (!InitInstance(hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        // -- edit
        try {
            if (GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
                return FALSE;
            bmap = new Bitmap(L"test.png");         //if (bmap->)
        }
        catch (int _exc) {
            return FALSE;
        }
    
        HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT1));
    
        MSG msg;
    
        // Main message loop:
        while (GetMessage(&msg, nullptr, 0, 0))
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    
        // -- edit
        GdiplusShutdown(gdiplusToken);
    
        return (int)msg.wParam;
    }
    
    

    but then I copied/pasted the example that shows how to start GDI in desktop app, I commented the first line which was giving error and did not add any value

    //#include <stdafx.h>

    and I edited the on paint handler (is ugly but is a test, is not supposed to look pretty)

    VOID OnPaint(HDC hdc)
    {
        Graphics graphics(hdc);
        Pen      pen(Color(255, 0, 0, 255));
        graphics.DrawLine(&pen, 0, 0, 200, 100);
        // --
        Bitmap* bmap;
        bmap = new Bitmap(L"test.png");
        graphics.DrawImage(bmap, 0, 0);
    }
    

    and now it works, it does show the bitmap. Thank you


  3. Tong Xu - MSFT 2,456 Reputation points Microsoft Vendor
    2024-10-03T03:17:12.8333333+00:00

    You are missing a call to GdiplusStartup. It is necessary.

    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    

    No need to be so complicated. Image::FromFile from gdiplus will be easier. https://stackoverflow.com/questions/78143741/winapi-32-and-gdi-move-window-when-draggin-a-png-image-with-some-region-with-tr

    Image* image; // Declare the image pointer globally
    int imageWidth = 0;
    int imageHeight = 0;
    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();
    ...
    ...
        }
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.