Last active
February 10, 2026 14:42
-
-
Save peteroupc/f1a5d8e45e27123b86b284271cfd802b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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