- •Содержание
- •Глава 1 README.TXT
- •Вызов, брошенный программистам
- •Основные правила
- •Краткая история Windows
- •Краткая история этой книги
- •Начнем
- •Глава 2 Hello, Windows 95
- •Отличительная особенность Windows
- •Графический интерфейс пользователя
- •Концепции и обоснование GUI
- •Содержимое интерфейса пользователя
- •Преимущество многозадачности
- •Управление памятью
- •Независимость графического интерфейса от оборудования
- •Соглашения операционной системы Windows
- •Вызовы функций
- •Объектно-ориентированное программирование
- •Архитектура, управляемая событиями
- •Оконная процедура
- •Ваша первая программа для Windows
- •Что в этой программе неправильно?
- •Файлы HELLOWIN
- •Make-файл
- •Вызовы функций Windows
- •Идентификаторы, написанные прописными буквами
- •Новые типы данных
- •Описатели
- •Венгерская нотация
- •Точка входа программы
- •Регистрация класса окна
- •Создание окна
- •Отображение окна
- •Цикл обработки сообщений
- •Оконная процедура
- •Обработка сообщений
- •Воспроизведение звукового файла
- •Сообщение WM_PAINT
- •Сообщение WM_DESTROY
- •Сложности программирования для Windows
- •Не вызывай меня, я вызову тебя
- •Синхронные и асинхронные сообщения
- •Думайте о ближнем
- •Кривая обучения
- •Глава 3 Рисование текста
- •Рисование и обновление
- •Сообщение WM_PAINT
- •Действительные и недействительные прямоугольники
- •Введение в графический интерфейс устройства (GDI)
- •Контекст устройства
- •Структура информации о рисовании
- •Получение описателя контекста устройства. Второй метод
- •Функция TextOut. Подробности
- •Системный шрифт
- •Размер символа
- •Метрические параметры текста. Подробности
- •Форматирование текста
- •Соединим все вместе
- •Не хватает места!
- •Размер рабочей области
- •Полосы прокрутки
- •Диапазон и положение полос прокрутки
- •Сообщения полос прокрутки
- •Прокрутка в программе SYSMETS
- •Структурирование вашей программы для рисования
- •Создание улучшенной прокрутки
- •Мне не нравится пользоваться мышью
- •Глава 4 Главное о графике
- •Концепция GDI
- •Структура GDI
- •Типы функций
- •Примитивы GDI
- •Другие аспекты
- •Контекст устройства
- •Получение описателя контекста устройства
- •Программа DEVCAPS1
- •Размер устройства
- •О цветах
- •Атрибуты контекста устройства
- •Сохранение контекста устройства
- •Рисование отрезков
- •Ограничивающий прямоугольник
- •Сплайны Безье
- •Использование стандартных перьев
- •Создание, выбор и удаление перьев
- •Закрашивание пустот
- •Режимы рисования
- •Рисование закрашенных областей
- •Функция Polygon и режим закрашивания многоугольника
- •Закрашивание внутренней области
- •Режим отображения
- •Координаты устройства (физические координаты) и логические координаты
- •Системы координат устройства
- •Область вывода и окно
- •Работа в режиме MM_TEXT
- •Метрические режимы отображения
- •Ваши собственные режимы отображения
- •Программа WHATSIZE
- •Прямоугольники, регионы и отсечение
- •Работа с прямоугольниками
- •Случайные прямоугольники
- •Создание и рисование регионов
- •Отсечения: прямоугольники и регионы
- •Программа CLOVER
- •Пути
- •Создание и воспроизведение путей
- •Расширенные перья
- •Bits and Blts
- •Цвета и битовые образы
- •Файл DIB
- •Упакованный формат хранения DIB
- •Отображение DIB
- •Преобразование DIB в объекты "битовые образы"
- •Битовый образ — объект GDI
- •Создание битовых образов в программе
- •Формат монохромного битового образа
- •Формат цветного битового образа
- •Контекст памяти
- •Мощная функция BitBlt
- •Перенос битов с помощью функции BitBlt
- •Функция DrawBitmap
- •Использование других ROP кодов
- •Дополнительные сведения о контексте памяти
- •Растяжение битовых образов с помощью функции StretchBlt
- •Кисти и битовые образы
- •Метафайлы
- •Простое использование метафайлов памяти
- •Сохранение метафайлов на диске
- •Расширенные метафайлы
- •Делаем это лучше
- •Базовая процедура
- •Заглянем внутрь
- •Вывод точных изображений
- •Текст и шрифты
- •Вывод простого текста
- •Атрибуты контекста устройства и текст
- •Использование стандартных шрифтов
- •Типы шрифтов
- •Шрифты TrueType
- •Система EZFONT
- •Внутренняя работа
- •Форматирование простого текста
- •Работа с абзацами
- •Глава 5 Клавиатура
- •Клавиатура. Основные понятия
- •Игнорирование клавиатуры
- •Фокус ввода
- •Аппаратные и символьные сообщения
- •Аппаратные сообщения
- •Системные и несистемные аппаратные сообщения клавиатуры
- •Переменная lParam
- •Виртуальные коды клавиш
- •Использование сообщений клавиатуры
- •Модернизация SYSMETS: добавление интерфейса клавиатуры
- •Логика обработки сообщений WM_KEYDOWN
- •Посылка асинхронных сообщений
- •Символьные сообщения
- •Сообщения WM_CHAR
- •Сообщения немых символов
- •Каретка (не курсор)
- •Функции работы с кареткой
- •Программа TYPER
- •Наборы символов Windows
- •Набор символов OEM
- •Набор символов ANSI
- •Наборы символов OEM, ANSI и шрифты
- •Международные интересы
- •Работа с набором символов
- •Связь с MS-DOS
- •Использование цифровой клавиатуры
- •Решение проблемы с использованием системы UNICODE в Windows NT
- •Глава 6 Мышь
- •Базовые знания о мыши
- •Несколько кратких определений
- •Сообщения мыши, связанные с рабочей областью окна
- •Простой пример обработки сообщений мыши
- •Обработка клавиш <Shift>
- •Сообщения мыши нерабочей области
- •Сообщение теста попадания
- •Сообщения порождают сообщения
- •Тестирование попадания в ваших программах
- •Гипотетический пример
- •Пример программы
- •Эмуляция мыши с помощью клавиатуры
- •Добавление интерфейса клавиатуры к программе CHECKER
- •Использование дочерних окон для тестирования попадания
- •Дочерние окна в программе CHECKER
- •Захват мыши
- •Рисование прямоугольника
- •Решение проблемы — захват
- •Программа BLOKOUT2
- •Глава 7 Таймер
- •Основы использования таймера
- •Система и таймер
- •Таймерные сообщения не являются асинхронными
- •Использование таймера: три способа
- •Первый способ
- •Второй способ
- •Третий способ
- •Использование таймера для часов
- •Позиционирование и изменение размеров всплывающего окна
- •Получение даты и времени
- •Обеспечение международной поддержки
- •Создание аналоговых часов
- •Стандартное время Windows
- •Анимация
- •Класс кнопок
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Нажимаемые кнопки
- •Флажки
- •Переключатели
- •Окна группы
- •Изменение текста кнопки
- •Видимые и доступные кнопки
- •Кнопки и фокус ввода
- •Дочерние окна управления и цвета
- •Системные цвета
- •Цвета кнопок
- •Сообщение WM_CTLCOLORBTN
- •Кнопки, определяемые пользователем
- •Класс статических дочерних окон
- •Класс полос прокрутки
- •Программа COLORS1
- •Интерфейс клавиатуры, поддерживаемый автоматически
- •Введение новой оконной процедуры
- •Закрашивание фона
- •Окрашивание полос прокрутки и статического текста
- •Класс редактирования
- •Стили класса редактирования
- •Коды уведомления управляющих окон редактирования
- •Использование управляющих окон редактирования
- •Сообщения управляющему окну редактирования
- •Класс окна списка
- •Стили окна списка
- •Добавление строк в окно списка
- •Выбор и извлечение элементов списка
- •Получение сообщений от окон списка
- •Простое приложение, использующее окно списка
- •Список файлов
- •Утилита Head для Windows
- •Компиляция ресурсов
- •Значки и курсоры
- •Редактор изображений
- •Получение описателя значков
- •Использование значков в вашей программе
- •Использование альтернативных курсоров
- •Битовые образы: картинки в пикселях
- •Использование битовых образов и кистей
- •Символьные строки
- •Использование ресурсов-символьных строк
- •Меню
- •Структура меню
- •Шаблон меню
- •Ссылки на меню в вашей программе
- •Меню и сообщения
- •Образец программы
- •Этикет при организации меню
- •Сложный способ определения меню
- •Третий подход к определению меню
- •Независимые всплывающие меню
- •Использование системного меню
- •Изменение меню
- •Другие команды меню
- •Использование в меню битовых образов
- •Два способа создания битовых образов для меню
- •Контекст памяти
- •Создание битового образа, содержащего текст
- •Масштабирование битовых образов
- •Соберем все вместе
- •Добавление интерфейса клавиатуры
- •Быстрые клавиши
- •Зачем нужны быстрые клавиши?
- •Некоторые правила назначения быстрых клавиш
- •Таблица быстрых клавиш
- •Преобразование нажатий клавиш клавиатуры
- •Получение сообщений быстрых клавиш
- •Программа POPPAD, имеющая меню и быстрые клавиши
- •Разрешение пунктов меню
- •Обработка опций меню
- •Глава 11 Окна диалога
- •Модальные окна диалога
- •Создание окна диалога About
- •Шаблон окна диалога
- •Диалоговая процедура
- •Вызов окна диалога
- •Дополнительная информация о стиле окна диалога
- •Дополнительная информация об определении дочерних окон элементов управления
- •Более сложное окно диалога
- •Работа с дочерними элементами управления окна диалога
- •Кнопки OK и Cancel
- •Позиции табуляции и группы
- •Рисование в окне диалога
- •Использование с окном диалога других функций
- •Определение собственных окон управления
- •Окна сообщений
- •Информация во всплывающих окнах
- •Немодальные окна диалога
- •Различия между модальными и немодальными окнами диалога
- •Новая программа COLORS
- •Программа HEXCALC: обычное окно или окно диалога?
- •Творческое использование идентификаторов дочерних окон элементов управления
- •Диалоговые окна общего пользования
- •Модернизированная программа POPPAD
- •Изменение шрифта
- •Поиск и замена
- •Программа для Windows, содержащая всего один вызов функции
- •Основы элементов управления общего пользования
- •Инициализация библиотеки
- •Создание элементов управления общего пользования
- •Стили элементов управления общего пользования
- •Уведомляющие сообщения от элементов управления общего пользования
- •Элементы управления главного окна
- •Панели инструментов
- •Создание панели инструментов
- •Строка состояния
- •Программа GADGETS
- •Наборы страниц свойств
- •Создание набора страниц свойств
- •Процедуры диалогового окна страницы свойств
- •Программа PROPERTY
93
конечная точка предыдущей и т. д. Параметр iCount всегда равен единице плюс умноженному на три числу связанных кривых, которые вы хотите отобразить.
Рис. 4.15 Окно программы BEZIER
Функция PoliBezierTo использует текущее положение пера как начальную точку. Первый и все последующие сплайны требуют только три точки. Когда функция возвращает управление, текущая позиция пера устанавливается в конечную точку последней кривой.
Одно предупреждение: Когда вы рисуете набор связанных сплайнов Безье, в точке связи будет плавный переход, только если вторая контрольная точка первой кривой Безье, конечная точка первой кривой (она же начальная точка второй кривой) и первая контрольная точка второй кривой лежат на одной прямой.
Использование стандартных перьев
Когда вы вызываете одну из функций рисования кривых, которые только что рассматривались, Windows для рисования линии использует перо, выбранное в контексте устройства в данный момент. Перо определяет цвет линии, ширину и ее стиль, который может быть сплошным (solid), точечным (dotted) или пунктирным (dashed). Перо, устанавливаемое в контексте устройства по умолчанию называется BLACK_PEN (черное перо, черный карандаш). Это перо рисует сплошные черные линии толщиной в один пиксель независимо от режима отображения (mapping mode). BLACK_PEN — это одно из трех "стандартных" перьев, поддерживаемых Windows. Два других — это WHITE_PEN (белое перо) и NULL_PEN (пустое перо). NULL_PEN это перо, которое ничего не рисует. Вы можете также создавать свои собственные перья.
В программах для Windows обычно для ссылки на перо используется описатель (handle). Заголовочный файл Windows содержит определение типа HPEN, описатель пера (handle to a pen). Вы можете определить переменную, например, hPen, используя такое определение:
HPEN hPen;
Вы получаете описатель одного из стандартных перьев, вызывая функцию GetStockObject. Например, предположим, вы хотите использовать стандартное перо WHITE_PEN. Его описатель можно получить так:
hPen = GetStockObject(WHITE_PEN);
Теперь вы должны сделать это перо выбранным в контексте устройства текущим пером. Для этого необходимо вызвать функцию SelectObject:
SelectObject(hdc, hPen);
После этого вызова все линии, которые вы рисуете, будут использовать WHITE_PEN до тех пор, пока вы не выберете другое перо в контекст устройства или пока не освободите контекст устройства.
Вместо того, чтобы определять переменную hPen, вы можете совместить вызовы GetStockObject и SelectObject в одной инструкции:
SelectObject(hdc, GetStockObject(WHITE_PEN));
Если затем вы захотите вернуться к использованию пера BLACK_PEN, вы можете получить описатель этого стандартного пера и выбрать его в контекст устройства в одной инструкции:
SelectObject(hdc, GetStockObject(BLACK_PEN));
94
SelectObject возвращает описатель того пера, которое уже было выбрано в контексте устройства. Если вы начинаете работать с только что полученным описателем контекста устройства и вызываете:
hPen = SelectObject(hdc, GetStockObject(WHITE_PEN));
то текущим выбранным пером в контексте устройства становится WHITE_PEN, а переменная hPen становится описателем пера BLACK_PEN. Вы можете выбрать BLACK_PEN в контекст устройства, используя вызов:
SelectObject(hdc, hPen);
Создание, выбор и удаление перьев
Хотя перья, определенные как стандартные объекты, несомненно, удобны, вы ограничены использованием только сплошного черного пера, сплошного белого пера или пустого пера. Если вы хотите большего, то вы должны создавать свои собственные перья. Здесь приведена последовательность действий: вы создаете "логическое перо", которое только описывает перо, используя функции CreatePen или CreatePenIndirect. (Вы можете также использовать функцию ExtCreatePen, которая будет обсуждаться далее в этой главе.) Эти функции возвращают описатель логического пера. Вы выбираете перо в контекст устройства путем вызова SelectObject. Затем вы можете рисовать линии, используя это новое перо. Только одно перо может быть одновременно выбрано в контексте устройства. После того, как вы освободите контекст устройства (или выберете в контекст устройства другое перо), вы можете удалить созданное вами перо, используя DeleteObject. После того, как вы это сделаете, значение описателя пера становится недействительным.
Логическое перо — объект GDI. Вы создаете и используете перо, но оно не принадлежит вашей программе. В действительности оно принадлежит модулю GDI. Перо — это один из шести объектов GDI, которые вы можете создавать. Другие пять — это кисти, битовые образы, регионы, шрифты и палитры. За исключением палитр все эти объекты выбираются в контекст устройства, используя функцию SelectObject.
Три правила управляют использованием таких объектов GDI как перья:
•Обязательно удаляйте все созданные вами объекты GDI.
•Не удаляйте объекты GDI, пока они выбраны в действительном контексте устройства.
•Не удаляйте стандартные объекты.
Это разумные правила, но они иногда могут немного подвести. Рассмотрим на примерах, как действуют эти правила.
Вызов функции CreatePen выглядит так:
hPen = CreatePen(iPenStyle, iWidth, rgbColor);
Параметр iPenStyle определяет, какого типа линии будут отображаться: сплошная, точечная или пунктирная. Этот параметр может принимать одно значение из следующего списка идентификаторов, приведенных в заголовочном файле Windows. На рис. 4.16 показаны линии каждого типа.
PS_SO LID
PS_D ASH
PS_D O T
PS_D ASH DO T
PS_D ASH DO TDO T
PS_N ULL
PS_IN SID EFRAM E
Рис. 4.16 Семь стилей пера
Для стилей PS_SOLID, PS_NULL и PS_INSIDEFRAME параметр iWidth — ширина пера. Ширина iWidth равная 0, заставляет Windows использовать перо шириной в один пиксель. Стандартные перья имеют ширину в 1 пиксель. Если вы зададите точечный или пунктирный стиль линии с физической шириной больше 1, Windows, тем не менее, будут использовать сплошное перо.
95
Параметр rgbColor функции CreatePen — это беззнаковое длинное целое, задающее цвет пера. Для перьев всех стилей, кроме PS_INSIDEFRAME, когда вы выбираете перо в контекст устройства, Windows преобразует значение этого параметра в ближайший чистый цвет, какой может представить устройство. Только перья со стилем PS_INSIDEFRAME могут использовать полутона, и только если их ширина больше 1.
Стиль PS_INSIDEFRAME, когда используется с функциями, определяющими закрашенные области, имеет еще одну особенность. Для всех стилей, кроме PS_INSIDEFRAME, если перо используется для рисования контура шириной более 1 пикселя, то оно центрируется таким образом, что часть линии может оказаться за пределами ограничивающего прямоугольника. Для стиля PS_INSIDEFRAME вся линия целиком рисуется внутри ограничивающего прямоугольника.
Вы можете также создать перо, определив структуру типа LOGPEN — "логическое перо" (logical pen) и вызвав функцию CreatePenIndirect. Если ваша программа использует несколько различных перьев, которые вы инициализируете в своей программе, этот метод наиболее эффективен. В начале вы определяете переменную типа
LOGPEN, например, logpen:
LOGPEN logpen;
Эта структура имеет три члена: lopnStyle (UINT) — стиль пера, lopnWidth (POINT) — ширина пера в логических единицах измерения, lopnColor (COLORREF) — цвет пера. Член структуры lopnWidth имеет тип POINT, но Windows использует только величину lopnWidth.x как ширину пера и игнорирует значение lopnWidth.y. Затем вы создаете перо, передавая адрес структуры в функцию CreatePenIndirect:
hPen = CreatePenIndirect(&logpen);
Вы можете также получить информацию логического пера для уже существующего пера. Если у вас есть описатель пера, вы можете скопировать данные, определяющие логическое перо в структуру типа LOGPEN, используя вызов GetObject:
GetObject(hPen, sizeof(LOGPEN),(LPVOID) &logpen);
Обратите внимание, что функции CreatePen и CreatePenIndirect не требуют описателя контекста устройства. Эти функции создают логические перья, которые никак не связаны с контекстом устройства до тех пор, пока вы не вызовите SelectObject. Например, вы можете использовать одно логическое перо для нескольких различных устройств, таких как дисплей и принтер.
Ниже представлен метод создания, выбора и удаления перьев. Предположим, ваша программа использует три пера
— черное шириной 1, красное шириной 3 и черное точечное. Вы можете сначала определить переменные для хранения описателей этих перьев:
static HPEN hPen1, hPen2, hPen3;
В процессе обработки сообщения WM_CREATE вы можете создать три пера:
hPen1 = CreatePen(PS_SOLID, 1, 0);
hPen2 = CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
hPen3 = CreatePen(PS_DOT, 0, 0);
В процессе обработки сообщения WM_PAINT (или в любой момент, когда у вас есть действительный контекст устройства) вы можете выбрать одно из этих перьев в контекст устройства и рисовать, используя его:
SelectObject(hdc, hPen2);
[функции рисования линий]
SelectObject(hdc, hPen1);
[другие функции рисования линий]
В процессе обработки сообщения WM_DESTROY вы можете удалить три пера, созданные вами ранее:
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
Это наиболее общий, магистральный метод создания, выбора и удаления перьев, но он требует резервирования памяти для логических перьев на все время работы вашей программы. Вместо этого вы можете создать перья в процессе обработки сообщения WM_PAINT и удалить их после вызова EndPaint. (Вы можете удалить их и до вызова EndPaint, но вы должны быть осторожны и не удалить перо, выбранное в контекст устройства.)
Кроме того вы можете создать перья на "лету" и объединить вызовы функций CreatePen и SelectObject в одну инструкцию:
SelectObject(hdc, CreatePen(PS_DASH, 0, RGB(255, 0, 0)));
Теперь, когда вы рисуете линии, вы будете использовать красное точечное перо. Когда вы закончите рисовать красным точечным пером, вы можете удалить его. Но, как можно удалить это перо, если не сохранен его
96
описатель? Новый вызов SelectObject возвращает описатель пера, которое было раньше выбрано в контексте устройства. Таким образом, вы можете удалить перо путем выбора стандартного пера BLACK_PEN в контекст устройства и удаления значения, возвращаемого функцией SelectObject:
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
Рассмотрим другой метод. Когда вы выбираете только что созданное перо в контекст устройства, сохраните описатель, возвращаемый функцией SelectObject:
hPen = SelectObject(hdc, CreatePen(PS_DASH, 0, RGB(255, 0, 0)));
Что такое hPen? Если это первый вызов SelectObject после получения описателя контекста устройства, то hPen — это описатель стандартного пера BLACK_PEN. Вы можете теперь выбрать это перо в контекст устройства и удалить перо, созданное вами (описатель, возвращаемый вторым вызовом функции SelectObject) в одной инструкции:
DeleteObject(SelectObject(hdc, hPen));
Закрашивание пустот
Использование точечных и штриховых перьев ставит интересный вопрос: что будет с пустотами между точками и штрихами? Цвет этих пробелов или пустот зависит от режима фона (background mode) и атрибутов цвета фона, определенных в контексте устройства. Режим фона по умолчанию равен OPAQUE, т. е. Windows заполняет пустоты цветом фона, который, по умолчанию, белый. Это согласуется с работой стандартной кисти WHITE_BRUSH, которую многие программы используют в классе окна для стирания фона окна.
Вы можете изменить цвет фона, который Windows будет использовать для закрашивания пустот, вызвав:
SetBkColor(hdc, rgbColor);
Также как и для перьев, Windows преобразует этот цвет фона к чистому цвету. Вы можете определить текущий цвет фона, выбранный в контексте устройства, вызвав функцию GetBkColor.
Вы можете также отменить заполнение пустот системой Windows, изменив режим фона на TRANSPARENT:
SetBkMode(hdc, TRANSPARENT);
Windows будет игнорировать цвет фона и не будет заполнять пустоты. Вы можете определить текущий режим фона (как TRANSPARENT, так и OPAQUE), путем вызова GetBkMode.
Режимы рисования
Представление линий, отображаемых на дисплее, зависит также от режима рисования (drawing mode), установленного в контексте устройства. Представление цветной линии основывается не только на цвете пера, но и на цвете той области дисплея, где эта линия отображается. Подумайте о возможности использовать одно и то же перо для рисования черной линии на белом фоне и белой линии на черном фоне без знаний о том, какого цвета фон. Было бы это для вас удобным? Вы можете все проверить, применяя различные режимы рисования.
Когда Windows использует перо для рисования линии, на самом деле осуществляется поразрядная логическая операция между пикселями пера и пикселями принимающей поверхности устройства. Выполняемая поразрядная логическая операция над пикселями носит название "растровой операции" (raster operation или ROP). Поскольку рисование линий требует только двух пиксельных шаблонов (пера и приемной поверхности), логическая операция называется "бинарной растровой операцией" (binary raster operation или ROP2). Windows определяет 16 ROP2 кодов, показывающих, как Windows оперирует с пикселями пера и приемника. В контексте устройства по умолчанию режим рисования определяется как R2_COPYPEN, что означает простое копирование системой Windows пикселей пера в приемник и привычным при работе с перьями. Существует также еще 15 других ROP2 кодов.
Откуда взялись эти 16 различных ROP2 кодов? Для того, чтобы показать зачем они введены, рассмотрим монохромную систему. Цвет приемника (цвет рабочей области окна) может быть либо черным (0) либо белым (1). Перо также может быть либо белым, либо черным. Существует четыре комбинации использования черного или белого пера на черном или белом приемнике: белое перо на белом приемнике, белое перо на черном приемнике, черное перо на белом приемнике и черное перо на черном приемнике.
Что произойдет с приемником после рисования? Один вариант — это линия всегда будет черной, независимо от цвета пера или приемника: Это режим рисования, имеющий один из ROP2 кодов, а именно код R2_BLACK. Другой вариант — это линия будет черной, кроме комбинации, когда и перо и приемник — черные. В этом случае линия будет белой. Хотя это может показаться странным, в Windows есть соответствующий режим рисования, который называется R2_NOTMERGEPEN. Windows выполняет поразрядную операцию OR над пикселями пера и приемника, а затем инвертирует результат.