c++ - 设置窗口像素的正确(无闪烁)方式?

标签 c++ winapi gdi

我正在努力找出在 WM_PAINT 期间将纯 RGBA 值数组转储到 Win32 窗口的客户区的正确方法。我有以下代码,但它看起来已经很复杂,我什至没有完成:

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;
}

关于源“图像”内存格式,我有一些回旋余地,因此我可以根据目标要求进行匹配。

我这样做正确吗?有没有更好的办法?

P.S.:显然,每次出现 WM_PAINT 时,我都会存储而不是重新创建大部分对象。这只是一个示例/WIP。

编辑:添加了对 WM_ERASEBKGND 的处理。

编辑 2: 好的,看来我需要更具体一些。我不是在寻找我发布的代码的实际问题。这只是我目前在工作流程方面的一个例子。这意味着我有窗口 HDC、屏幕外 HDC、屏幕外 HBITMAP 和指向我的像素的指针,比方说,假设的 R8G8B8A8 内存布局。我该如何处理这些对象?我是否通过 CreateDIBSection 创建另一个 HBITMAP 并将我的像素写入其中?之后我该怎么办?

编辑 3:请参阅 Barmak Shemirani 的回答以获得正确的解决方案(我的示例代码有问题)。另请查看 Paul Sanders 的回答以了解一些现代 WinAPI 技巧。

谢谢大家!

最佳答案

要打印 mypixels vector ,请使用 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;
}

使用内存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;
}

关于c++ - 设置窗口像素的正确(无闪烁)方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50656080/

相关文章:

c++ - 如何为 32 位位图生成单色位掩码

c++ - 提升精神: parse boolean expression and reduce to canonical normal form

c++ - const 转换 std 容器

c++ - 在 Folly 的无锁 SPSC 队列中使用 std::memory_order_consume

c++ - 在 c++ 的 SetWindowsHookEx 函数中

C++ WinHttp 获取响应头和正文

c# - .NET 中的透明控件

c# - 像图形软件一样绘制抗锯齿文本图像

c++ - 优先选择隐式转换而不是其他转换

c# - 不同的文本渲染方法不会产生我想要的