【发布时间】:2019-03-16 23:01:00
【问题描述】:
上下文:
- Windows 10
- WinAPI 和 C 代码
问题:
我有一个像素缓冲区,表示为uint8_t buffer[width * height * PIXEL_SIZE],我想定期修改内容数据并将缓冲区重绘到窗口中。
我遇到了两个与我迷路的 winapi 相关的问题:
- 如何在窗口上打印像素缓冲区?
- 我如何在后面的代码中重绘女士修改缓冲区?
我做了很多研究,但没有代码 sn-p 成功地帮助我解决了我的问题。
这里是一个不工作的代码示例,以展示我想用我拥有的代码元素归档的内容:
new_image.c
// Global variables
static HDC hdc;
static HDC context_hdc;
static HBITMAP hDib;
static HGDIOBJ obj;
static void set_bmi_object(BITMAPINFO *bmi, int width, int height) {
memset(bmi, 0, sizeof(BITMAPINFO));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi->bmiHeader.biWidth = width;
bmi->bmiHeader.biHeight = -height;
bmi->bmiHeader.biPlanes = 1;
bmi->bmiHeader.biBitCount = 32;
bmi->bmiHeader.biCompression = BI_RGB;
}
// Allocate a new image buffer
void *new_image(HWND hwnd, int width, int height)
{
BITMAPINFO bmi;
BYTE *bits = NULL;
void *buffer;
if (NULL == (buffer = (char*)malloc(width * height * PIXEL_SIZE)))
return (NULL);
set_bmi_object(&bmi, width, height);
hdc = GetDC(hwnd);
hDib = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)(&bits),
NULL, 0);
if (hDib != NULL) {
context_hdc = CreateCompatibleDC(hdc);
if (context_hdc == NULL) {
DeleteObject(hDib);
} else {
obj = SelectObject(context_hdc, hDib);
CopyMemory(bits, buffer, width * height * sizeof(PIXEL_SIZE));
}
}
return (newimg);
}
// Print the buffer of pixel on the window
void put_image_to_window(HWND hwnd, void *buffer, int x, int y)
{
(void)hwnd;
// Void buffer because i should use directly HDCcontext_hdc linked to HGDIOBJ obj ?
(void)buffer;
BitBlt(hdc, // destination
x,
y,
500, // width of the region
500, // height
context_hdc, // source
0, // x
0, // y
SRCCOPY);
UpdateWindow(hwnd);
}
main.c
static const char g_szClassName[] = "myWindowClass";
static void paint(HWND hwnd) {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
paint(hwnd);
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int main(void) {
MSG Msg;
HINSTANCE hInstance;
HWND hwnd;
STARTUPINFOA startup_info;
WNDCLASSEX wc;
HWND hwnd;
GetStartupInfoA(&startup_info);
hInstance = GetModuleHandle(NULL);
memset(&wc, 0, sizeof(wc));
// Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
// ... etc
wc.lpszClassName = TEXT(g_szClassName);
if (!RegisterClassEx(&wc)) {
return (-1);
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"Title,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
500,
NULL,
NULL,
hInstance,
NULL);
if (hwnd == NULL) {
return (-1);
}
ShowWindow(hwnd, startup_info.wShowWindow);
image = new_image(hwnd, 500, 500);
put_image_to_window(hwnd, image, 0, 0);
UpdateWindow(hwnd);
// The Message Loop
while (GetMessage(&Msg, NULL, 0, 0)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (Msg.wParam);
}
这里是new_image.c
一个函数
new_image,它返回一个像素缓冲区的指针,一个函数
put_image_to_window在窗口上显示像素缓冲区。
【问题讨论】:
-
如果 UpdateWindow 不起作用,您可能会使用InvalidateRect 触发重绘,如果您的 WM_PAINT 处理程序可以使用它,那么您可以具体说明更改的位。
-
您在
WM_PAINT处理程序之外进行绘制,因此您绘制的任何内容都将在下一次绘制请求时被覆盖。而你的WM_PAINT处理程序什么也没画,所以它只会画你的wc.hbrBackground。 -
必读:Painting and Drawing。请务必阅读全部。
-
无论如何,您必须能够在 WM_PAINT 中完成所有绘图,因为如果用户最小化窗口然后恢复它,您需要重新生成您的内容。如果您在 WM_PAINT 之外绘制,则需要记住足够的信息,以便 WM_PAINT 可以复制您所做的。大多数人只是让 WM_PAINT 完成所有的绘图。避免代码重复。
-
有两种处理这种情况的方法:1)处理
WM_PAINT的“旧”方法,其中像素被放入HBITMAP,然后重新绘制到窗口的@ 987654334@每次收到WM_PAINT; 2) 使用WS_EX_LAYERED窗口样式和UpdateLayeredWindow()函数的“新”方式。将像素放入HBITMAP并创建内存中的HDC以将其选中,然后将HDC传递给UpdateLayeredWindow()并且根本不处理WM_PAINT。每当像素发生变化时,只需再次调用UpdateLayeredWindow()。