Skip to content

Instantly share code, notes, and snippets.

@peteroupc
Last active February 10, 2026 14:42
Show Gist options
  • Select an option

  • Save peteroupc/f1a5d8e45e27123b86b284271cfd802b to your computer and use it in GitHub Desktop.

Select an option

Save peteroupc/f1a5d8e45e27123b86b284271cfd802b to your computer and use it in GitHub Desktop.
// If this code is in a file named 'graf.c' then:
// To compile: i686-w64-mingw32-gcc -O3 graf.c -lgdi32 -o graf.exe
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#define APP_NAME _T("Game App")
#define ALLOC(m) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,m)
#define FREE(m) HeapFree(GetProcessHeap(),0,m)
#define ODS(s) MessageBox(NULL,s,_T("Debug"),MB_OK)
// Use 8-bpp frame buffer if 1; 24-bpp frame buffer if 0
#define EIGHTBPP 0
typedef struct {
BITMAPINFOHEADER bmiHeader;
RGBQUAD colors[256];
} BITMAPINFO256;
typedef struct {
BITMAPINFOHEADER bmiHeader;
RGBQUAD colors[16];
} BITMAPINFO16;
typedef struct {
#ifdef EIGHTBPP
BITMAPINFO256 bmi;
#else
BITMAPINFO bmi;
#endif
BYTE *bitmapBits;
DWORD frame;
} APPDATA;
LRESULT CALLBACK WndProc (HWND hWnd, UINT msg, UINT wParam, LONG lParam);
// Frame buffer size
#define FBWIDTH 320
#define FBHEIGHT 240
static_assert(FBWIDTH%4==0);
// Zoom factor for the frame buffer
#define ZOOM 2
///////////////////////////////
#define CLAMP(a,b,c) ( ((a)<(b)) ? (b) : ( ((a)>(c)) ? (c) : (a) ) )
#if EIGHTBIT
BOOL GRectFill(APPDATA *app, LONG x0, LONG x1, LONG y0, LONG y1, DWORD pixel) {
if(x0>x1 || y0>y1)return FALSE;
if((pixel&0xFFFFFF00)!=0)return FALSE;
x0=CLAMP(x0,0,FBWIDTH);
x1=CLAMP(x1,0,FBWIDTH);
y0=CLAMP(y0,0,FBHEIGHT);
y1=CLAMP(y1,0,FBHEIGHT);
int x,y;
for (y=y0;y<y1;y++){
memset(&app->bitmapBits[y*FBWIDTH+x0], (BYTE)pixel, x1-x0);
}
return TRUE;
}
#else
BOOL GRectFill(APPDATA *app, LONG x0, LONG x1, LONG y0, LONG y1, DWORD pixel) {
if(x0>x1 || y0>y1)return FALSE;
if((pixel&0xFFFFFF00)!=0)return FALSE;
x0=CLAMP(x0,0,FBWIDTH);
x1=CLAMP(x1,0,FBWIDTH);
y0=CLAMP(y0,0,FBHEIGHT);
y1=CLAMP(y1,0,FBHEIGHT);
int x,y;
for (y=y0;y<y1;y++){
BYTE *pos=&app->bitmapBits[y*FBWIDTH*3+x0*3];
for (x=x0;x<x1;x++){
pos[0]=GetRValue(pixel);
pos[1]=GetGValue(pixel);
pos[2]=GetBValue(pixel);
pos+=3;
}
}
return TRUE;
}
#endif
BOOL AppInitialize(APPDATA *app) {
// Do initialization work
return TRUE;
}
void AppShutdown(APPDATA *app) {
// Do shutdown work
}
// Render the frame buffer
void RenderFrame(APPDATA *app) {
BYTE*pos = app->bitmapBits; // Get the frame buffer data
int x, y;
for (y = 0; y < FBHEIGHT; y++) {
for (x = 0; x < FBWIDTH; x++) {
BYTE b = (BYTE) (((x < y ? x : y) < 255 ? (x < y ? x : y) : 255) + app->frame);
pos[0] = b;
#if EIGHTBPP
pos += 1;
#else
pos[1] = b;
pos[2] = b;
pos += 3;
#endif
}
}
}
////////////////////////////////
BOOL SetClientAreaSize (HWND hWnd, LONG cx, LONG cy) {
if (cx < 0 || cy < 0 || !hWnd) {
ODS("Invalid params");
return FALSE;
}
RECT rc = {0, 0, 0, 0};
rc.right = cx;
rc.bottom = cy;
if (!AdjustWindowRect (&rc, GetWindowLong (hWnd, GWL_STYLE), FALSE)) {
ODS("Failed to adjust rect");
return FALSE;
}
rc.right -= rc.left;
rc.bottom -= rc.top;
return SetWindowPos (hWnd, NULL, 0, 0, rc.right, rc.bottom,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd;
WNDCLASS wndclass;
MSG msg;
memset (&wndclass, 0, sizeof (WNDCLASS));
memset (&msg, 0, sizeof (MSG));
wndclass.lpszClassName = _T("GameApp");
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = (WNDPROC) WndProc;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) COLOR_WINDOW;
if (!RegisterClass (&wndclass)) {
ODS("failed to register class");
return 1;
}
hWnd = CreateWindow (_T("GameApp"),
APP_NAME,
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
GetSystemMetrics (SM_CYCAPTION) * 2,
GetSystemMetrics (SM_CYCAPTION) * 2,
1, 1,
NULL,
NULL,
hInstance,
NULL);
if(!hWnd){
ODS(_T("failed to create window"));
return 1;
}
if (!SetClientAreaSize (hWnd, FBWIDTH * ZOOM, FBHEIGHT * ZOOM)) {
ODS(_T("failed to set window size"));
DestroyWindow (hWnd);
return 1;
}
ShowWindow (hWnd, SW_SHOW);
if (!UpdateWindow (hWnd)) {
ODS(_T("failed to update window"));
DestroyWindow (hWnd);
return 1;
}
while (GetMessage (&msg, NULL, 0, 0) == TRUE) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc (
HWND hWnd,
UINT msg,
UINT wParam,
LONG lParam)
{
switch (msg) {
case WM_ERASEBKGND:
return 1;
case WM_CREATE:
{
APPDATA *appData;
appData = (APPDATA*)ALLOC (sizeof (APPDATA));
if(sizeof(APPDATA*)!=4)ODS("not 32-bit pointer");
if (!appData) {
return 0;
}
appData->frame = 0;
(appData->bmi).bmiHeader.biSize = 0x28;
(appData->bmi).bmiHeader.biWidth = FBWIDTH;
// Negative so that image is top down in memory.
(appData->bmi).bmiHeader.biHeight = -FBHEIGHT;
(appData->bmi).bmiHeader.biPlanes = 1;
appData->bitmapBits = NULL;
BYTE *bmbits = NULL;
#if EIGHTBPP
(appData->bmi).bmiHeader.biBitCount = 8;
int i;
for(i=0;i<256;i++){
(appData->bmi).colors[i].rgbRed = (BYTE)i;
(appData->bmi).colors[i].rgbGreen = (BYTE)i;
(appData->bmi).colors[i].rgbBlue = (BYTE)i;
(appData->bmi).colors[i].rgbReserved = 0;
}
bmbits = (BYTE*)ALLOC (FBWIDTH * FBHEIGHT);
#else
(appData->bmi).bmiHeader.biBitCount = 24;
bmbits = (BYTE*)ALLOC (FBWIDTH * FBHEIGHT * 3);
#endif
if (!bmbits) {
FREE (appData);
return 0;
}
appData->bitmapBits = bmbits;
if(!AppInitialize(appData)) {
FREE (bmbits);
FREE (appData);
return 0;
}
SetWindowLong (hWnd, GWL_USERDATA, (LONG)appData);
RenderFrame(appData);
appData->frame+=1;
SetTimer (hWnd, 1, 16, NULL);
}
break;
case WM_TIMER:
{
APPDATA* app = (APPDATA*)GetWindowLong (hWnd, GWL_USERDATA);
if (app) {
// Draw the frame
RenderFrame(app);
app->frame += 1;
}
InvalidateRect (hWnd, NULL, FALSE);
}
break;
case WM_PAINT:
{
// Paint the frame buffer to the window
PAINTSTRUCT ps;
HDC hDC = BeginPaint (hWnd, &ps);
APPDATA* app = (APPDATA*)GetWindowLong (hWnd, GWL_USERDATA);
if (app) {
StretchDIBits (hDC, 0, 0, FBWIDTH * ZOOM, FBHEIGHT * ZOOM, 0, 0, FBWIDTH, FBHEIGHT,
app->bitmapBits,
(BITMAPINFO*)&app->bmi, DIB_RGB_COLORS, SRCCOPY);
}
EndPaint (hWnd, &ps);
}
break;
case WM_DESTROY:
{
APPDATA* app = (APPDATA*)GetWindowLong (hWnd, GWL_USERDATA);
if (app) {
AppShutdown(app);
if (app->bitmapBits) {
FREE (app->bitmapBits);
}
FREE (app);
}
SetWindowLong (hWnd, GWL_USERDATA, 0);
KillTimer (hWnd, 1);
PostQuitMessage (0);
return 0;
}
default:
return DefWindowProc (hWnd, msg, wParam, lParam);
}
return 0L;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment