#include "Window.h" static LRESULT CALLBACK wndow_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_CREATE: { HDC hdc = GetDC(hwnd); bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitmap_info.bmiHeader.biWidth = window_width; bitmap_info.bmiHeader.biHeight = -window_height; // Negative to flip the image bitmap_info.bmiHeader.biPlanes = 1; bitmap_info.bmiHeader.biBitCount = 32; // 32 bits per pixel (RGBA) bitmap_info.bmiHeader.biCompression = BI_RGB; bitmap = CreateDIBSection(hdc, &bitmap_info, DIB_RGB_COLORS, (void**)&pixel_buffer, NULL, 0); hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, bitmap); ReleaseDC(hwnd, hdc); return 0; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); HDC hdc_mem = CreateCompatibleDC(hdc); HGDIOBJ old_bitmap = SelectObject(hdc_mem, bitmap); BitBlt(hdc, 0, 0, window_width, window_height, hdc_mem, 0, 0, SRCCOPY); SelectObject(hdc_mem, old_bitmap); DeleteDC(hdc_mem); EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: { DeleteDC(hdc_mem); DeleteObject(bitmap); SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); PostQuitMessage(0); return 0; } default: return DefWindowProc(hwnd, msg, wparam, lparam); } } static DWORD WINAPI RenderThreadProc(LPVOID lpParameter) { render_job_t* job = (render_job_t*)lpParameter; renderer_start(job); return 0; } bool window_create(const char* title, HINSTANCE hInst, int width, int height, render_job_t* render_job) { RECT rect = {0, 0, width, height}; AdjustWindowRect(&rect, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE); window_width = rect.right - rect.left; window_height = rect.bottom - rect.top; WNDCLASS wc = {0}; wc.lpfnWndProc = wndow_proc; wc.hInstance = hInst; wc.lpszClassName = title; wc.hCursor = LoadCursor(hInst, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClass(&wc); int screen_width = GetSystemMetrics(SM_CXSCREEN); int screen_height = GetSystemMetrics(SM_CYSCREEN); int pos_x = (screen_width - window_width) / 2; int pos_y = (screen_height - window_height) / 2; hwnd = CreateWindowEx(0, title, title, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, pos_x, pos_y, window_width, window_height, NULL, NULL, wc.hInstance, NULL); if (!hwnd) { return false; } ShowWindow(hwnd, SW_SHOW); render_thread = CreateThread(NULL, 0, RenderThreadProc, render_job, 0, NULL); if (render_thread == NULL) { return false; } return true; } void window_update_pixel(vec4s color, int pixel_x, int pixel_y) { int pixel_index = (pixel_y * window_width + pixel_x) * 4; float alpha = fminf(fmaxf(color.w, 0.0f), 1.0f); // NOTE: The pixel buffer is in BGRA format, so we need to swap the channels pixel_buffer[pixel_index] = COLOR_CLAMP(color.z * alpha * 255.0f); pixel_buffer[pixel_index + 1] = COLOR_CLAMP(color.y * alpha * 255.0f); pixel_buffer[pixel_index + 2] = COLOR_CLAMP(color.x * alpha * 255.0f); pixel_buffer[pixel_index + 3] = COLOR_CLAMP(alpha * 255.0f); } void window_refresh_region(int pixel_x, int pixel_y, int region_width, int region_height) { HDC hdc = GetDC(hwnd); BitBlt(hdc, pixel_x, pixel_y, region_width, region_height, hdc_mem, pixel_x, pixel_y, SRCCOPY); ReleaseDC(hwnd, hdc); } void window_close() { PostMessage(hwnd, WM_DESTROY, 0, 0); WaitForSingleObject(render_thread, INFINITE); CloseHandle(render_thread); DestroyWindow(hwnd); }