Index Types & Handles Window & Input GDI System & Files File I/O Multimedia

GDI — Bitmaps & Device Contexts

Free API implements a minimal GDI subset sufficient for games that pre-render everything into off-screen bitmaps and blit them to the window each frame. Real GDI drawing primitives (lines, rectangles, text, brushes, pens) are not implemented.

What is NOT implemented TextOut, DrawText, LineTo, Rectangle, FillRect, PatBlt, BitBlt (use StretchBlt with equal src/dst size), real palette management, DIB sections, CreateDIBitmap, CreateDIBSection, regions, clipping, font objects.

How GDI is backed in Free API

LoadImageA("sprites.bmp")
    │   SDL_LoadBMP → SDL_ConvertSurface(RGBA32)
    ▼
HBITMAP  — wraps an SDL_Surface (RGBA32, heap-allocated)

CreateCompatibleDC(NULL)
    │   allocates an internal DC record
    ▼
HDC (memory)  — has a "selected bitmap" slot (initially empty)

SelectObject(hdcMem, hBmp)
    │   attaches the bitmap to the DC
    ▼
HDC (memory)  ← points to HBITMAP's SDL_Surface pixels

StretchBlt(hdcDst, ...)
    │   nearest-neighbour copy from src SDL_Surface → dst SDL_Surface
    ▼
SDL_RenderTexture  displayed on screen next frame
      

Loading bitmaps from disk

LoadImageA

Signature
HANDLE WINAPI LoadImageA(
    HINSTANCE hInst,   // ignored (pass NULL)
    LPCSTR    name,    // file path when LR_LOADFROMFILE is set
    UINT      type,    // must be IMAGE_BITMAP (0)
    int       cx,      // desired width (0 = use natural size)
    int       cy,      // desired height (0 = use natural size)
    UINT      fuLoad   // must include LR_LOADFROMFILE
);
// Returns HBITMAP cast to HANDLE, or NULL on error.
      

Only IMAGE_BITMAP + LR_LOADFROMFILE is implemented. Resource loading (without LR_LOADFROMFILE) always returns NULL. The file is loaded via SDL_LoadBMP and converted to RGBA32 internally. Paths are normalised (backslashes → slashes, drive letter removed) before the file is opened, so Windows-style paths work on Linux.

Flag constantValueMeaning
IMAGE_BITMAP0Required — load as bitmap.
LR_LOADFROMFILE0x0010Required — load from file path, not resource.
LR_CREATEDIBSECTION0x2000Accepted but ignored — Free API always creates an RGBA32 surface.
C++ — load a BMP file
// Load from a relative path (backslash or slash both work)
HBITMAP hBackground = (HBITMAP)LoadImageA(
    NULL,
    "data\\background.bmp",   // or "data/background.bmp"
    IMAGE_BITMAP,
    0, 0,                       // 0,0 = keep natural size
    LR_LOADFROMFILE);

if (!hBackground) {
    OutputDebugString("Failed to load background.bmp");
    return FALSE;
}

// Load and scale to a specific size
HBITMAP hIcon = (HBITMAP)LoadImageA(
    NULL, "data\\icon.bmp",
    IMAGE_BITMAP, 32, 32,
    LR_LOADFROMFILE);
      

CreateBitmap

Signature
HBITMAP WINAPI CreateBitmap(
    int         nWidth,   // bitmap width in pixels
    int         nHeight,  // bitmap height in pixels
    UINT        nPlanes,  // colour planes (1 for RGB)
    UINT        nBitCount,// bits per pixel (8, 16, 24, or 32)
    const void* lpBits    // initial pixel data, or NULL for blank
);
      

Creates an SDL3 RGBA32 surface. If lpBits is not NULL, raw pixel data is copied and converted from the specified bit-depth. A blank (all-zero) surface is created when lpBits is NULL. Supported nBitCount values: 8 (treated as 8-bit indexed, palette not applied), 16 (RGB565), 24 (RGB), 32 (RGBA).

C++ — create a blank bitmap for off-screen rendering
// Create a 320×200 blank canvas
HBITMAP hCanvas = CreateBitmap(320, 200, 1, 32, NULL);
HDC     hdcCanvas = CreateCompatibleDC(NULL);
HGDIOBJ hOld = SelectObject(hdcCanvas, hCanvas);

// ... draw into hdcCanvas via SetPixel, StretchBlt, etc. ...

// Cleanup
SelectObject(hdcCanvas, hOld);
DeleteDC(hdcCanvas);
DeleteObject(hCanvas);
      

Memory DCs — CreateCompatibleDC, SelectObject, DeleteDC

A device context (DC) is a drawing surface handle. In Free API, DCs come in two flavours: memory DCs (off-screen) and the window surface DC.

FunctionStatusDescription
CreateCompatibleDC(HDC hdc) PARTIAL Allocates a new memory DC. The hdc parameter is ignored (no real compatibility with an existing DC). The new DC has no bitmap selected until SelectObject is called.
SelectObject(HDC hdc, HGDIOBJ h) PARTIAL Selects a bitmap (HBITMAP) into a memory DC and returns the previously selected object. Only bitmaps are supported — brushes, pens, and fonts are not.
DeleteDC(HDC hdc) PARTIAL Releases the internal DC record. Does NOT free any bitmap that was selected into it — call DeleteObject(hBmp) separately.
DeleteObject(HGDIOBJ ho) PARTIAL Frees the underlying SDL3 surface. Do not call while the object is still selected in a DC. Returns TRUE on success.
C++ — correct DC / bitmap lifecycle
// ── Create ──
HBITMAP hBmp   = (HBITMAP)LoadImageA(NULL, "map.bmp",
                     IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC     hdcMem = CreateCompatibleDC(NULL);
HGDIOBJ hOld   = SelectObject(hdcMem, hBmp);   // select in

// ── Use ──
StretchBlt(hdcDest, 0, 0, 800, 600,
           hdcMem,  0, 0, 320, 200, SRCCOPY);

// ── Cleanup (always in this order) ──
SelectObject(hdcMem, hOld);  // restore previous selection first
DeleteDC(hdcMem);            // release DC
DeleteObject(hBmp);          // free surface
      

StretchBlt — copying and scaling bitmaps

Signature
BOOL WINAPI StretchBlt(
    HDC   hdcDest,            // destination DC
    int   xDest, int yDest,   // destination top-left
    int   wDest, int hDest,   // destination size
    HDC   hdcSrc,             // source DC (must have bitmap selected)
    int   xSrc,  int ySrc,    // source top-left
    int   wSrc,  int hSrc,    // source size
    DWORD rop                 // raster op — only SRCCOPY supported
);
      

Supported: SRCCOPY from a memory DC (with a bitmap selected) to the window surface DC, or between two memory DCs. Scaling uses nearest-neighbour interpolation.

Not supported: raster operations other than SRCCOPY, alpha-blending, colour-key transparency, mirroring (negative width/height), or blitting to DCs that have no bitmap selected.

C++ — common blit patterns
// 1. Copy a sprite (no scaling)
StretchBlt(
    hdcWindow, dstX, dstY, 64, 64,
    hdcSprites, spriteSheetX, spriteSheetY, 64, 64,
    SRCCOPY);

// 2. Scale up a 320×200 game canvas to fill an 800×600 window
StretchBlt(
    hdcWindow, 0, 0, 800, 600,
    hdcCanvas,  0, 0, 320, 200,
    SRCCOPY);

// 3. Copy a sub-region (source tile from a tileset)
int tileCol = 3, tileRow = 2;
int TILE = 32;
StretchBlt(
    hdcDest, screenX, screenY, TILE, TILE,
    hdcTiles, tileCol * TILE, tileRow * TILE, TILE, TILE,
    SRCCOPY);
      
SRCCOPY constant SRCCOPY is defined as 0x00CC0020. It is the only raster operation code that does anything in Free API. All others are silently accepted but fall back to a plain copy.

GetPixel / SetPixel & colour macros

Function / MacroStatusDescription
GetPixel(HDC hdc, int x, int y) PARTIAL Reads the RGB colour at pixel (x, y) from the selected SDL3 surface. Returns CLR_INVALID (0xFFFFFFFF) if out of bounds or no bitmap selected.
SetPixel(HDC hdc, int x, int y, COLORREF color) PARTIAL Writes an RGB colour to pixel (x, y) in the selected surface. Returns the colour actually written, or CLR_INVALID on error.
RGB(r, g, b) IMPLEMENTED Macro that packs three byte values into a COLORREF: (r) | (g << 8) | (b << 16).
GetRValue(col) / GetGValue(col) / GetBValue(col) IMPLEMENTED Extract the red, green, or blue component from a COLORREF.
CLR_INVALID HEADER_ONLY 0xFFFFFFFF — sentinel returned when a pixel operation fails.
C++ — pixel reading and writing
// Read a pixel
COLORREF col = GetPixel(hdcMem, 10, 20);
if (col != CLR_INVALID) {
    BYTE r = GetRValue(col);
    BYTE g = GetGValue(col);
    BYTE b = GetBValue(col);
}

// Write pixels — simple flood-fill loop
for (int y = 0; y < 16; y++) {
    for (int x = 0; x < 16; x++) {
        SetPixel(hdcCanvas, x, y, RGB(255, 0, 0));  // red square
    }
}

// Colour constant examples
COLORREF red    = RGB(255,   0,   0);
COLORREF green  = RGB(  0, 255,   0);
COLORREF blue   = RGB(  0,   0, 255);
COLORREF white  = RGB(255, 255, 255);
COLORREF black  = RGB(  0,   0,   0);
COLORREF yellow = RGB(255, 255,   0);
      

GetObjectA, GetDeviceCaps & palette

FunctionStatusDescription
GetObjectA(HANDLE h, int c, LPVOID pv) PARTIAL When h is an HBITMAP and pv points to a BITMAP struct (c = sizeof(BITMAP)), fills width, height, planes, bitsPerPixel and bits pointer from the underlying SDL surface. Returns the byte count filled, or 0 on error.
GetDeviceCaps(HDC hdc, int index) PARTIAL Returns 256 when index == SIZEPALETTE; returns 0 for all other indices. Many games use SIZEPALETTE to determine colour depth.
GetSystemPaletteEntries(HDC, start, count, entries) PARTIAL Fills PALETTEENTRY array with a grayscale ramp (R=G=B=index). Satisfies palette-aware games that expect 256 entries.
C++ — query bitmap dimensions
BITMAP bm;
if (GetObjectA(hBmp, sizeof(bm), &bm)) {
    printf("Width=%ld Height=%ld BPP=%d\n",
           bm.bmWidth, bm.bmHeight, bm.bmBitsPixel);
}
      

Data structures

StructureStatusFields & Notes
BITMAP PARTIAL bmType, bmWidth, bmHeight, bmWidthBytes, bmPlanes, bmBitsPixel, bmBits. Filled by GetObjectA.
BITMAPINFOHEADER STUB Standard BMP info header layout. Declared for compilation compatibility with code that reads .BMP files manually.
BITMAPFILEHEADER STUB 14-byte BMP file header. Declared for compilation compatibility.
RGBQUAD STUB 4-byte BGR+reserved colour entry used in BMP palette tables.
PALETTEENTRY PARTIAL peRed, peGreen, peBlue, peFlags. Used by GetSystemPaletteEntries. Also shared with DirectDraw compatibility code.