Подтвердить что ты не робот

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

Я использую GDI + в приложении С++/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);
}

Я что-то делаю неправильно? Или есть другой способ добиться этого?

4b9b3361

Ответ 1

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

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

  • Do-nothing 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.

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

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

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

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

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

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

Ответ 2

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

Ответ 4

Вы можете получить некоторую тягу, используя Direct3D, чтобы "рассказать" вам, когда происходит vsync, и др., поэтому вы можете BitBlt/update в удобное время. См. GDI vsync, чтобы избежать разрыва (хотя получение некоторых вещей до одного небольшого BitBlt может быть "достаточно хорошим" для некоторых случаев).

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

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

Ответ 5

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

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

Ответ 6

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

Если это ваш сценарий... с помощью Vistas новый аэродинамический облик поможет решить вашу проблему, поскольку оконный менеджер aero desktop автоматически компонует окна. С более старым оконным менеджером это лаваш.