Last active
July 25, 2017 20:53
-
-
Save bit-hack/0ce311db7e940d29925cb212da27872b to your computer and use it in GitHub Desktop.
Direct Window Pixel Blitting
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
#include <cassert> | |
#include <string> | |
#define WIN32_LEAN_AND_MEAN | |
#include <Windows.h> | |
typedef unsigned int uint; | |
struct GDIDetail { | |
struct LockInfo { | |
uint32_t* _pixels; | |
uint32_t _width, _pitch; | |
uint32_t _height; | |
}; | |
GDIDetail() | |
: _window(NULL) | |
, _dwExStyle(WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW) | |
, _dwStyle(WS_CAPTION | WS_OVERLAPPED) | |
{ | |
ZeroMemory(&_screen, sizeof(_screen)); | |
} | |
bool windowCreate(uint w, uint h); | |
bool windowResize(uint w, uint h); | |
bool screenInvalidate(); | |
bool screenLock(LockInfo* out); | |
HWND windowHandle() const | |
{ | |
return _window; | |
} | |
protected: | |
static LRESULT CALLBACK windowEventHandler(HWND, UINT, WPARAM, LPARAM); | |
// repaint the current window (WM_PAINT) | |
LRESULT _windowRedraw(); | |
// create a back buffer of a specific size | |
bool _screenCreate(uint w, uint h); | |
// window handle | |
HWND _window; | |
// window styles used by resizing code | |
const DWORD _dwStyle, _dwExStyle; | |
// screen buffer info | |
struct { | |
BITMAPINFO _bmp; | |
uint32_t* _data; | |
} _screen; | |
}; | |
bool GDIDetail::_screenCreate(uint w, uint h) | |
{ | |
assert(w && h); | |
// create screen data | |
if (_screen._data) { | |
delete[] _screen._data; | |
_screen._data = NULL; | |
} | |
_screen._data = new uint32_t[w * h]; | |
assert(_screen._data); | |
// create bitmap info | |
ZeroMemory(&_screen._bmp, sizeof(BITMAPINFO)); | |
BITMAPINFOHEADER& b = _screen._bmp.bmiHeader; | |
b.biSize = sizeof(BITMAPINFOHEADER); | |
b.biBitCount = 32; | |
b.biWidth = w; | |
b.biHeight = h; | |
b.biPlanes = 1; | |
b.biCompression = BI_RGB; | |
// success | |
return true; | |
} | |
LRESULT GDIDetail::_windowRedraw() | |
{ | |
if (!_screen._data) { | |
// no screen; hand back to default handler | |
return DefWindowProcA(_window, WM_PAINT, 0, 0); | |
} | |
// flip blitted image | |
static const int xFlip = false ? -1 : 1; | |
static const int yFlip = false ? -1 : 1; | |
// blit buffer to screen | |
PAINTSTRUCT ps; | |
HDC hdc = BeginPaint(_window, &ps); | |
const BITMAPINFOHEADER& bih = _screen._bmp.bmiHeader; | |
// do the bit blit | |
StretchDIBits( | |
hdc, | |
0, | |
0, | |
bih.biWidth * xFlip, | |
bih.biHeight * yFlip, | |
0, | |
0, | |
bih.biWidth, | |
bih.biHeight, | |
_screen._data, | |
&(_screen._bmp), | |
DIB_RGB_COLORS, | |
SRCCOPY); | |
// finished WM_PAINT | |
EndPaint(_window, &ps); | |
return 0; | |
} | |
LRESULT CALLBACK GDIDetail::windowEventHandler(HWND hWnd, UINT Msg, | |
WPARAM wParam, LPARAM lParam) | |
{ | |
GDIDetail* self; | |
switch (Msg) { | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
return 0; | |
case WM_PAINT: { | |
// dispatch redraw to window instance | |
if (self = (GDIDetail*)GetWindowLongPtrA(hWnd, GWLP_USERDATA)) { | |
return self->_windowRedraw(); | |
} | |
// fallthrough | |
} | |
default: | |
printf("- %x\n", Msg); | |
return DefWindowProcA(hWnd, Msg, wParam, lParam); | |
} | |
} | |
bool GDIDetail::windowCreate(uint w, uint h) | |
{ | |
assert(_window == NULL && w && h); | |
static const char* kClassName = "ScummVMClass"; | |
static const char* kWndName = "ScummVM"; | |
// create window class | |
HINSTANCE hinstance = GetModuleHandle(NULL); | |
WNDCLASSEXA wndClassEx = { | |
sizeof(WNDCLASSEXA), | |
CS_OWNDC, | |
windowEventHandler, | |
0, | |
0, | |
hinstance, | |
NULL, | |
NULL, | |
NULL, | |
NULL, | |
(LPCSTR)kClassName, | |
NULL | |
}; | |
if (RegisterClassExA(&wndClassEx) == 0) { | |
return false; | |
} | |
// create window | |
_window = CreateWindowExA(_dwExStyle, kClassName, (LPCSTR)kWndName, _dwStyle, CW_USEDEFAULT, | |
CW_USEDEFAULT, w, h, NULL, NULL, hinstance, NULL); | |
if (_window == NULL) { | |
return false; | |
} | |
// resize window and create the offscreen buffer | |
if (!windowResize(w, h)) { | |
return false; | |
} | |
// pass class instance to window user data to that the static window proc | |
// can dispatch to the GDIDetail instance. | |
SetLastError(0); | |
if (SetWindowLongPtrA(_window, GWLP_USERDATA, (LONG)this) == 0 && GetLastError()) { | |
// unable to set window user data | |
return false; | |
} | |
// force a screen redraw | |
screenInvalidate(); | |
// display window | |
ShowWindow(_window, SW_SHOW); | |
return true; | |
} | |
bool GDIDetail::windowResize(uint w, uint h) | |
{ | |
assert(w && h); | |
// get current window rect | |
RECT rect; | |
GetWindowRect(_window, &rect); | |
// adjust to expected client area | |
rect.right = rect.left + w; | |
rect.bottom = rect.top + h; | |
// adjust so rect becomes client area | |
if (AdjustWindowRectEx(&rect, _dwStyle, FALSE, _dwExStyle) == FALSE) { | |
return false; | |
} | |
// XXX: this will move the window by [5,5] pixels | |
MoveWindow(_window, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); | |
// create a new screen buffer for out window size | |
if (!_screenCreate(w, h)) { | |
return false; | |
} | |
// force a screen redraw | |
screenInvalidate(); | |
return true; | |
} | |
bool GDIDetail::screenLock(LockInfo* info) | |
{ | |
assert(info); | |
info->_pixels = _screen._data; | |
info->_width = _screen._bmp.bmiHeader.biWidth; | |
info->_height = _screen._bmp.bmiHeader.biHeight; | |
info->_pitch = _screen._bmp.bmiHeader.biWidth; | |
return info->_pixels != NULL; | |
} | |
bool GDIDetail::screenInvalidate() | |
{ | |
// invalidate entire window | |
return InvalidateRect(_window, NULL, FALSE) != 0; | |
} | |
namespace { | |
bool appEventPump() | |
{ | |
MSG msg; | |
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { | |
switch (msg.message) { | |
case WM_PAINT: | |
break; | |
case WM_QUIT: | |
return false; | |
default: | |
printf("+ %x\n", msg.message); | |
} | |
// translate virtual key code | |
TranslateMessage(&msg); | |
// dispatch to window proc | |
DispatchMessage(&msg); | |
} | |
Sleep(1); | |
return true; | |
} | |
uint32_t xorshift32() | |
{ | |
static uint32_t x = 12345; | |
x ^= x << 13; | |
x ^= x >> 17; | |
x ^= x << 5; | |
return x; | |
} | |
} | |
int main() | |
{ | |
GDIDetail gdi; | |
if (!gdi.windowCreate(320, 240)) { | |
return false; | |
} | |
while (appEventPump()) { | |
GDIDetail::LockInfo lock; | |
if (gdi.screenLock(&lock)) { | |
for (uint i = 0; i < lock._width * lock._height; ++i) { | |
lock._pixels[i] = xorshift32(); | |
} | |
} | |
gdi.screenInvalidate(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment