设置窗口像素的正确方法(无闪烁)吗? [英] Correct (and flicker-free) way of setting window pixels?
问题描述
我正在努力寻找在WM_PAINT期间将纯RGBA值数组转储到Win32窗口的客户区中的正确方法.我有以下代码,但它似乎已经令人费解,甚至还没有完成:
I'm struggling to figure out the proper way of dumping an array of plain RGBA values into the client area of a Win32 window during WM_PAINT. I have the following code but it already seems convoluted and I'm not even finished:
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;
}
关于源图像"内存格式,我有一些回旋余地,因此我可以使它与目标所需的内容相匹配.
I have some wiggle room with regards to the source "image" memory format so I can make that match whatever the target requires.
我这样做正确吗?有更好的方法吗?
Am I doing this correctly? Is there a better way?
P.S .:显然,每次WM_PAINT出现时,我都会存储而不是重新创建大多数对象.这只是一个示例/在制品.
P.S.: Obviously I would be storing and not recreating most of the objects each time a WM_PAINT comes along. This is just an example/WIP.
:添加了对WM_ERASEBKGND的处理.
Added handling of WM_ERASEBKGND.
好的,看来我需要更具体一些.我不是在查找发布的代码的实际问题.这只是到目前为止我在工作流程方面的一个例子.这意味着我有一个窗口HDC,一个离屏HDC,一个离屏HBITMAP和一个指向我的像素的指针,这些像素位于假设的R8G8B8A8内存布局中.这些对象我该怎么办?我是否可以通过CreateDIBSection创建另一个HBITMAP并将像素写入其中?以后我该怎么办?
Edit 2: Ok, it seems I need to be more specific. I am not looking for actual issues with the code I posted. It is only an example of what I have so far in terms of workflow. That means I have the window HDC, an offscreen HDC, an offscreen HBITMAP and a pointer to my pixels which are in, let's say, a hypothetical R8G8B8A8 memory layout. What do I do with these objects? Do I create another HBITMAP via CreateDIBSection and write my pixels into that? What do I do with it after?
请参阅Barmak Shemirani的答案以获取适当的解决方案(我的示例代码有问题).也请查看Paul Sanders的答案以获取一些现代WinAPI提示.
Edit 3: See answer from Barmak Shemirani for proper solution (my example code has issues). Check also Paul Sanders' answer for some modern WinAPI tips.
谢谢!
推荐答案
要打印mypixels
矢量,请使用SetDIBitsToDevice
绘制设备上下文.或使用SetDIBits
创建一个新的HBITMAP
对象.
To print mypixels
vector use SetDIBitsToDevice
to draw to device context. Or use SetDIBits
to create a new HBITMAP
object.
为简单起见,本示例直接绘制到HDC
中.但是您可以使用CreateCompatibleDC
进行缓冲,也可以使用其他答案中所示的缓冲方法.
For simplicity, this example draw directly in to HDC
. But you can use CreateCompatibleDC
for buffering, or use the buffer method shown in the other answer.
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;
}
使用内存dc:
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;
}
这篇关于设置窗口像素的正确方法(无闪烁)吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!