Уменьшите мерцание с помощью GDI+ и C++
Я использую GDI+ в приложении C++/MFC, и я просто не могу избежать мерцания при изменении размера окна.
Я уже попробовал эти шаги:
- вернул TRUE
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);
}
Я делаю что-то неправильно? Или есть другой способ добиться этого?
6 ответов
Чтобы полностью избежать мерцания, вам нужно будет завершить все рисование в промежутке между обновлениями экрана. Windows не предоставляет каких-либо простых способов сделать это для обычной окраски окон (Vista предоставляет составной чертеж через DWM, но на это нельзя полагаться даже в системах, работающих под управлением Vista). Поэтому лучшее, что вы можете сделать, чтобы свести к минимуму мерцание, - это нарисовать все как можно быстрее (уменьшить разрывы, увеличивая ваши шансы на завершение всего рисования в цикле обновления), и избежать перерисовки (рисование части экрана, а затем рисование чего-то еще поверх вверху: рискует представить пользователю частично нарисованный экран).
Давайте обсудим методы, представленные здесь до сих пор:
Ничего не делать OnEraseBkgnd (): помогает избежать перерисовки, предотвращая заполнение недействительной области окна цветом фона окна. Полезно, когда вы будете рисовать всю область снова во время обработки WM_PAINT в любом случае, как в случае рисования с двойной буферизацией... но см. Примечания по предотвращению перерисовки путем предотвращения рисования после вашегометода WM_PAINT.
Возврат NULL для OnCtlColor (): на самом деле это ничего не должно делать... если у вас нет дочерних элементов управления в форме. В этом случае см. Примечания по предотвращению перерисовки путем предотвращения отрисовки после вашегометода WM_PAINT.
Рисование с двойной буферизацией: помогает избежать разрыва (и, возможно, перерисовывания), сводя фактическое рисование на экране к одному BitBLT. Хотя это может повредить время, необходимое для рисования: аппаратное ускорение не может быть использовано (хотя при использовании GDI+ шансы на использование любого аппаратного рисования весьма невелики), необходимо создавать и заполнять закадровое растровое изображение для каждой перерисовки, а также все окно должно быть перекрашено для каждой перерисовки. См. Примечания по эффективной двойной буферизации.
Использование вызовов GDI вместо GDI+ для BitBlt: это часто хорошая идея -
Graphics::DrawImage()
может быть очень медленным Я даже нашел нормальный GDIBitBlt()
призывать быть быстрее на некоторых системах. Поиграйте с этим, но только после того, как попробуете несколько других предложений.Избегание стилей класса окна, которыевызывают полную перерисовку при каждом изменении размера (CS_VREDRAW, CS_HREDRAW): это поможет, но только если вам не нужно перерисовывать все окно при изменении размера.
Примечания о том, как избежать перерасхода путем предотвращения рисования до вашего метода WM_PAINT
Когда все или часть окна будут признаны недействительными, они будут стерты и перекрашены. Как уже отмечалось, вы можете пропустить удаление, если планируете перекрасить всю недействительную область. Однако, если вы работаете с дочерним окном, то вы должны убедиться, что родительское окно (окна) также не стирает вашу область экрана. Стиль WS_CLIPCHILDREN должен быть установлен во всех родительских окнах - это предотвратитрисование областей, занятых дочерними окнами (включая ваш вид).
Замечания о том, как избежать оверлея путем предотвращения рисования после вашего методаWM_PAINT
Если у вас есть какие-либо дочерние элементы управления, размещенные в вашей форме, вы захотите использовать стильWS_CLIPCHILDREN, чтобы избежать их рисования (и, как следствие, их перерисовки. Имейте в виду, что это несколько повлияет на скорость процедуры BitBlt.
Замечания об эффективной двойной буферизации
Прямо сейчас вы создаете новое изображение заднего буфера каждый раз, когда представление отрисовывается само. Для больших окон это может представлять значительный объем выделяемой и освобождаемой памяти, что приведет к значительным проблемам с производительностью. Я рекомендую сохранять динамически размещаемое растровое изображение в вашем объекте представления, перераспределяя его по мере необходимости, чтобы соответствовать размеру вашего представления.
Обратите внимание, что при изменении размера окна это приведет к тому же количеству выделений, что и к существующей системе, поскольку для каждого нового размера требуется новое растровое изображение заднего буфера, чтобы соответствовать ему - вы можете немного облегчить боль, округлив размеры вверх до следующего наибольшего значения, кратного 4, 8, 16 и т. д., что позволяет избежать перераспределения при каждом крошечном изменении размера.
Обратите внимание, что если размер окна не изменился с момента последнего рендеринга в задний буфер, вам не нужно повторно визуализировать его, когда окно становится недействительным - просто удалите уже отрендеренное изображение на экран.
Кроме того, выделите растровое изображение, которое соответствует разрядности экрана. Конструктор дляBitmap
в настоящее время вы используете по умолчанию 32bpp, ARGB-макет; если это не соответствует экрану, то его придется конвертировать. Рассмотрите возможность использования метода GDICreateCompatibleBitmap()
чтобы получить соответствующее растровое изображение.
Наконец... Я предполагаю, что ваш пример кода является просто иллюстративным фрагментом. Но если вы на самом деле ничего не делаете, кроме того, что визуализируете существующее изображение на экране, вам вообще не нужно поддерживать резервный буфер - просто Blt непосредственно из изображения (и заранее конвертируйте формат изображения в соответствовать экрану).
Вы можете попытаться использовать старомодный GDI вместо GDI+ для записи в DC, тем более что вы уже буферизуете изображение. Используйте Bitmap::LockBits для доступа к необработанным данным растрового изображения, создайте структуру BITMAPINFO и используйте SetDIBitsToDevice для отображения растрового изображения.
Убедитесь, что класс окна для окна не включает флаги CS_VREDRAW и CS_HREDRAW в своем стиле.
См. http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx
Вы можете получить некоторую поддержку, используя Direct3D, чтобы "сообщить" вам, когда происходит vsync, и т. Д., Чтобы вы могли своевременно BitBlt / update. См. GDI vsync, чтобы избежать разрыва (хотя в некоторых случаях сведение всего к одному небольшому BitBlt может быть "достаточно хорошим").
Также обратите внимание, что похоже, что GDI BitBlt не синхронизирован с vsync экрана. Смотрите быстрее, чем BitBlt.
Также обратите внимание, что использование CAPTUREBLT (которое позволяет захватывать прозрачные окна) вызывает мерцание мыши (если аэро не используется), если используется.
Эта ссылка содержит некоторую полезную информацию: http://www.catch22.net/tuts/flicker-free-drawing
(Я знаю, что это очень позднее добавление в ветку, но это для любого (как я), кто нашел его, когда хотел уменьшить мерцание в моем приложении Win32...)
Есть ли дочерние окна в форме? Диспетчер окон начинает с того, что заставляет родительское окно стирать свой фон, отправляя сообщение WM_ERASEBKGND, а затем отправляет сообщение wM_PAINT - предположительно, это сопоставляется с вашим методом wx::OnDraw. Затем он перебирает каждый дочерний элемент управления и заставляет их рисовать себя.
Если это ваш сценарий... использование Vistas new aero look решит вашу проблему, так как оконный менеджер aero на рабочем столе автоматически создает окна. Со старым оконным менеджером это лаваш.