您不能通过将字节插入某个缓冲区来绘制到任意窗口。 Win32 是比它更高级别的 API。
可以通过写入缓冲区将绘制到位图中。您创建一个位图,这样当您使用CreateDIBSection(...) 创建它时,Windows 会返回一个指向其内容的指针。然后,您可以通过BitBlt 和适当的设备上下文等将该位图绘制到窗口。
下面是一个最小的例子。 (我保留了您对后缓冲区和前缓冲区的使用,即使在这里实际上并不需要。窗口本身本质上是一个前缓冲区,因此您只需要一个后缓冲区的“交换链”就不会出现闪烁。 )
#include <windows.h>
#include <stdint.h>
#include <utility>
#include <algorithm>
constexpr int kTimerID = 101;
LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
struct graphics_buffer {
HBITMAP hbm;
uint32_t* data;
};
graphics_buffer create_graphics_buffer(int wd, int hgt)
{
HDC hdcScreen = GetDC(NULL);
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = wd;
bmi.bmiHeader.biHeight = -hgt; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
graphics_buffer gb;
gb.hbm = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, reinterpret_cast<void**>(&gb.data), NULL, NULL);
ReleaseDC(NULL, hdcScreen);
return gb;
}
class graphic_buffers {
graphics_buffer front_;
graphics_buffer back_;
int wd_;
int hgt_;
public:
graphic_buffers(int wd, int hgt) :
wd_(wd),
hgt_(hgt),
front_(create_graphics_buffer(wd, hgt)),
back_(create_graphics_buffer(wd, hgt))
{
clear();
}
HBITMAP front_bmp() {
return front_.hbm;
}
void swap() {
std::swap(front_, back_);
}
size_t size() const {
return static_cast<size_t>(wd_ * hgt_);
}
int width() const {
return wd_;
}
int height() const {
return hgt_;
}
void clear() {
std::fill(back_.data, back_.data + size(), 0);
}
void set_pixel(int x, int y, uint32_t pix) {
back_.data[y * wd_ + x] = pix;
}
~graphic_buffers() {
DeleteObject(front_.hbm);
DeleteObject(back_.hbm);
}
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = wndproc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"swap_buffers_window";
if (!RegisterClass(&wc))
return 1;
if (!CreateWindow(wc.lpszClassName,
L"buffered window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 640, 480, 0, 0, hInstance, NULL))
return 2;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void draw_something(graphic_buffers& buffs) {
int wd = buffs.width();
int hgt = buffs.height();
static int x = 0;
static int y = 0;
static int x_vel = 4;
static int y_vel = 7;
if (x >= 0 && x < wd && y >= 0 && y < hgt) {
buffs.set_pixel(x, y, 0xffffffff);
}
x += x_vel;
y += y_vel;
if (x < 0 || x > wd) {
x_vel *= -1;
}
if (y < 0 || y > hgt) {
y_vel *= -1;
}
}
LRESULT CALLBACK wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: {
RECT r;
GetClientRect(hWnd, &r);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(new graphic_buffers(r.right - r.left, r.bottom - r.top)));
SetTimer(hWnd, kTimerID, 1, NULL);
}
break;
case WM_TIMER: {
auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
draw_something(*buffs);
buffs->swap();
buffs->clear();
InvalidateRect(hWnd, NULL, FALSE);
}
break;
case WM_PAINT: {
auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC hdc_bmp = CreateCompatibleDC(hdc);
auto old_bmp = SelectObject(hdc_bmp, buffs->front_bmp());
BitBlt(hdc, 0, 0, buffs->width(), buffs->height(), hdc_bmp, 0, 0, SRCCOPY);
SelectObject(hdc, old_bmp);
DeleteDC(hdc_bmp);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY: {
auto buffs = reinterpret_cast<graphic_buffers*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
delete buffs;
}
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}