Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Плавная перерисовка экрана, Реализация плавной перерисовки экрана 
V
    Опции темы
MironenkoDima
Дата 15.6.2013, 03:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Здравствуйте.
Подскажите, пожалуйста, как реализовать плавное обновление экрана в программе.
Вся графика выводится с использованием библиотеки GDI+. Программа позволяет перемещаться по рабочей области с помощью зажатия курсора мыши и т.п. манипуляции, при таком подходе экран мерцает при перерисовки. Как это можно преодолеть?
PM MAIL   Вверх
Dem_max
Дата 15.6.2013, 06:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Двойное буфферизирование ?????


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Earnest
Дата 15.6.2013, 17:25 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



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


--------------------
...
PM   Вверх
MironenkoDima
Дата 15.6.2013, 17:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Earnest, спасибо за развёрнутый ответ!
Буду копать дальше )
PM MAIL   Вверх
MironenkoDima
Дата 19.6.2013, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Под руки попалась книжка "Компьютерная графика" Виктора Порева. В ней нашёл кое-что про двойную буферизацию.
Рассмотрено такое применение:
Код
void DrawStudyExample(HWND hWnd)
{
    HDC hdcWin, hdc;
    HBITMAP hBitmap;

    hdcWin = GetDC(hWnd);
    hdc = CreateCompatibleDC(winhdc);
    hBitmap = CreateCompatibleBitmap(winhdc, 400, 300);
    SelectObject(hdc, hBitmap);
    Rectangle(hdc, -1, -1, 401, 301); // очистка
    // тут рисование
    BitBlt(hdcWin, 0, 0, 400, 300, hdc, 0, 0, SRCCOPY);
    DeleteDC(hdc);
    DeleteObject(hBitmap);
    ReleaseDC(hWnd, hdcWin);
}


Попробовал я сделать подобное в своём проекте:
Код

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC winhdc, hdc;
    HBITMAP hBitmap;
    RECT rect;

    winhdc = BeginPaint(hWnd, &ps);
    hdc = CreateCompatibleDC(winhdc);

    GetClientRect(hWnd, &rect);
    hBitmap = CreateCompatibleBitmap(winhdc, rect.right, rect.bottom);
    SelectObject(hdc, hBitmap);
    Rectangle(hdc, -1, -1, rect.right+1, rect.bottom+1);
        
    DrawGraphic(winhdc, hdc, rect);
            
    DeleteDC(hdc);
    DeleteObject(hBitmap);
    EndPaint(hWnd, &ps);
            
    return 0;
}

void DrawGraphic(HDC winhdc, HDC hdc, RECT rect)
{
    Graphics graphics(hdc);
    // рисование
    BitBlt(winhdc,0,0,rect.right,rect.bottom, hdc,0,0,SRCCOPY);
}


К сожалению не помогло.. Не подскажите, что можно изменить?
PM MAIL   Вверх
Dem_max
Дата 20.6.2013, 03:57 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Код

HDC g_hdcWin,g_ hdcMem;
HBITMAP g_hBitmap, g_hOldBitmap;

void Init(HWND hWnd)
{
    GetClientRect(hWnd, &rect);
    g_hdcWin = GetDC(hWnd);
    g_hdcMem = CreateCompatibleDC(g_hdcWin);
    g_hBitmap = CreateCompatibleBitmap(g_hdcWin, rect.right, rect.bottom);
    g_hOldBitmap = (HBITMAP)SelectObject(g_hdcMem, g_hBitmap);
}

void UnInit(HWND hWnd)
{
    SelectObject(g_hdcMem, g_hOldBitmap);
    DeleteObject(g_hBitmap);
    DeleteDC(g_hdcMem);
    ReleaseDC(hWnd, g_hdcWin);
}

void DrawStudyExampleToMemory(HWND hWnd)
{
    // тут рисование
    GetClientRect(hWnd, &rect);
    Rectangle(g_hdcMem, 0, 0, rect.right, rect.bottom); // очистка
    
}

case WM_PAINT:
{
    PAINTSTRUCT ps = {0};
    HDC hDC = BeginPaint(hwnd, &ps);
    GetClientRect(hwnd, &rect);
    BitBlt(hDC, 0, 0, rect.right, rect.bottom, g_hdcMem, 0, 0, SRCCOPY);
    EndPaint(hwnd, &ps);
    return 0;
}



--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Earnest
Дата 20.6.2013, 05:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Не помогло в каком смысле? Не рисуется? По прежнему мерцает?


--------------------
...
PM   Вверх
MironenkoDima
Дата 20.6.2013, 18:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Цитата(Earnest @  20.6.2013,  05:00 Найти цитируемый пост)
Не помогло в каком смысле? Не рисуется? По прежнему мерцает?

Рисует, но по-прежнему мерцает.


Dem_max, не совсем понял, в какое место Init и UnInit вставлять? Рабочая область у меня меняется, т.е. пользователь может увеличить/уменьшить размеры. Неужели в WM_SIZE постоянно обрабатывать?

И правильно ли я понял, что перед всеми InvalidateRect'ов нужно вызывать функцию DrawStudyExampleToMemory(из вашего примера)? А в WM_PAINT'е оставить только функцию переноса BitBlt ?


-----------------------------------

Так-с, попробовал ваш пример. В принципе, мерцаний стало гораздо меньше, только теперь пропала плавность. Изменения происходят с дёрганием

Это сообщение отредактировал(а) MironenkoDima - 20.6.2013, 18:59
PM MAIL   Вверх
Dem_max
Дата 20.6.2013, 19:49 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Цитата

в какое место Init и UnInit вставлять?

в начале и конце программы.


Цитата

Рабочая область у меня меняется, т.е. пользователь может увеличить/уменьшить размеры. Неужели в WM_SIZE постоянно обрабатывать?

Да. И постоянно вызывать UnInit() а потом Init().

Цитата

А в WM_PAINT'е оставить только функцию переноса BitBlt ?

WM_PAINT не трогай, что я нарисовал то и вставляй.

Цитата

И правильно ли я понял, что перед всеми InvalidateRect'ов нужно вызывать функцию DrawStudyExampleToMemory(из вашего примера)?

нет, не обязательно. InvalidateRect 'bh всего лишь одна из функций которая заставляет вызвать WM_PAINT

DrawStudyExampleToMemory() вызывается в любой момент времени после Init() и пока не наступит UnInit()


Это сообщение отредактировал(а) Dem_max - 20.6.2013, 20:20


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
MironenkoDima
Дата 21.6.2013, 00:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Вставил вызов Init в WM_CREATE, вызов UnInit в WM_DESTROY,  пару UnInit+Init в WM_SIZE. Обработку WM_PAINT скопировал из вашего примера.
Все прошлые вызовы InvalidateRect заменил на вызов функции, в которой рисуется вся графика, а в конец этой функции поставил InvalidateRect.
Как сказал выше, теперь пропала плавность перерисовки..
PM MAIL   Вверх
Dem_max
Дата 21.6.2013, 03:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Как может пропасть "плавность" перерисовки если в WM_PAINT только 1 функция и то самая быстрая для переноса графики ????
Код

case WM_PAINT:
{
    PAINTSTRUCT ps = {0};
    HDC hDC = BeginPaint(hwnd, &ps);
    // Попробуй GetClientRect вынести за пределы этой функции
    GetClientRect(hwnd, &rect);

    // !!! это самая быстрая функция переноса данных, выполняется аппаратно.
    BitBlt(hDC, 0, 0, rect.right, rect.bottom, g_hdcMem, 0, 0, SRCCOPY);
    EndPaint(hwnd, &ps);
    return 0;
}




--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
MironenkoDima
Дата 21.6.2013, 04:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 12.6.2013

Репутация: нет
Всего: нет



Попробовал. Сделал rect - глобальной. И поместил GetClientRect в Init. Не помогло..

Может у меня нарушена "общая" логика обновления, но тут я даже не знаю, как можно по-другому.
Всякий раз, когда мне нужно обновить экран, вызываю свою функцию рисования. В ней создаётся класс Graphics(gdi+) по дескриптору на контекст граф.устройства, который создаём в памяти(g_hdcMem из примера). Используются различные методы класса Graphics(не мало методов). В конце этой функции находится InvalidateRect..
PM MAIL   Вверх
Dem_max
Дата 21.6.2013, 06:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Кидай сюда сокращенный код(проект) я посмотрю где накосячил.
P.S. ну и расскажи что я должен увидеть и как ты определил что все не плавно.

Это сообщение отредактировал(а) Dem_max - 21.6.2013, 06:26


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Earnest
Дата 21.6.2013, 07:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



MironenkoDima, за мерцание может отвечать стирание фона. По-умолчанию оно почти всегда есть. Бороться 2 способами: при регистрации класса окна задать нулевую кисть, либо перехватить WM_ERASEBKGND и ничего не делать (и вернуть TRUE... или FALSE... не помню, в MSDN смотри, там написано, чтобы система не лезла).
Что касается принципа двойной буферизации, то он таков: на WM_PAINT ты практически всегда  ТОЛЬКО копируешь на экран внеэкранный буфер. Рисовать во внеэкранный буфер можно в любой момент (и даже в отдельном потоке). Хотя можно и на PAINT, но совсем не каждый раз, а только при необходимости перерисовать внутренний буфер. Например, если твое окно временно перекрылось другим, при восстановлении буфер перестраивать не нужно.

Бывают случаи, когда рисование происходит как бы двумя частями: на относительно постоянном фоне рисуют какие-то детали. Например, у тебя сложный чертеж, а ты добавляешь линию. Или нужно показать какой-то объект выделенным. Очевидно, что в этом случае неэффективно рисовать все целиком, чтобы добавить (или стереть) фрагмент линии. Тогда либо переменную часть рисуют прямо на экран, либо держат 2 буфера.

Как-то так. Надеюсь, принцип понятен.



--------------------
...
PM   Вверх
Dem_max
Дата 21.6.2013, 10:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Сделай так
Код

    WNDCLASSEX wcex = {0};

    wcex.cbSize          = sizeof(WNDCLASSEX);
    wcex.style           = 0    ;//CS_HREDRAW | CS_VREDRAW;  <--------------- !!!!



--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0644 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.