Updated Aug 3, 2021

Win32 - Drawing Pixels (C/C++)

How to draw pixels using Windows API.

Code (main.cpp)

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <windows.h>
#include <stdint.h>

typedef uint32_t u32;

void* memory;
int client_width;
int client_height;

void draw_pixel(int x, int y, u32 color)
{
    u32 *pixel = (u32 *)memory;
    pixel += y * client_width + x;
    *pixel = color;
}

void clear_screen(u32 color)
{
    u32 *pixel = (u32 *)memory;
    for(int i = 0;
        i < client_width * client_height;
        ++i)
    {
        *pixel++ = color;
    }
}

LRESULT CALLBACK 
window_proc(HWND window, 
            UINT message, 
            WPARAM w_param, 
            LPARAM l_param)
{
    switch(message)
    {
        case WM_KEYDOWN:
        {
            switch(w_param)
            {
                // "o" exits the program
                case 'O':
                {
                    DestroyWindow(window);
                };
            }
        } break;
        
        case WM_DESTROY:
        {
            PostQuitMessage(0);
        } break;
        
        default:
        {
            return DefWindowProc(window, 
                                 message, 
                                 w_param, 
                                 l_param);
        }
    }
    
    return 0;
}

int WINAPI 
wWinMain(HINSTANCE instance, 
         HINSTANCE prev_instance, 
         PWSTR cmd_line, 
         int cmd_show)
{
    // window creation
    
    WNDCLASS window_class = {};
    
    const wchar_t class_name[] = L"MyWindowClass";
    
    window_class.lpfnWndProc = window_proc;
    window_class.hInstance = instance;
    window_class.lpszClassName = class_name;
    window_class.hCursor = LoadCursor(0, IDC_CROSS);
    
    if(!RegisterClass(&window_class))
    {
        MessageBox(0, L"RegisterClass failed", 0, 0);
        return GetLastError();
    }
    
    HWND window = CreateWindowEx(0,
                                 class_name,
                                 L"Window",
                                 WS_OVERLAPPEDWINDOW|WS_VISIBLE,
                                 CW_USEDEFAULT,
                                 CW_USEDEFAULT,
                                 CW_USEDEFAULT,
                                 CW_USEDEFAULT,
                                 0,
                                 0,
                                 instance,
                                 0);
    
    if(!window)
    {
        MessageBox(0, L"CreateWindowEx failed", 0, 0);
        return GetLastError();
    }
    
    // allocate memory
    
    RECT rect;
    GetClientRect(window, &rect);
    client_width = rect.right - rect.left;
    client_height = rect.bottom - rect.top;
    
    memory = VirtualAlloc(0,
                          client_width * client_height * 4,
                          MEM_RESERVE|MEM_COMMIT,
                          PAGE_READWRITE
                          );
    
    // create BITMAPINFO struct for StretchDIBits
    
    BITMAPINFO bitmap_info;
    bitmap_info.bmiHeader.biSize = sizeof(bitmap_info.bmiHeader);
    bitmap_info.bmiHeader.biWidth = client_width;
    bitmap_info.bmiHeader.biHeight = client_height;
    bitmap_info.bmiHeader.biPlanes = 1;
    bitmap_info.bmiHeader.biBitCount = 32;
    bitmap_info.bmiHeader.biCompression = BI_RGB;
    
    HDC hdc = GetDC(window);
    
    // loop
    
    bool running = true;
    
    while(running)
    {
        MSG msg;
        while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT) running = false;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        
        // clear screen with gray color
        clear_screen(0x333333);
        // draw white pixel at 100, 100 (from bottom left)
        draw_pixel(100, 100, 0xffffff);
        
        StretchDIBits(hdc,
                      0,
                      0,
                      client_width,
                      client_height,
                      0,
                      0,
                      client_width,
                      client_height,
                      memory,
                      &bitmap_info,
                      DIB_RGB_COLORS,
                      SRCCOPY
                      );
    }
    
    return 0;
}

Build script (build.bat)

@echo off

set COMMON_COMPILER_FLAGS=/W4 /wd4100 /Oi /nologo /Fegame.exe

set DEBUG_FLAGS=/Zi /MTd
set RELEASE_FLAGS=/O2

set COMPILER_FLAGS=%COMMON_COMPILER_FLAGS% %DEBUG_FLAGS%
REM set COMPILER_FLAGS=%COMMON_COMPILER_FLAGS% %RELEASE_FLAGS%

set LINKER_FLAGS=/INCREMENTAL:NO /OPT:REF
set LIBS=user32.lib gdi32.lib

cl %COMPILER_FLAGS% main.cpp /link %LINKER_FLAGS% %LIBS%