1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
#include <windows.h>
class [[nodiscard]] w32_screen_buffer
{
BITMAPINFO info_{};
LPVOID data_{};
public:
~w32_screen_buffer() { clear(); }
w32_screen_buffer() = default;
w32_screen_buffer(w32_screen_buffer const&) = delete;
w32_screen_buffer& operator=(w32_screen_buffer const&) = delete;
[[nodiscard]] LONG width_px() const { return info_.bmiHeader.biWidth; }
[[nodiscard]] LONG height_px() const { return -1*info_.bmiHeader.biHeight; }
[[nodiscard]] LPVOID data() { return data_; }
[[nodiscard]] LPCVOID data() const { return data_; }
[[nodiscard]] BITMAPINFO const& info() const { return info_; }
static LONG constexpr bytes_per_pixel = 4;
[[nodiscard]] bool resize(LONG new_width_px, LONG new_height_px)
{
SIZE_T const pixels_allocated = info_.bmiHeader.biWidth * info_.bmiHeader.biHeight;
SIZE_T const pixels_requested = new_width_px * new_height_px;
SIZE_T const bytes_requested = bytes_per_pixel * pixels_requested;
if (pixels_allocated < pixels_requested) clear(); else return true;
// DOCS: https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo
info_.bmiHeader.biSize = sizeof info_.bmiHeader;
info_.bmiHeader.biWidth = new_width_px;
info_.bmiHeader.biBitCount = 8 * bytes_per_pixel;
info_.bmiHeader.biHeight = -1 * new_height_px;
info_.bmiHeader.biPlanes = 1;
info_.bmiHeader.biCompression = BI_RGB;
data_ = VirtualAlloc(nullptr, bytes_requested, MEM_COMMIT, PAGE_READWRITE);
if (! data_) clear();
return data_;
}
void clear()
{
info_.bmiHeader.biWidth = 0;
info_.bmiHeader.biHeight = 0;
if (data_) VirtualFree(data_, 0, MEM_RELEASE);
}
};
struct w32_window_dimension { LONG w, h; };
w32_window_dimension window_client_dimensions(HWND window)
{
RECT r;
GetClientRect(window, &r);
return { r.right - r.left, r.bottom - r.top };
}
w32_screen_buffer screen_buffer;
bool running = true;
void present_buffer(w32_screen_buffer const& buf, HDC dc, LONG w_px, LONG h_px)
{
StretchDIBits(dc, 0, 0, w_px, h_px, 0, 0, buf.width_px(), buf.height_px(),
buf.data(), &buf.info(), DIB_RGB_COLORS, SRCCOPY);
}
void put_pixel(w32_screen_buffer& buf, LONG x, LONG y, ULONG color)
{
LPBYTE p = reinterpret_cast<LPBYTE>(buf.data());
LONG w = buf.width_px();
p += y * buf.bytes_per_pixel * w;
p += x * buf.bytes_per_pixel;
p[0] = static_cast<BYTE>((color & 0xff000000) >> 24);
p[1] = static_cast<BYTE>((color & 0x00ff0000) >> 16);
p[2] = static_cast<BYTE>((color & 0x0000ff00) >> 8);
p[3] = static_cast<BYTE>((color & 0x000000ff) >> 0);
}
LRESULT CALLBACK my_wndproc(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_SIZE:
(void)::screen_buffer.resize(LOWORD(lp), HIWORD(lp));
return 0;
case WM_DESTROY: [[fallthrough]];
case WM_CLOSE: ::running = false;
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(window, &ps);
auto [w, h] = window_client_dimensions(window);
present_buffer(::screen_buffer, dc, w, h);
EndPaint(window, &ps);
return 0;
}
default: return DefWindowProc(window, msg, wp, lp);
}
}
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int)
{
LPCWSTR class_name = L"GDI Demo";
WNDCLASSEXW wc {};
wc.lpszClassName = class_name;
wc.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC;
wc.lpfnWndProc = my_wndproc;
wc.cbSize = sizeof wc;
wc.hInstance = instance;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
if (! RegisterClassExW(&wc)) return 1;
HWND window =
CreateWindowExW(0, wc.lpszClassName, wc.lpszClassName, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
instance, 0);
if (! window) return 1;
HDC dc = GetWindowDC(window);
MSG message {};
while (::running)
{
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
if (message.message == WM_QUIT) ::running = false; else DispatchMessage(&message);
auto [w, h] = window_client_dimensions(window);
// make the top left quarter of the screen red; basically
// do all your video rendering here
for (int x = 0; x < w / 2; ++x)
for (int y = 0; y < h / 2; ++y)
put_pixel(::screen_buffer, x, y, 0xff00);
present_buffer(::screen_buffer, dc, w, h);
}
return 0;
}
|