Правильный (и без мерцания) способ установки пикселей окна? - PullRequest
0 голосов
/ 02 июня 2018

Я изо всех сил пытаюсь найти правильный способ выгрузки массива простых значений RGBA в клиентскую область окна Win32 во время WM_PAINT.У меня есть следующий код, но он уже кажется запутанным, и я даже не закончил:

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

У меня есть некоторая комната для маневра в отношении формата памяти исходного изображения, поэтому я могу сделать так, чтобы это совпадало с любымцель требует.

Я делаю это правильно?Есть ли лучший способ?

PS: Очевидно, я буду хранить и не воссоздавать большинство объектов каждый раз, когда появляется WM_PAINT.Это только пример / WIP.

Редактировать: Добавлена ​​обработка WM_ERASEBKGND.

Редактировать 2: Хорошо, похоже, мне нужноболее конкретно.Я не ищу актуальные проблемы с кодом, который я разместил.Это всего лишь пример того, что я имею до сих пор с точки зрения рабочего процесса.Это означает, что у меня есть окно HDC, вне экрана HDC, вне экрана HBITMAP и указатель на мои пиксели, которые, скажем, в гипотетической схеме памяти R8G8B8A8.Что мне делать с этими объектами?Могу ли я создать еще один HBITMAP через CreateDIBSection и записать в него свои пиксели?Что мне с ним делать после?

Редактировать 3: См. Ответ Бармака Шемирани для правильного решения (в моем примере кода есть проблемы).Посмотрите также ответ Пола Сандерса о некоторых современных советах по WinAPI.

Спасибо всем!

Ответы [ 2 ]

0 голосов
/ 02 июня 2018

Для печати 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;
}

Использование памяти 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;
}
0 голосов
/ 02 июня 2018

Что касается рисования без мерцания, Vista и более поздние версии имеют поддержку двойной буферизации, встроенную в Win32 API.Я адаптировал приведенный ниже код из этой статьи .Больше информации на MSDN .Ответ Бармака показывает вам, как рисовать ваши пиксели.

Инициализация (для потока):

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

Ничего, на самом деле.

...