Уменьшите мерцание с помощью GDI + и C ++

Я использую GDI + в приложении C ++ / MFC, и мне просто не удается избежать мерцания при изменении размера окна.

Я уже пробовал эти шаги:

  • вернул ИСТИНА на OnEraseBkGnd();
  • вернул NULL на OnCtlColor();
  • используется двойная буферизация по этому коду:

void vwView::OnDraw(CDC* pDC) 
{
   CRect rcClient;
   GetClientRect(rcClient);

   Bitmap bmp(rcClient.Width(), rcClient.Height());
   Graphics graphics(&bmp);

   graphics.DrawImage(m_image, rcClient.left, rcClient.top);

   Graphics grph(pDC->m_hDC);
   grph.DrawImage(&bmp, 0, 0);
}

Am I doing something wrong? Or is there another way to achieve this?


person djeidot    schedule 13.10.2008    source источник


Ответы (6)


Чтобы полностью избежать мерцания, вам нужно будет завершить все рисование в интервале между обновлениями экрана. Windows не предоставляет никаких простых средств для выполнения этого при обычном рисовании окон (Vista предоставляет составное рисование через DWM, но на это нельзя положиться даже в системах под управлением Vista). Следовательно, лучшее, что вы можете сделать, чтобы минимизировать мерцание, - это рисовать все как можно быстрее (уменьшить разрыв, увеличивая ваши шансы на завершение всего рисования в цикле обновления), и избегать перерисовки (рисование части screen, а затем нарисовать что-то еще поверх: рискует представить пользователю частично нарисованный экран).

Давайте обсудим представленные здесь техники:

  • Ничего не делать OnEraseBkgnd (): помогает избежать чрезмерного рисования, предотвращая заполнение недействительной области окна цветом фона окна. Полезно, когда вы будете снова рисовать всю область во время WM_PAINT обработки в любом случае, как в случае рисования с двойной буферизацией ... но см. Примечания по предотвращению перерисовки предотвращая рисование после вашего WM_PAINT метода.

  • Возвращение NULL для OnCtlColor (): на самом деле это не должно делать ничего ... если у вас нет дочерних элементов управления в вашей форме. В этом случае см. Примечания по предотвращению перерисовки путем предотвращения рисования после вашего WM_PAINT метода.

  • Рисование с двойной буферизацией: помогает избежать разрывов (а также потенциально перерисовки) за счет сокращения фактического рисования на экране до одного BitBLT. Однако это может сократить время, необходимое для рисования: аппаратное ускорение не может использоваться (хотя с GDI + шансы использования любого аппаратного рисования довольно малы), растровое изображение за пределами экрана должно создаваться и заполняться для каждой перерисовки, а все окно необходимо перекрашивать при каждой перерисовке. См. Примечания по эффективной двойной буферизации.

  • Использование вызовов GDI вместо GDI + для BitBlt: часто это хорошая идея - Graphics::DrawImage() может быть очень медленным. Я даже обнаружил, что обычный вызов GDI BitBlt() быстрее на некоторых системы. Поиграйте с этим, но только предварительно попробовав несколько других предложений.

  • Избегайте стилей классов окон, которые заставляют полностью перерисовывать при каждом изменении размера (CS_VREDRAW, CS_HREDRAW): это поможет, но только если вы этого не сделаете. необходимо перерисовывать все окно при изменении размера.

Примечания по предотвращению перерисовки путем предотвращения рисования до вашего метода WM_PAINT

Когда все или часть окна становится недействительным, оно стирается и перекрашивается. Как уже отмечалось, вы можете пропустить стирание, если планируете перекрашивать всю недопустимую область. Однако, если вы работаете с дочерним окном, вы должны убедиться, что родительские окна также не стирают вашу область экрана. Стиль WS_CLIPCHILDREN должен быть установлен для всех родительских окон - это предотвратит рисование областей, занятых дочерними окнами (включая ваш вид).

Примечания по предотвращению перерисовки путем предотвращения рисования после вашего метода WM_PAINT

Если у вас есть какие-либо дочерние элементы управления, размещенные в вашей форме, вы захотите использовать стиль WS_CLIPCHILDREN, чтобы не рисовать поверх них (и впоследствии не рисоваться ими. Имейте в виду, это несколько повлияет на скорость работы BitBlt.

Примечания по эффективной двойной буферизации

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

Обратите внимание, что при изменении размера окна это приведет к тому же количеству выделений, что и в существующей системе, поскольку каждый новый размер потребует выделения нового растрового изображения заднего буфера, чтобы соответствовать ему - вы можете немного облегчить боль, округлив размеры в большую сторону. до следующего по величине числа, кратного 4, 8, 16 и т. д., что позволяет избежать перераспределения при каждом крошечном изменении размера.

Обратите внимание, что если размер окна не изменился с момента последнего рендеринга в задний буфер, вам не нужно повторно рендерить его, когда окно становится недействительным - просто перенесите уже отрендеренное изображение на экран.

Также выделите растровое изображение, соответствующее разрядности экрана. Конструктор для Bitmap, который вы сейчас используете, по умолчанию будет 32bpp, ARGB-layout; если это не соответствует экрану, то его придется преобразовать. Рассмотрите возможность использования метода GDI CreateCompatibleBitmap() для получения соответствующего растрового изображения.

Наконец ... Я предполагаю, что ваш пример кода - это просто иллюстративный фрагмент. Но если вы на самом деле ничего не делаете, кроме рендеринга существующего изображения на экран, тогда вам вообще не нужно поддерживать задний буфер - просто Blt прямо из изображения (и заранее преобразуйте формат изображения в соответствовать экрану).

person Shog9    schedule 14.10.2008
comment
Так что сокращения фактического рисования на экране до одного BitBLt на самом деле недостаточно, чтобы предотвратить разрывы? Как окна могут позволить этому одиночному битовому преобразованию происходить в середине обновления экрана, позволяя разрывать, даже если это единственная операция? - person rogerdpack; 09.05.2012
comment
Конечно. Для передачи фактических данных требуется время, которое во многом зависит от настройки конкретной машины. - person Shog9; 09.05.2012

Вы можете попробовать использовать старомодный GDI вместо GDI + для записи в DC, тем более, что вы уже буферизуете изображение. Используйте Bitmap :: LockBits для доступа к необработанным данным растрового изображения, создайте структуру BITMAPINFO и используйте SetDIBitsToDevice для отображения растрового изображения.

person Mark Ransom    schedule 13.10.2008

Убедитесь, что класс окна для окна не включает в свой стиль флаги CS_VREDRAW и CS_HREDRAW.

См. http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx.

person Frederik Slijkerman    schedule 13.10.2008
comment
Похоже, описания стилей окна были перемещены в Стили классов окон. - person jrh; 11.06.2018

Вы можете получить некоторую поддержку, используя Direct3D, чтобы «сообщить» вам, когда происходит vsync, и т. Д., Поэтому вы можете BitBlt / обновить в удобное время. См. GDI vsync, чтобы избежать разрывов < / em> (хотя в некоторых случаях «достаточно хорошо» свести все к одному маленькому BitBlt).

Также обратите внимание, что похоже, что GDI BitBlt не синхронизируется с vsync экрана. См. Быстрее, чем BitBlt.

Также обратите внимание, что использование CAPTUREBLT (которое позволяет захватывать прозрачные окна) вызывает мерцание мыши (если аэро не используется), если используется.

person rogerdpack    schedule 09.05.2012
comment
Последнее сообщение на форуме Faster than BitBlt кажется, что он недоступен с апреля 2015 года. - person jrh; 11.06.2018

Эта ссылка содержит полезную информацию: http://www.catch22.net/tuts/flicker-free-drawing

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

person Liam    schedule 30.08.2013
comment
Это более или менее ссылка, отвечающая только на который не рекомендуется. По состоянию на 11.06.2018 это похоже на ответ только на мертвую ссылку, хотя читатели должны отметить, что сайт не обслуживался все это время (действовало в 2017 году). Лично меня это устраивает, на странице есть полезные советы. Надеюсь, он скоро вернется. Я рекомендую в любом случае включать в ответ контент со страницы (своими словами). - person jrh; 11.06.2018

Есть ли в форме дочерние окна? Диспетчер окон начинает с того, что заставляет родительское окно стереть свой фон, отправляя сообщение WM_ERASEBKGND, ЗАТЕМ оно отправляет сообщение wM_PAINT - предположительно, это отображается на ваш метод wx :: OnDraw. Затем он перебирает каждый дочерний элемент управления и заставляет их раскрашивать себя.

Если это ваш сценарий ... использование Vistas new aero look решит вашу проблему, поскольку оконный менеджер рабочего стола aero выполняет компоновку окон автоматически. Со старым оконным менеджером это лаваш.

person Chris Becke    schedule 13.10.2008