C++: избегайте мерцания с помощью сообщения WM_ERASEBKGND в приложении MFC.

в методе OnDraw() я создаю растровое изображение и выношу его на вывод каждый раз, когда изменяется размер окна:

void CmbmView::OnDraw(CDC* pDC)
{
  CRect WindowSize;
  HDC hdc;
  BITMAPINFO pbmi;
  HBITMAP hbm;
  CBitmap *pBitmap;
  CDC MemDC;
  void* ppvBits;

  GetClientRect(WindowSize);

  hdc = CreateDC (TEXT ("DISPLAY"), NULL, NULL, NULL) ;

  memset(&pbmi, 0, sizeof(BITMAPINFO));
  pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  pbmi.bmiHeader.biWidth = WindowSize.Width();
  pbmi.bmiHeader.biHeight = -WindowSize.Height(); // top down
  pbmi.bmiHeader.biPlanes = 1;
  pbmi.bmiHeader.biBitCount = 32;
  pbmi.bmiHeader.biCompression = BI_RGB;

  hbm = CreateDIBSection(hdc, &pbmi, DIB_RGB_COLORS, &ppvBits, NULL, NULL);
  pBitmap = CBitmap::FromHandle(hbm);

  MemDC.CreateCompatibleDC(pDC); 
  MemDC.SelectObject(pBitmap);

  // "Draw" into ppvBits
  GetDocument()->DrawApple(pDC, ppvBits, WindowSize.Width(), WindowSize.Height(), m_MaxIter, m_MaxBetragQuadrat, m_BW);

  // Blit it to the output
  pDC->BitBlt(0, 0, WindowSize.Width(), WindowSize.Height(), &MemDC, 0, 0, SRCCOPY);
}

Но каждый раз, когда приложению необходимо воссоздать растровое изображение в OnDraw(), экран становится белым до тех пор, пока не выведет растровое изображение на экран. Как я могу использовать сообщение WM_ERASEBKGND, чтобы избежать этого мерцания?


person viertro    schedule 31.10.2014    source источник


Ответы (2)


Я не знаю, с MFC, но с собственным Windows API вам нужно обработать сообщение WM_ERASEBKGND и просто return TRUE;. Это сообщает оконной процедуре по умолчанию, что сообщение обработано, поэтому фон окна стирается. В результате мерцание исчезает.

Кроме того, если вы используете функцию InvalidateRect(..), обязательно установите для параметра bErase значение FALSE. Обратите внимание, что параметр TRUE по умолчанию, если он не указан явно.

person Lukas Thomsen    schedule 31.10.2014
comment
Вот версия MFC. - person Vincent; 31.10.2014

В приложении, где рендеринг содержимого окна занимал нетривиальное количество времени, я предпринял следующие шаги:

  1. когда экран нуждается в перерисовке, скопируйте его содержимое из растрового изображения
  2. если базовые данные изменяются, запустите поток, чтобы преобразовать эти данные в новое растровое изображение (если этот поток уже запущен, просто установите флаг)
  3. когда поток рендеринга завершается, замените сохраненное растровое изображение результатом потока и сделайте окно недействительным (если установлен соответствующий флаг, немедленно перезапустите рендеринг)
  4. когда размер окна изменяется, запускайте рендеринг и растягивайте растровое изображение в окно
  5. когда представление прокручивается, разбивайте те части, которые доступны, и запускайте рендеринг

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

  • Не запускайте одновременно несколько потоков в фоновом режиме, так как это может снизить производительность. Если вы просто растягиваете окно с помощью мыши, вы можете легко генерировать десятки сообщений об изменении размера, и вы не хотите тратить на это время и объем памяти.
  • Отрисовывайте только видимые части, так как с виртуальными размерами прокрутки растровое изображение может стать очень большим. Чтобы упростить прокрутку, вы можете добавить рамку (например, 1/5 ширины/высоты), чтобы сохранить доступ к некоторым дополнительным данным при подготовке нового растрового изображения в фоновом режиме.
person Ulrich Eckhardt    schedule 31.10.2014