Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.73/78: Рейтинг темы: голосов - 78, средняя оценка - 4.73
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5

Двойная буферизация и WIN32 (GDI).

18.12.2011, 18:23. Показов 16026. Ответов 10
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
В MSDN написано:
Цитата Сообщение от MSDN
If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue. The hwnd member of the message's MSG structure contains the value of the hWnd parameter.
Я в свою очередь добавил обработку этого сообщения, но оно приходит только раз - когда я сам его посылаю.

Это был первый вопрос, второй: когда перетаскиваешь окно мерцание все-таки видно, можно ли как-то укорить процесс копирование информации с буфера в буфер? Например, просто свопать ссылки на буфера.

Третий вопрос: если создавать контекст через GetDC() и выделять под него буфер, тогда задний фон черного цвета, как его можно поменять?

Заранее благодарен за помощь.
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
64
65
66
67
68
69
70
71
72
73
//
//  FUNCTION: DrawIfNeeded(HWND)
//
COLORREF color = 0;
RECT rect = {20, 20, 220, 220};
HDC hdc = 0;
HDC hdcMem = 0;
HBITMAP buffer = 0;
 
void DrawIfNeeded(HWND hWnd)
{
    ::BitBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, hdcMem, rect.left, rect.top, SRCCOPY);
}
 
//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
 
    switch (message)
    {
    case WM_CREATE:
        {
            hdc = ::GetDC(hWnd);
            hdcMem = ::CreateCompatibleDC(hdc);
            buffer = ::CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
            ::SelectObject(hdcMem, buffer);
 
            ::SendMessageA(hWnd, WM_TIMER, NULL, NULL);
            
            ::SetTimer(hWnd, 1, 1000, NULL);
        }
        break;
    case WM_PAINT:
        {
            ::DrawIfNeeded(hWnd);
        }
        break;
    case WM_TIMER:
        {
            // create color
            color = RGB(rand()%256, rand()%256, rand()%256);
            
            // set current brush
            HBRUSH newBrush = ::CreateSolidBrush(color);
            HBRUSH oldBrush = (HBRUSH)::SelectObject(hdcMem, newBrush);
 
            // draw filled ellipse
            ::Ellipse(hdcMem, rect.left, rect.top, rect.right, rect.bottom);
 
            ::DeleteObject((HGDIOBJ)oldBrush);
            //if (CLR_INVALID == ::SetBkColor(hdcMem, color))
            //{
            //  ::MessageBoxA(hWnd, (LPCSTR)"Can't set bkgColor.", (LPCSTR)"Error", MB_OK);
            //  DestroyWindow(hWnd);
            //}
        }
        break;
    case WM_DESTROY:
        {
            ::DeleteDC(hdcMem);
            ::DeleteObject(buffer);
            ::ReleaseDC(hWnd, hdc);
            ::PostQuitMessage(0);
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.12.2011, 18:23
Ответы с готовыми решениями:

Двойная буферизация в gdi+
Не могу разобраться, как правильно ее сделать. Есть функция: void createImage(wchar_t *path, int _abscissa, int _ordinate,...

Двойная буферизация
Добрый день! Имеется обычное оконное приложение Win32, необходимо реализовать при рисовании механизм двойной буферизации. Вот мой код, по...

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

10
DU
1500 / 1146 / 165
Регистрация: 05.12.2011
Сообщений: 2,279
18.12.2011, 18:40
да вроде нормальная буфериция у вас. эффект мерцания наверно из-за того, что перед сообщением WM_PAINT идет сообщения об отчистке окна с целью зарисовать все фоном. это отменяется. правда не помню как. либо вставить свой обработчик, который ничего не делает, либо это как-то стилем окна выставляется. дефолтный фон у дс - хз. когда делал подобную штуку явно заливал дс нужным цветом.

http://msdn.microsoft.com/en-u... S.85).aspx - попробуйте для этого сообщения свой ничего не делающий обработчик вставить.
0
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
18.12.2011, 19:52  [ТС]
Цитата Сообщение от DU Посмотреть сообщение
попробуйте для этого сообщения свой ничего не делающий обработчик вставить.
Не то.

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

Добавлено через 45 минут
Всем спасибо, вот что у меня вышло:
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//
//  FUNCTION: ReDrawElipse(HWND)
//
COLORREF color = 0;
RECT rect = {20, 20, 220, 220};
HDC hdc = 0;
HDC hdcMem = 0;
HBITMAP buffer = 0;
const UINT_PTR timerPtr = 1;
 
void ReDrawElipse(HWND hWnd)
{
    COLORREF white = RGB(255, 255, 255);
    HBRUSH newBrush = ::CreateSolidBrush(white);
    HBRUSH oldBrush = (HBRUSH)::SelectObject(hdcMem, newBrush);
    ::Rectangle(hdcMem, rect.left-1, rect.top-1, rect.right+1, rect.bottom+1);
    ::DeleteObject((HGDIOBJ)oldBrush);
 
    color = RGB(rand()%256, rand()%256, rand()%256);
    newBrush = ::CreateSolidBrush(color);
    oldBrush = (HBRUSH)::SelectObject(hdcMem, newBrush);
    ::Ellipse(hdcMem, rect.left, rect.top, rect.right, rect.bottom);
    ::DeleteObject((HGDIOBJ)oldBrush);
 
    ::InvalidateRect(hWnd, &rect, false);
}
 
//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT paintStruct;
 
    switch (message)
    {
    case WM_CREATE:
        {
            hdc = ::BeginPaint(hWnd, &paintStruct);
            hdcMem = ::CreateCompatibleDC(hdc);
            buffer = ::CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
            ::SelectObject(hdcMem, buffer);
            ::EndPaint(hWnd, &paintStruct);
            
            ReDrawElipse(hWnd);
 
            ::SetTimer(hWnd, timerPtr, 1000, NULL);
        }
        break;
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_EXIT:
            ::SendMessageA(hWnd, WM_DESTROY, NULL, NULL);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        {
            hdc = ::BeginPaint(hWnd, &paintStruct);
            ::BitBlt(hdc, rect.left, rect.top, rect.right, rect.bottom, hdcMem, rect.left, rect.top, SRCCOPY);
            ::EndPaint(hWnd, &paintStruct);
        }
        break;
    case WM_TIMER:
        {
            switch (wParam)
            {
            case timerPtr:
                {
                    ReDrawElipse(hWnd);
                }
                break;
            }
        }
        break;
    case WM_DESTROY:
        {
            ::DeleteObject(buffer);
            ::ReleaseDC(hWnd, hdc);
            ::PostQuitMessage(0);
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
Отдельное спасибо автору сообщения: https://www.cyberforum.ru/post1119781.html
1
 Аватар для Dimazzzzzz
589 / 96 / 6
Регистрация: 24.01.2009
Сообщений: 379
19.12.2011, 04:00
Все вроде нормально, только в функции BitBlt нужно использовать структуру paintStruct, иначе зачем мы передаем в нее прямоугольник через функцию InvalidateRect:

C++
1
2
3
4
5
6
7
8
9
10
::BitBlt(
hdc, 
paintStruct.rcPaint.left, 
paintStruct.rcPaint.top, 
paintStruct.rcPaint.right - paintStruct.rcPaint.left,  //Проверь, тут по-моему нужно ширину и высоту
paintStruct.rcPaint.bottom - paintStruct.rcPaint.top, //Проверь, тут по-моему нужно ширину и высоту
hdcMem, 
paintStruct.rcPaint.left, 
paintStruct.rcPaint.top, 
SRCCOPY);
Просто, когда мы НЕ вызываем функцию InvalidateRect, система сама посылает сообщение WM_PAINT, например, при перекрывании окон, тогда данные будут браться из структуры paintStruct для копирования и может быть скопирован всего маленький уголок окна, который требует перекрашивания, а не всё окно
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
19.12.2011, 20:04  [ТС]
Dimazzzzzz, у меня к тебе будет пара вопросов:

1. Следующий участок кода закрашивает область в белый, как залить вспомогательный буфер областью памяти из текущего представления и рисовать поверх (или как дополнительный контекст залить данными с контекста окна) и как просто задать цвет фона при создании вспомогательного буфера?
C++
1
2
3
4
5
        COLORREF white = RGB(255, 255, 255);
        HBRUSH newBrush = ::CreateSolidBrush(white);
        HBRUSH oldBrush = (HBRUSH)::SelectObject(hdcMem, newBrush);
        ::Rectangle(hdcMem, rect.left-1, rect.top-1, rect.right+1, rect.bottom+1);
        ::DeleteObject((HGDIOBJ)oldBrush);
2. Если в PAINTSTRUCT хранится область которую мы хотим перекрашивать, тогда нам надо еще проверять входит ли наша область в ту, которая подлежит перерисовке и перекрашивать только их пересечение.

3. Меня интересует создание буфер под кондекст, а именно что означают ширина и высота в параметрах HBITMAP CreateCompatibleBitmap(HDC hdc, int nWidth, int nHeight); на MSDN не нашел внятного ответа. Если мы создадим буфер под вспомогательный контекст 100 пикселов, а рисовать будем вне его пределах что тогда произойдет.
Только что посмотрел, часть которая не попадает в область буфера просто обрезается, причем если надо отрисовать полосу от 20 до 40 пикселов, надо создавать буфер размером 40, т.к. он начинается с 0, как выделять буфер под конкретную область не с начала координат?

Добавлено через 24 минуты
Вот что вышло для перерисовки:
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
bool getIntersection(RECT rect1, RECT rect2, RECT *result)
{
    result->left = max(rect1.left, rect2.left);
    result->right = min(rect1.right, rect2.right);
    result->top = max(rect1.top, rect2.top);
    result->bottom = min(rect1.bottom, rect2.bottom);
 
    return res->left <= res->right && res->top <= res->bottom;
}
 
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
 
    case WM_PAINT:
        {
            hdc = ::BeginPaint(hWnd, &paintStruct);
            RECT rc;
            // rect - поле, которое перерисовывает этот блок
            if (getIntersection(paintStruct.rcPaint, rect, &rc)) 
            {
                ::BitBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, hdcMem, rc.left, rc.top, SRCCOPY);
            }
            ::EndPaint(hWnd, &paintStruct);
        }
        break;
0
 Аватар для Dimazzzzzz
589 / 96 / 6
Регистрация: 24.01.2009
Сообщений: 379
19.12.2011, 20:05
1. Функция Rectangle создает прямоугольник с рамкой, нарисованной текущим Pen'ом и заливается текущей Brush'ю. Если нужно просто закрасить какой-то участок, то лучше использовать функцию FillRect ().
2. В структуре paintstruct будет содержаться прямоугольник, на контексте ОКНА, который нужно перекрасить. При вызове функции InvalidateRect мы сами задаем этот прямоугольник, а при перекрытии окон, его задает система.

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

Хотя можно изначально создать достаточно большой буфер (например, 1600х1200) и создать механизм масштабирования при копировании и кликанье мышью на картинке. В таком случае нужно будет использовать не BitBlt, а StretchBlt в обработчике WM_PAINT. Главное не перепутать указание координат прямоугольника: во второй функции указываются координаты углов, а не размеры.

В любом случае, тот прямоугольник, который в момент обработки WM_PAINT находится в структуре paintstruct, выражает именно ту область основного окна, которую надо перекрасить. А каким образом это сделать, это уже решать тебе.
3) Все правильно ты нашел: те точки, которые не влезают в наш bitmap, будут просто откидываться. Начало координат находится в верхнем левом углу и начинается с нуля.
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
19.12.2011, 20:17  [ТС]
Ок, поставлю вопрос так: если нам надо перерисовывать область (20,20):(40,40) мы можем создать буфер размером 40 и туда все рисовать, если это уже (1020,1020):(1040,1040) все уже не так хорошо, так как куча памяти идет в никуда, а ведь нужно просто квадрат 20х20.

И еще буфер создается мной для каждого элемента UI и он прорисовывает себя, затем говорит перерисовать себя элементам на нем, они в свою очередь своим и т.д. Каждый элемент имеет свой буфер, если нужно прорисовать он просто копирует свой буфер в общий буфер (буфер всего окна), и если этот элемент находится далеко и занимает мало места и таких не мало, тогда уже приходиться худо, вот к чему я веду.
0
 Аватар для Dimazzzzzz
589 / 96 / 6
Регистрация: 24.01.2009
Сообщений: 379
20.12.2011, 01:31
В любом случае размер промежуточного буфера должен совпадать с размером окна, т.к. это как бы копия одного и того же.

Если я правильно понял, ты хочешь узнать зачем создавать большой bitmap, если то, что нужно нам на нем, будет маленькое и незаметное где-нибудь в уголке?

На картинке посмотри, как происходит процесс. Этот промежуточный буфер содержит то, что нужно рисовать в окне. Но рисунок на окне постоянно нужно восстанавливать и сделать это можно двумя способами:
- либо рисовать заново, высчитывая всё
- либо просто скопировать с буфера нужный кусок (наш выбор )

То, каким образом и что ты будешь рисовать, копировать на этом буфере, это не важно. Если у тебя одна маленькая картинка, которую ты постоянно рисуешь в окне (и в буфере разумеется тоже), то ты можешь хранить ее в одном маленьком bitmap'e. Если у тебя свой графический интерфейс со кучей нарисованных кнопок, то не нужно для каждой из них создавать отдельный контекст и буфер, можно уместить всё на одном (вспомни старые спрайтовые игры, где много мелких игровых картинок рисовались на одной. Хотя и в любой современной программе все элементы интерфейса, как правило, рисуют на одной картинке) и создать механизм копирования нужного маленького квадратика на промежуточный буфер с этого большого холста.
Миниатюры
Двойная буферизация и WIN32 (GDI).  
1
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
18.01.2012, 01:34  [ТС]
Мы друг друга не поняли, ну и ладно...
0
375 / 322 / 32
Регистрация: 24.02.2011
Сообщений: 1,512
Записей в блоге: 1
18.01.2012, 16:38
Цитата Сообщение от Dimazzzzzz Посмотреть сообщение
В любом случае размер промежуточного буфера должен совпадать с размером окна, т.к. это как бы копия одного и того же.
Это не совсем так. Например, в какой-то части диалогового окна я рисую диаграмму чего-то там. Мне не нужно все окно...
Цитата Сообщение от outoftime Посмотреть сообщение
Ок, поставлю вопрос так: если нам надо перерисовывать область (20,20)40,40) мы можем создать буфер размером 40 и туда все рисовать, если это уже (1020,1020)1040,1040) все уже не так хорошо, так как куча памяти идет в никуда, а ведь нужно просто квадрат 20х20.
Достаточно буфера 20х20. В функции BitBlt, например, для этого и указываются координаты и размер. Читаем описание в MSDN. Еще читаем о регионах.
1
 Аватар для Dimazzzzzz
589 / 96 / 6
Регистрация: 24.01.2009
Сообщений: 379
18.01.2012, 19:00
Цитата Сообщение от bigredcat Посмотреть сообщение
Это не совсем так. Например, в какой-то части диалогового окна я рисую диаграмму чего-то там. Мне не нужно все окно...
Согласен, но когда человек только изучает такой метод, проще всего взять всё окно. А потом уже он сам решит, какой прямоугольник ему нужен. Хотя, судя по тому, что мы не поняли друг друга, я неправильно предположил
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.01.2012, 19:00
Помогаю со студенческими работами здесь

Двойная буферизация
Написал тетрис (но только с палочками (друг попросил сделать =) )) Писал на чистом WinAPI, в WM_PAINT происходит сначала отрисовка...

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

Двойная буферизация, мерцание
Всем привет! Пишу просто графическое приложение на чистом WinApi и никак не могу разобраться с двойной буфферизацией. Попробовал...

Двойная буферизация не работает
почему то не работает Двойная Буферизация case WM_PAINT: { hdc=BeginPaint(hwnd,&amp;PaintStruct); GetClientRect(hwnd,&amp;rect); ...

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


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

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru