设置 window 像素的正确(且无闪烁)方式?
Correct (and flicker-free) way of setting window pixels?
我正在努力找出在 WM_PAINT 期间将纯 RGBA 值数组转储到 Win32 window 客户区的正确方法。我有以下代码,但它看起来已经很复杂,我什至没有完成:
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
{
PAINTSTRUCT paintInfo{};
HDC device = BeginPaint(window, &paintInfo);
if (device == nullptr)
throw runtime_error(RG_LOCATION());
ScopeExit endPaint([&] { EndPaint(window, &paintInfo); });
HDC offscreenDevice = CreateCompatibleDC(device);
ScopeExit deleteOffscreenDevice([&] { DeleteDC(offscreenDevice); });
HBITMAP offscreenBitmap = CreateCompatibleBitmap(device, Distance(paintInfo.rcPaint.left, paintInfo.rcPaint.right),
Distance(paintInfo.rcPaint.top, paintInfo.rcPaint.bottom));
ScopeExit deleteOffscreenBitmap([&] { DeleteObject(offscreenBitmap); });
HBITMAP previousBitmap = reinterpret_cast<HBITMAP>(SelectObject(offscreenDevice, offscreenBitmap));
// now I need to blit the available pixel data...
vector<array<uint8_t, 4>> mypixels;
// ...onto the client area of the window.
// What do I do next?
// CreateDIBSection ?
// BitBlt ?
return 0;
}
关于源 "image" 内存格式,我有一些回旋余地,因此我可以匹配目标需要的任何内容。
我这样做正确吗?有没有更好的方法?
P.S.: 显然,每次出现 WM_PAINT 时,我都会存储而不是重新创建大部分对象。这只是一个 example/WIP.
编辑: 添加了对 WM_ERASEBKGND.
的处理
Edit 2: 好的,看来我需要更具体一些。我不是在寻找我发布的代码的实际问题。这只是我目前在工作流程方面的一个例子。这意味着我有 window HDC、一个屏幕外 HDC、一个屏幕外 HBITMAP 和一个指向我的像素的指针,比方说,这是一个假设的 R8G8B8A8 内存布局。我该如何处理这些对象?我是否通过 CreateDIBSection 创建另一个 HBITMAP 并将我的像素写入其中?之后我该怎么办?
编辑 3: 请参阅 Barmak Shemirani 的回答以获得正确的解决方案(我的示例代码有问题)。另请查看 Paul Sanders 的回答以了解一些现代 WinAPI 技巧。
谢谢大家!
关于无闪烁绘图,Vista 和更高版本在 Win32 中内置了双缓冲支持 API。我从 this article. More info at MSDN 改编了下面的代码。 Barmak 的回答向您展示了如何绘制像素。
初始化(每个线程):
BufferedPaintInit();
终止(每个线程):
BufferedPaintUnInit();
在你的 WndProc 中:
case WM_PAINT:
{
// Set things up in the usual way
PAINTSTRUCT ps;
HDC hDC = BeginPaint (hWnd, &ps);
RECT rc;
GetClientRect (hWnd, &rc);
// Try to use buffered painting (may fail, so they say)
HDC hBufferedDC;
HPAINTBUFFER hBufferedPaint = BeginBufferedPaint (hDC, &rc, BPBF_COMPATIBLEBITMAP, NULL, &hBufferedDC);
if (hBufferedPaint)
hDC = hBufferedDC;
// Draw stuff into hDC
// Clean up
if (hBufferedPaint)
EndBufferedPaint (hBufferedPaint, TRUE);
// Finished
EndPaint (hWnd, &ps);
break;
}
没什么,真的。
要打印 mypixels
向量,请使用 SetDIBitsToDevice
绘制到设备上下文。或者使用 SetDIBits
创建一个新的 HBITMAP
对象。
为简单起见,此示例直接绘制到HDC
。但是你可以使用 CreateCompatibleDC
进行缓冲,或者使用其他答案中显示的缓冲方法。
case WM_PAINT:
{
//int w = width of source bitmap
//int h = height of source bitmap
//optional: make sure width and height are correct
assert(mypixels.size() == w * h);
PAINTSTRUCT ps;
auto hdc = BeginPaint(hwnd, &ps);
BITMAPINFOHEADER bi{ sizeof(bi) };
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
SetDIBitsToDevice(hdc, 0, 0, w, h, 0, 0, 0, h, &mypixels[0],
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
EndPaint(hwnd, &ps);
return 0;
}
使用内存直流:
case WM_PAINT:
{
RECT rc;
GetClientRect(hwnd, &rc);
int canvas_width = rc.right;
int canvas_height = rc.bottom;
PAINTSTRUCT ps;
auto hdc = BeginPaint(hwnd, &ps);
//create memory dc:
auto memdc = CreateCompatibleDC(hdc);
auto hbmp = CreateCompatibleBitmap(hdc, canvas_width, canvas_height);
auto oldbmp = SelectObject(memdc, hbmp); //<- memdc is ready
//draw on memory dc:
BITMAPINFOHEADER bi{ sizeof(bi), w, h, 1, 32, BI_RGB };
SetDIBitsToDevice(memdc, 0, 0, w, h, 0, 0, 0, h, mypixels.data(),
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
//draw on actual dc:
BitBlt(hdc, 0, 0, canvas_width, canvas_height, memdc, 0, 0, SRCCOPY);
//clean up:
SelectObject(memdc, oldbmp);
DeleteObject(hbmp);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
我正在努力找出在 WM_PAINT 期间将纯 RGBA 值数组转储到 Win32 window 客户区的正确方法。我有以下代码,但它看起来已经很复杂,我什至没有完成:
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
{
PAINTSTRUCT paintInfo{};
HDC device = BeginPaint(window, &paintInfo);
if (device == nullptr)
throw runtime_error(RG_LOCATION());
ScopeExit endPaint([&] { EndPaint(window, &paintInfo); });
HDC offscreenDevice = CreateCompatibleDC(device);
ScopeExit deleteOffscreenDevice([&] { DeleteDC(offscreenDevice); });
HBITMAP offscreenBitmap = CreateCompatibleBitmap(device, Distance(paintInfo.rcPaint.left, paintInfo.rcPaint.right),
Distance(paintInfo.rcPaint.top, paintInfo.rcPaint.bottom));
ScopeExit deleteOffscreenBitmap([&] { DeleteObject(offscreenBitmap); });
HBITMAP previousBitmap = reinterpret_cast<HBITMAP>(SelectObject(offscreenDevice, offscreenBitmap));
// now I need to blit the available pixel data...
vector<array<uint8_t, 4>> mypixels;
// ...onto the client area of the window.
// What do I do next?
// CreateDIBSection ?
// BitBlt ?
return 0;
}
关于源 "image" 内存格式,我有一些回旋余地,因此我可以匹配目标需要的任何内容。
我这样做正确吗?有没有更好的方法?
P.S.: 显然,每次出现 WM_PAINT 时,我都会存储而不是重新创建大部分对象。这只是一个 example/WIP.
编辑: 添加了对 WM_ERASEBKGND.
的处理Edit 2: 好的,看来我需要更具体一些。我不是在寻找我发布的代码的实际问题。这只是我目前在工作流程方面的一个例子。这意味着我有 window HDC、一个屏幕外 HDC、一个屏幕外 HBITMAP 和一个指向我的像素的指针,比方说,这是一个假设的 R8G8B8A8 内存布局。我该如何处理这些对象?我是否通过 CreateDIBSection 创建另一个 HBITMAP 并将我的像素写入其中?之后我该怎么办?
编辑 3: 请参阅 Barmak Shemirani 的回答以获得正确的解决方案(我的示例代码有问题)。另请查看 Paul Sanders 的回答以了解一些现代 WinAPI 技巧。
谢谢大家!
关于无闪烁绘图,Vista 和更高版本在 Win32 中内置了双缓冲支持 API。我从 this article. More info at MSDN 改编了下面的代码。 Barmak 的回答向您展示了如何绘制像素。
初始化(每个线程):
BufferedPaintInit();
终止(每个线程):
BufferedPaintUnInit();
在你的 WndProc 中:
case WM_PAINT:
{
// Set things up in the usual way
PAINTSTRUCT ps;
HDC hDC = BeginPaint (hWnd, &ps);
RECT rc;
GetClientRect (hWnd, &rc);
// Try to use buffered painting (may fail, so they say)
HDC hBufferedDC;
HPAINTBUFFER hBufferedPaint = BeginBufferedPaint (hDC, &rc, BPBF_COMPATIBLEBITMAP, NULL, &hBufferedDC);
if (hBufferedPaint)
hDC = hBufferedDC;
// Draw stuff into hDC
// Clean up
if (hBufferedPaint)
EndBufferedPaint (hBufferedPaint, TRUE);
// Finished
EndPaint (hWnd, &ps);
break;
}
没什么,真的。
要打印 mypixels
向量,请使用 SetDIBitsToDevice
绘制到设备上下文。或者使用 SetDIBits
创建一个新的 HBITMAP
对象。
为简单起见,此示例直接绘制到HDC
。但是你可以使用 CreateCompatibleDC
进行缓冲,或者使用其他答案中显示的缓冲方法。
case WM_PAINT:
{
//int w = width of source bitmap
//int h = height of source bitmap
//optional: make sure width and height are correct
assert(mypixels.size() == w * h);
PAINTSTRUCT ps;
auto hdc = BeginPaint(hwnd, &ps);
BITMAPINFOHEADER bi{ sizeof(bi) };
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
SetDIBitsToDevice(hdc, 0, 0, w, h, 0, 0, 0, h, &mypixels[0],
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
EndPaint(hwnd, &ps);
return 0;
}
使用内存直流:
case WM_PAINT:
{
RECT rc;
GetClientRect(hwnd, &rc);
int canvas_width = rc.right;
int canvas_height = rc.bottom;
PAINTSTRUCT ps;
auto hdc = BeginPaint(hwnd, &ps);
//create memory dc:
auto memdc = CreateCompatibleDC(hdc);
auto hbmp = CreateCompatibleBitmap(hdc, canvas_width, canvas_height);
auto oldbmp = SelectObject(memdc, hbmp); //<- memdc is ready
//draw on memory dc:
BITMAPINFOHEADER bi{ sizeof(bi), w, h, 1, 32, BI_RGB };
SetDIBitsToDevice(memdc, 0, 0, w, h, 0, 0, 0, h, mypixels.data(),
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
//draw on actual dc:
BitBlt(hdc, 0, 0, canvas_width, canvas_height, memdc, 0, 0, SRCCOPY);
//clean up:
SelectObject(memdc, oldbmp);
DeleteObject(hbmp);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}