Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.50/18: Рейтинг темы: голосов - 18, средняя оценка - 4.50
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
1

Реализация двойной буферизации для рисования графических примитивов

19.11.2018, 23:56. Показов 3231. Ответов 12
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Решил я значит порисовать всяких кружков-квадратиков, а они мерцают. Убрал эффект наполовину, перестав обрабатывать WM_ERASEBKGND. Однако мерцание не ушло, ушел только белый цвет фона. Нашел, что проблему можно решить двойной буферизацией, но у меня не очень-то получилось((( вот код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);//получение контекста
            hdcMemDC = CreateCompatibleDC(hdc);
 
 
 
            GetClientRect(hwnd,&r);//работа с рабочей областью
            cx=r.right;
            cy=r.bottom;
 
            background.top=0;//объявление фонового прямоугольника
            background.bottom=cy-1;
            background.right=cy-1;
            background.left=0;
 
          //  br = CreateSolidBrush(RGB(0,0,140));
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(0,0,140)));
 
            Rectangle(hdcMemDC, 0, 0, cy-1, cy-1);//рисуем фоновый прямоугольник
            //FillRect(hdc,&background,br);
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(100,0,0)));
 
            Ellipse(hdcMemDC, cy/2 - 10, cy/2 - 10, cy/2 +10, cy/2+10);
 
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            BitBlt(hdc, 0, 0, cx, cy, hdcMemDC, 0, 0, SRCCOPY);
 
            DeleteDC(hdcMemDC);
            EndPaint(hwnd, &ps);
            InvalidateRect(hwnd, NULL, NULL);
 
            break;
           case  WM_ERASEBKGND :
          break;
Почему-то ничего не рисуется, просто белое окно... Что не так?
Спасибо!
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
19.11.2018, 23:56
Ответы с готовыми решениями:

Основные методы для рисования графических примитивов в Windows Forms
Назвать основные методы для рисования графических примитивов (Windows Forms)

Библиотека C для рисования графических примитивов в буфер или файл
Коллеги, подскажите, пожалуйста ответ на мой вопрос! У меня такая задача - рисовать различные...

Реализация двойной буферизации
Есть pictureBox создаваемый в коде. Я реализовал возможность его передвижения мышкой, но проблема в...

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

12
131 / 116 / 25
Регистрация: 03.05.2017
Сообщений: 336
Записей в блоге: 1
20.11.2018, 01:10 2
Пример…
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
            hdc = BeginPaint(hwnd, &ps);//получение контекста
            hdcMemDC = CreateCompatibleDC(hdc);
 
 
            GetClientRect(hwnd,&r);//работа с рабочей областью
            cx=r.right;
            cy=r.bottom;
 
            background.top=0;//объявление фонового прямоугольника
            background.bottom=cy-1;
            background.right=cy-1;
            background.left=0;
 
            oldbm = SelectObject(hdcMemDC, CreateCompatibleBitmap(hdc, r.right,r.bottom)); // Создаём память буфера
 
            //  br = CreateSolidBrush(RGB(0,0,140));
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(0,0,140)));
 
            Rectangle(hdcMemDC, 0, 0, cy-1, cy-1);//рисуем фоновый прямоугольник
            //FillRect(hdc,&background,br);
            //  DeleteObject(SelectObject(hdcMemDC,oldbr));  // Необязательно удалять, если …
 
            DeleteObject(SelectObject(hdcMemDC,CreateSolidBrush(RGB(100,0,0)))); // … кисть снова обновляем
 
            Ellipse(hdcMemDC, cy/2 - 10, cy/2 - 10, cy/2 +10, cy/2+10);
 
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            BitBlt(hdc, 0, 0, cx, cy, hdcMemDC, 0, 0, SRCCOPY);
 
            DeleteObject(SelectObject(hdcMemDC, oldbm)); // Удаляем память буфера
 
            DeleteDC(hdcMemDC);
            EndPaint(hwnd, &ps);
            InvalidateRect(hwnd, NULL, NULL);
1
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
20.11.2018, 01:59 3
Лучший ответ Сообщение было отмечено sad_guy как решение

Решение

Хотелось бы добавить от себя несколько замечаний. Первое это использование в WM_PAINT функции InvalidateRect. Это зло, не делайте так, попадёте в ад. Ну и второе, это использование кистей для рисования. Со своего опыта могу сказать, что с увеличением строк кода, будет всё запутаннее читать такое.
Вот как я использую двойную буферизацию (типа шаблона)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    PAINTSTRUCT ps;
    HDC hDC = BeginPaint(hWnd, &ps);
    RECT rc;
    GetClientRect(hWnd, &rc);
    const int iWindowWidth = rc.right - rc.left;
    const int iWindowHeight = rc.bottom - rc.top;
    HDC hMemDC = CreateCompatibleDC(hDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hDC, iWindowWidth, iWindowHeight);
    HBITMAP hOldBitmap = SelectBitmap(hMemDC, hBitmap);
    FillRect(hMemDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 11));
 
    int iOldBkMode = SetBkMode(hMemDC, TRANSPARENT);
    
    // Здесь уже рисование.
 
    SetBkMode(hMemDC, iOldBkMode);
 
    BitBlt(hDC, 0, 0, iWindowWidth, iWindowHeight, hMemDC, 0, 0, SRCCOPY);
    SelectBitmap(hMemDC, hOldBitmap);
    DeleteBitmap(hBitmap);
    DeleteDC(hMemDC);
    EndPaint(hWnd, &ps);
1
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
20.11.2018, 13:39  [ТС] 4
Я поставил invalidate rect в вм паинт для постоянной перерисовки - планируется динамичная картинка. Можно сделать лучше?
ЗЫ : за шаблон спасибо)
0
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
20.11.2018, 15:07 5
Цитата Сообщение от sad_guy Посмотреть сообщение
Можно сделать лучше?
Лучше поставить таймер, и перерисовывать через определённые промежутки времени.
0
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
20.11.2018, 21:02  [ТС] 6
Попробовал сделать с таймером:
(решил каждую секунду менять размер круга, _size..., создавал таймер при wm create)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
case WM_CREATE:
 
            SetTimer(hwnd, 1, 100, NULL);//установка таймера
 
            break;
        case WM_TIMER:
            if(_size == 10) _size=20;
            else _size = 10;
            InvalidateRect(hwnd, NULL, true);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);//получение контекста
            hdcMemDC = CreateCompatibleDC(hdc);
 
 
 
            GetClientRect(hwnd,&r);//работа с рабочей областью
            cx=r.right;
            cy=r.bottom;
 
            background.top=0;//объявление фонового прямоугольника
            background.bottom=cy-1;
            background.right=cy-1;
            background.left=0;
 
            oldbm = SelectObject(hdcMemDC, CreateCompatibleBitmap(hdc, cx, cy));
 
          //  br = CreateSolidBrush(RGB(0,0,140));
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(0,0,140)));
 
            Rectangle(hdcMemDC, 0, 0, cy-1, cy-1);//рисуем фоновый прямоугольник
            //FillRect(hdc,&background,br);
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(100,0,0)));
 
            Ellipse(hdcMemDC, cy/2 - _size, cy/2 - _size, cy/2 +_size, cy/2+_size);
 
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            BitBlt(hdc, 0, 0, cx, cy, hdcMemDC, 0, 0, SRCCOPY);
 
            DeleteObject(SelectObject(hdcMemDC,oldbm));
 
            DeleteDC(hdcMemDC);
            EndPaint(hwnd, &ps);
            //InvalidateRect(hwnd, NULL, NULL);
 
            break;
почему-то не работает... Может я неправильный timer id сделал?

Добавлено через 11 минут
Он не заходит в вм таймер, т.е. проблема с установленным таймером
0
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
20.11.2018, 23:43 7
Значит смотрите
C++
1
SetTimer(hwnd, 1, 100, NULL);
100 - это не секунда, 1000 - это секунда (то есть 1000 миллисекунд).

Если используете двойную буферизацию, то третьим параметром InvalidateRect должно быть false, а не true как у Вас в коде.

Цитата Сообщение от sad_guy Посмотреть сообщение
Может я неправильный timer id сделал?
Нет, нормальный.
0
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
21.11.2018, 09:14  [ТС] 8
Нет, это не помогло. Я перепробовал кучу вариантов реализации, даже брейк убирал для провала в паинт, но ничего не помогло. Отсюда выходит, что нет сообщений вм таймер. Однако проверка мессадж боксами показала, что все и с таймером и с его сообщениями нормально... Может проблема с вм паинтом? Когда я поставил мессадж бокс в него, ничего не произошло, только колесо загрузки зачастило. ТОкда я убрал бреак из вм_таймер, но ничего не поменялось. Лажа в обработке паинта. Убрал из него invalidate rect. (насколько я понял, происходило зацикливание - инвалидайт вызывал помещение вм_паинта в очередь "в обход ее" из-за этого сообщения таймера глохли где-то сзади). Теперь началась ежесекундная обработка вм_паинт, но картинка не меняется
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
case WM_PAINT:
         // MessageBox(NULL, "wm_paint", "no error", MB_OK);
            hdc = BeginPaint(hwnd, &ps);//получение контекста
            hdcMemDC = CreateCompatibleDC(hdc);
 
 
 
            GetClientRect(hwnd,&r);//работа с рабочей областью
            cx=r.right;
            cy=r.bottom;
 
            background.top=0;//объявление фонового прямоугольника
            background.bottom=cy-1;
            background.right=cy-1;
            background.left=0;
 
            oldbm = SelectObject(hdcMemDC, CreateCompatibleBitmap(hdc, cx, cy));
 
          //  br = CreateSolidBrush(RGB(0,0,140));
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(0,0,140)));
 
            Rectangle(hdcMemDC, 0, 0, cy-1, cy-1);//рисуем фоновый прямоугольник
            //FillRect(hdc,&background,br);
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            oldbr = SelectObject(hdcMemDC,CreateSolidBrush(RGB(100,0,0)));
 
            Ellipse(hdcMemDC, cy/2 - _size, cy/2 - _size, cy/2 +_size, cy/2+_size);
 
            DeleteObject(SelectObject(hdcMemDC,oldbr));
 
            BitBlt(hdc, 0, 0, cx, cy, hdcMemDC, 0, 0, SRCCOPY);
 
            DeleteObject(SelectObject(hdcMemDC,oldbm));
 
            DeleteDC(hdcMemDC);
            EndPaint(hwnd, &ps);
         //   InvalidateRect(hwnd, NULL, NULL);
 
            break;
Что может быть не так? полагаю, лажа где-то в рисовании или какая-то особенность работы с двойной буферизацией
0
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
21.11.2018, 10:04 9
Наверное Вам нужно так
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
static LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static int _size;
    switch (msg)
    {
        case WM_CREATE:
        {
            _size = 10;
            SetTimer(hWnd, 1, 1000, NULL);
            return TRUE;
        }
        break;
        
        case WM_TIMER:
        {
            if (_size == 10)
                _size=20;
            else
                _size = 10;
            InvalidateRect(hWnd, NULL, FALSE);
        }
        break;
        
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hDC = BeginPaint(hWnd, &ps);
            RECT rc;
            GetClientRect(hWnd, &rc);
            const int iWindowWidth = rc.right - rc.left;
            const int iWindowHeight = rc.bottom - rc.top;
            HDC hMemDC = CreateCompatibleDC(hDC);
            HBITMAP hBitmap = CreateCompatibleBitmap(hDC, iWindowWidth, iWindowHeight);
            HBITMAP hOldBitmap = SelectBitmap(hMemDC, hBitmap);
            
            HBRUSH hBrush, hOldBrush;
            
            hBrush = CreateSolidBrush(RGB(0, 0, 140));
            FillRect(hMemDC, &ps.rcPaint, hBrush);
            DeleteBrush(hBrush);
            
            hOldBrush = SelectBrush(hMemDC, CreateSolidBrush(RGB(100, 0, 0)));
            Ellipse(hMemDC, iWindowWidth / 2 - _size, iWindowHeight / 2 - _size, iWindowWidth / 2 + _size, iWindowHeight / 2 + _size);
 
            DeleteObject(SelectObject(hMemDC, hOldBrush));
            
            BitBlt(hDC, 0, 0, iWindowWidth, iWindowHeight, hMemDC, 0, 0, SRCCOPY);
            SelectBitmap(hMemDC, hOldBitmap);
            DeleteBitmap(hBitmap);
            DeleteDC(hMemDC);
            EndPaint(hWnd, &ps);
        }
        break;
        
        case WM_DESTROY:
        {
            KillTimer(hWnd, 1);
            PostQuitMessage(0);
        }
        break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}
Добавлено через 1 минуту
Не знаю что в Вашем коде не так. Вы всё время кидаете код кусками и ждёте помощи. Здесь нет телепатов.
1
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
21.11.2018, 10:08  [ТС] 10
Решил я чекнуть значение _size, в конце обработки поставил простую проверку с мессадж боксами. Выяснилось, что значение всегда 20....
0
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
21.11.2018, 10:11 11
Проверьте код, который я сбросил и сравните со своим.
0
...
4 / 4 / 1
Регистрация: 10.11.2017
Сообщений: 151
21.11.2018, 10:14  [ТС] 12
О ДА, ОНО РАБОТАЕТ)))))))) Спасибо большое! Глупая лажа получилась(

Добавлено через 2 минуты
я объявлял просто int переменную и присваивал ей значение в начале функции. Получается, она каждый раз пересоздавалась.
0
Software Developer
315 / 229 / 113
Регистрация: 03.05.2017
Сообщений: 1,330
21.11.2018, 10:16 13
Цитата Сообщение от sad_guy Посмотреть сообщение
я объявлял просто int переменную и присваивал ей значение в начале функции. Получается, она каждый раз пересоздавалась.
Вот видите, а нужно было просто объявить как static или сделать глобальной.
0
21.11.2018, 10:16
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
21.11.2018, 10:16
Помогаю со студенческими работами здесь

Что использовать для рисования примитивов
Ребят подскажите на чем можно программно порисовать? желательно на с++ в 12 визуалке для WinForm....

Использование графических примитивов для создания статических и динамических изображений
Здравствуйте! А кто-то уже делал нечто подобное в Делфи? Лично у меня фантазии вообще нету, дабы...

Ускорение двойной буферизации
Недавно вычитал что при создании 2 буфера лучше использовать CreateDIBSection вместо...

Мерцание при двойной буферизации
Вот пытаюсь сделать что-то вроде игры. не могу убрать мерцание. двойную буферизацию пробовал, не...

Рисование графика с использованием двойной буферизации
Добрый день. Делаю программу, которая будет в себе содержать динамический график. Не знаю, как...

Библиотека для рисования графических объектов в С++
Подскажите пожалуйста библиотеку, аналог graphics.h для С++, просто я пишу в Visual Studio 2010 и...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru