
- •Содержание
- •Глава 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

135
PatBlt(hdc, 0, -100, 100, 100, dwROP);
или
PatBlt(hdc, 100, 0, -100, -100, dwROP);
или
PatBlt(hdc, 100, -100, -100, 100, dwROP);
Простейший путь задать правильные параметры функции PatBlt — это установить xDest и yDest в левый верхний угол прямоугольника. Если ваш режим отображения определяет координату y так, что она возрастает при движении вверх, то используйте отрицательную величину параметра yHeight. Если ваш режим отображения определяет координату x так, что она возрастает при движении влево (что почти не встречается), то используйте отрицательную величину параметра xWidth.
Перенос битов с помощью функции BitBlt
В некотором смысле функция BitBlt — это расширенная функция PatBlt. Она делает все то же, что и PatBlt, а также вовлекает второй контекст устройства в логическую операцию. Ниже приведен синтаксис функции:
BitBlt(hdcDest, xDest, yDest, xWidth, yHeight, hdcSrc, xSrc, ySrc, dwROP);
Вызов функции BitBlt модифицирует приемный контекст устройства (его описатель hdcDst) в рамках прямоугольника, заданного логической точкой (xDesr, yDest) и параметрами xWidth и yHeight, заданными в логических единицах. Эти параметры определяют прямоугольник в соответствии с тем, как описано в предыдущем разделе. Функция BitBlt также использует прямоугольник из контекста устройства источника (описатель контекста hdcSrc). Этот прямоугольник начинается в логической точке (xSrc, ySrc) и имеет ширину xWidth логических единиц и высоту yHeight логических единиц.
Функция BitBlt осуществляет логическую операцию над тремя элементами: кистью, выбранной в контексте устройства приемника, пикселями прямоугольника в контексте устройства источника и пикселями прямоугольника в контексте устройства приемника. Результат заносится в прямоугольник приемного контекста устройства. Вы можете использовать любой из 256 ROP кодов в качестве параметра dwROP функции BitBlt. Пятнадцать ROP кодов, имеющих имена, приведены в следующей таблице.
Шаблон: (Pattern) (P) |
1 1 1 1 0 0 0 0 |
Булева |
ROP код |
Имя |
Источник: (Source) (S) |
1 1 0 0 1 1 0 0 |
операция |
|
|
Приемник: (Destination) (D) |
1 0 1 0 1 0 1 0 |
|
|
|
Результат: (Result) |
0 0 0 0 0 0 0 0 |
0 |
0x000042 |
BLACKNESS |
|
0 0 0 1 0 0 0 1 |
~(S|D) |
0x1100A6 |
NOTSRCERASE |
|
0 0 1 1 0 0 1 1 |
~S |
0x330008 |
NOTSRCCOPY |
|
0 1 0 0 0 1 0 0 |
S&~D |
0x440328 |
SRCERASE |
|
0 1 0 1 0 1 0 1 |
~D |
0x550009 |
DSTINVERT |
|
0 1 0 1 1 0 1 0 |
P^D |
0x5A0049 |
PATINVERT |
|
0 1 1 0 0 1 1 0 |
S^D |
0x660046 |
SRCINVERT |
|
1 0 0 0 1 0 0 0 |
S&D |
0x8800C6 |
SRCAND |
|
1 0 1 1 1 0 1 1 |
~S|D |
0xBB0226 |
MERGEPAINT |
|
1 1 0 0 0 0 0 0 |
P&S |
0xC000CA |
MERGECOPY |
|
1 1 0 0 1 1 0 0 |
S |
0xCC0020 |
SRCCOPY |
|
1 1 1 0 1 1 1 0 |
S|D |
0xEE0086 |
SRCPAINT |
|
1 1 1 1 0 0 0 0 |
P |
0xF00021 |
PATCOPY |
|
1 1 1 1 1 0 1 1 |
P|~S|D |
0xFB0A09 |
PATPAINT |
|
1 1 1 1 1 1 1 1 |
1 |
0xFF0062 |
WHITENESS |
Обратите внимание на ряды из восьми нулей и восьми единиц, которые являются результатами логических операций. Двузначное шестнадцатиричное число, соответствующее этим битам, есть старшее слово ROP кода. Если мы можем создать таблицу результатов для тех шаблонов, источников и приемников, которые нам нужны, то мы можем легко определить ROP код из таблицы ROP кодов в разделе "References" пакета Microsoft Developer Studio. Мы сделаем это позднее. Если вы используете один из 16-ти ROP кодов, приведенных в предыдущей таблице, то можно работать с функцией PatBlt вместо BitBlt, поскольку вы не обращаетесь к контексту устройства источника.
Вы можете сделать так, что hdcSrc и hdcDst будут описывать один и тот же контекст устройства. В этом случае функция BitBlt выполняет логическую операцию над приемным прямоугольником, исходным прямоугольником и текущей кистью, выбранной в контексте устройства. Однако, существует некоторая доля риска при выполнении этой операции с контекстом устройства рабочей области. Если часть исходного прямоугольника закрыта другим окном, то Windows будет использовать пиксели этого окна как исходные. Windows ничего не знает о том, что какое-либо окно закрывает часть рабочей области вашего окна.

136
Тем не менее, примеры функции BitBlt, использующей один и тот же контекст устройства для источника и приемника, просты для понимания.
Функция:
BitBlt(hdc, 100, 0, 50, 100, hdc, 0, 0, SRCCOPY);
копирует прямоугольник с вершиной в логической точке (0, 0), шириной 50 и высотой 100 логических единиц в прямоугольную область с вершиной в логической точке (100,0).
Функция DrawBitmap
Функция BitBlt наиболее эффективна при работе с битовыми образами, которые выбраны в контекст памяти. Когда вы выполняете перенос блока битов (bit block transfer) из контекста памяти в контекст устройства вашей рабочей области, битовый образ, выбранный в контексте памяти переносится в вашу рабочую область.
Ранее упоминалась гипотетическая функция DrawBitmap, которая выводила бы битовый образ на поверхность отображения. Такая функция должна иметь следующий синтаксис:
DrawBitmap(hdc, hBitmap, xStart, yStart);
Было обещано, что мы ее напишем. Вот она:
void DrawBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart)
{
BITMAP bm; HDC hdcMem; DWORD dwSize;
POINT ptSize, ptOrg;
hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap); SetMapMode(hdcMem, GetMapMode(hdc)); GetObject(hBitmap, sizeof(BITMAP),(LPVOID) &bm); ptSize.x = bm.bmWidth;
ptSize.y = bm.bmHeight; DPtoLP(hdc, &ptSize, 1); ptOrg.x = 0;
ptOrg.y = 0; DPtoLP(hdcMem, &ptOrg, 1); BitBlt(
hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, SRCCOPY
);
DeleteDC(hdcMem);
}
Здесь предполагается, что вы не хотите растягивать или сжимать высоту или ширину битового образа. Таким образом, если ваш битовый образ имеет ширину 100 пикселей, то вы сможете с его помощью закрыть любой прямоугольник, имеющий ширину 100 пикселей, независимо от режима отображения.
Функция DrawBitmap сначала создает контекст памяти, используя функцию CreateCompatibleDC, затем выбирает в него битовый образ с использованием функции SelectObject. Режим отображения контекста памяти устанавливается таким же, как режим отображения контекста устройства вывода. Поскольку функция BitBlt работает с логическими координатами и логическими размерами, и учитывая то, что вы не предполагаете растягивать или сжимать битовый образ, параметры xWidth и yHeight функции BitBlt должны иметь значения в логических координатах, соответствующих размерам битового образа в физических координатах. Поэтому, функция DrawBitmap определяет размеры битового образа, используя функцию GetObject, и создает структуру POINT для сохранения в ней ширины и высоты. Затем она преобразует эту точку в логические координаты. Аналогичные действия осуществляются и в отношении начала координат битового образа — точки (0, 0) в координатах устройства.
Обратите внимание, что не имеет никакого значения, какая кисть выбрана в приемном контексте устройства (hdc), поскольку режим SRCCOPY не использует кисть.
Использование других ROP кодов
SRCCOPY — самое часто встречающееся значение параметра dwROP функции BitBlt. Вам будет трудно найти примеры использования других 255 ROP кодов. Поэтому здесь будет показано несколько примеров, в которых используются другие ROP коды.
Первый пример: пусть у вас есть монохромный битовый образ, который вы хотите перенести на экран. При этом, вы хотите отобразить битовый образ так, чтобы черные (0) биты не оказывали влияния на текущее содержание

137
рабочей области. Более того, вы хотите, чтобы для всех белых (1) битов рабочая область закрашивалась кистью, возможно цветной, созданной функцией CreateSolidBrush. Как это сделать?
Предполагается, что вы работаете в режиме отображения MM_TEXT, и что вы хотите отобразить битовый образ, начиная в точке (xStart, yStart) вашей рабочей области. У вас также есть описатель монохромного битового образа (hBitmap) и описатель цветной кисти (hBrush). Вы также знаете ширину и высоту битового образа, и они хранятся в переменной bm структуры BITMAP. Вот код программы:
hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap); hBrush = SelectObject(hdc, hBrush);
BitBlt(hdc, xStart, yStart, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, 0xE20746L); SelectObject(hdc, hBrush);
DeleteDC(hdcMem);
Функция BitBlt выполняет логическую операцию над приемным контекстом устройства (hdc), исходным контекстом устройства (hdcMem) и кистью, выбранной в приемном контексте устройства. Вы создаете контекст памяти, выбираете в него битовый образ, выбираете цветную кисть в контекст устройства вашей рабочей области, и вызываете BitBlt. Затем вы выбираете исходную кисть в контекст устройства вашего дисплея и удаляете контекст памяти.
Осталось объяснить значение ROP кода 0xE20746 приведенного фрагмента программы. Этот код задает Windows выполнение следующей логической операции:
((Destination ^ Pattern) & Source) ^ Destination
Если опять непонятно, попробуйте разобраться в следующем:
Pattern: |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
Source: |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
Destination: |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
Result: |
? |
? |
? |
? |
? |
? |
? |
? |
Для каждого черного бита битового образа (который будет выбран в исходный контекст памяти), вы хотите, чтобы приемный контекст устройства оставался неизменным. Это означает, что везде, где Source равен 0, вы хотите,
чтобы Result равнялся Destination:
Pattern: |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
Source: |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
Destination: |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
Result: |
? |
? |
1 |
0 |
? |
? |
1 |
0 |
Полдела сделано. Теперь для каждого белого бита битового образа вы хотите, чтобы приемный контекст закрашивался шаблоном. Кисть, выбранная вами в приемный контекст устройства — это шаблон. Таким образом, везде где Source равен 1, вы хотите, чтобы Result равнялся Pattern:
Pattern: |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
Source: |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
Destination: |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
Result: |
1 |
1 |
1 |
0 |
0 |
0 |
1 |
0 |
Это означает, что старшее слово ROP кода равняется 0xE2. Вы можете заглянуть в таблицу ROP кодов пакета
Microsoft Developer Studio и обнаружить, что полный ROP код равен 0xE20746.
Если обнаружится, что вы перепутали белые и черные биты при создании битового образа, то это легко исправить, используя другую логическую операцию:
Pattern: |
1 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
Source: |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
Destination: |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
Result: |
1 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
Теперь старшее слово ROP кода равно 0xB8, а весь ROP код равен 0xB8074A, что соответствует логической операции:
((Destination ^ Pattern) & Source) ^ Pattern
Теперь второй пример: вы можете заметить, что значки и курсоры состоят из двух битовых образов. Использование двух битовых образов позволяет этим объектам быть прозрачными или инвертировать цвет

138
закрываемых ими фрагментов экрана. Для монохромного значка или курсора, эти два битовых образа кодируются следующим образом:
Bitmap1: |
0 |
0 |
1 |
1 |
Bitmap2: |
0 |
1 |
0 |
1 |
Result: |
Черный |
Белый |
Цвет |
Инверсный |
|
|
|
экрана |
цвет экрана |
Windows выбирает битовый образ Bitmap1 в контекст памяти и использует функцию BitBlt с ROP кодом SRCAND для переноса битового образа на экран. Этот ROP код соответствует логической операции:
Destination & Source
Она сохраняет неизменными биты приемника, соответствующие единичным битам Bitmap1, и устанавливает в 0 биты, соответствующие нулевым битам Bitmap1. Затем Windows выбирает Bitmap2 в контекст устройства и использует функцию BitBlt с параметром SRCINVERT. Логическая операция такова:
Destination ^ Source
Данная операция сохраняет неизменными биты приемника, соответствующие нулевым битам Bitmap2, и инвертирует биты, соответствующие единичным битам Bitmap2.
Взгляните на первый и второй столбцы таблицы: Bitmap1 и SRCAND делают биты черными, а Bitmap2 и SRCINVERT инвертируют выбранные биты в белый цвет. Эти операции устанавливают белые и черные биты, которые составляют значок и курсор. Теперь посмотрите на третий и четвертый столбцы таблицы: Bitmap1 и SRCAND сохраняют дисплей неизменным, а Bitmap2 и SRCINVERT инвертируют цвета указанных битов. Эти операции делают значки и курсоры прозрачными или позволяют инвертировать цвет закрываемой области экрана.
Другой пример творческого использования ROP кодов приводится далее в этой главе при описании функции
GrayString.
Дополнительные сведения о контексте памяти
Мы использовали контекст памяти для передачи существующих битовых образов на экран. Вы можете также использовать контекст памяти для рисования на поверхности битового образа. Мы сделаем это в программе GRAFMENU в главе 10. Во-первых, вы строите контекст памяти:
hdcMem = CreateCompatibleDC(hdc);
Затем вы создаете битовый образ желаемого размера. Если вы хотите создать монохромный битовый образ, его можно сделать совместимым с hdcMem:
hBitmap = CreateCompatibleBitmap(hdcMem, xWidth, yHeight);
Для создания битового образа с такой же организацией цветов, как и у видеотерминала, сделайте битовый образ совместимым с hdc:
hBitmap = CreateCompatibleBitmap(hdc, xWidth, yHeight);
Теперь вы можете выбрать битовый образ в контекст памяти:
SelectObject(hdcMem, hBitmap);
А затем вы можете рисовать в этом контексте памяти (т. е. на поверхности битового образа), используя все функции GDI, рассмотренные в этой главе. Когда вы впервые создаете битовый образ, он содержит случайные биты. Поэтому есть смысл начать с использования функции PatBlt с ROP кодом WHITENESS или BLACKNESS для стирания фона контекста памяти.
Когда вы закончите рисование в контексте памяти, просто удалите его:
DeleteDC(hdcMem);
Теперь битовый образ будет содержать все, что вы нарисовали, пока он был выбран в контекст памяти.
Программа SCRAMBLE, показанная на рис. 4.26, очень "грубая", и не следовало бы показывать ее вам. Но она использует контекст памяти, как временный буфер для операций BitBlt, меняющих местами содержимое двух прямоугольных фрагментов экрана.
SCRAMBLE.MAK
#------------------------
# SCRAMBLE.MAK make file
#------------------------
scramble.exe : scramble.obj

139
$(LINKER) $(GUIFLAGS) -OUT:scramble.EXE scramble.obj $(GUILIBS)
scramble.obj : scramble.c $(CC) $(CFLAGS) scramble.c
SCRAMBLE.C
/*------------------------------------------------ |
|
SCRAMBLE.C -- |
Scramble(and Unscramble) Screen |
|
(c) Charles Petzold, 1996 |
------------------------------------------------ |
*/ |
#include <windows.h> #include <stdlib.h>
#define NUM 200
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static int iKeep [NUM][4];
HDC |
hdc, hdcMem; |
int |
cx, cy; |
HBITMAP |
hBitmap; |
int |
i, j, x1, y1, x2, y2; |
if(LockWindowUpdate(GetDesktopWindow()))
{ |
|
hdc |
= CreateDC("DISPLAY", NULL, NULL, NULL); |
hdcMem = CreateCompatibleDC(hdc); |
|
cx |
= GetSystemMetrics(SM_CXSCREEN) / 10; |
cy |
= GetSystemMetrics(SM_CYSCREEN) / 10; |
hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject(hdcMem, hBitmap);
srand((int) GetCurrentTime());
for(i = 0; i < 2; i++)
for(j = 0; j < NUM; j++)
{
if(i == 0)
{
iKeep [j] [0] = x1 = cx *(rand() % 10); iKeep [j] [1] = y1 = cy *(rand() % 10); iKeep [j] [2] = x2 = cx *(rand() % 10); iKeep [j] [3] = y2 = cy *(rand() % 10);
}
else
{
x1 = iKeep [NUM - 1 - j] [0];
y1 = iKeep [NUM - 1 - j] [1];
x2 = iKeep [NUM - 1 - j] [2];
y2 = iKeep [NUM - 1 - j] [3];
}
BitBlt(hdcMem, 0, 0, cx, cy, hdc, x1, y1, SRCCOPY);
BitBlt(hdc, x1, y1, cx, cy, hdc, x2, y2, SRCCOPY);
BitBlt(hdc, x2, y2, cx, cy, hdcMem, 0, 0, SRCCOPY);
Sleep(10);
}
DeleteDC(hdcMem);

140
DeleteDC(hdc);
DeleteObject(hBitmap);
LockWindowUpdate(NULL);
}
return FALSE;
}
Рис. 4.26 Программа SCRAMBLE
В программе SCRAMBLE нет оконной процедуры. В функции WinMain она получает контекст устройства для всего экрана:
hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
а также контекст памяти:
hdcMem = CreateCompatibleDC(hdc);
Затем она определяет размеры экрана и делит их на 10:
xSize = GetSystemMetrics(SM_CXSCREEN) / 10;
ySize = GetSystemMetrics(SM_CYSCREEN) / 10;
Программа использует эти размеры для создания битового образа:
hBitmap = CreateCompatibleBitmap(hdc, xSize, ySize);
и выбирает его в контекст памяти:
SelectObject(hdcMem, hBitmap);
Используя функцию rand языка C, программа SCRAMBLE формирует четыре случайных величины, кратные значениям xSize и ySize:
x1 = xSize *(rand() % 10);
y1 = ySize *(rand() % 10);
x2 = xSize *(rand() % 10);
y2 = ySize *(rand() % 10);
Программа меняет местами два прямоугольных блока дисплея, используя три функции BitBlt. Первая копирует прямоугольник с вершиной в точке (x1, y1) в контекст памяти:
BitBlt(hdcMem, 0, 0, xSize, ySize, hdc, x1, y1, SRCCOPY);
Вторая копирует прямоугольник с вершиной в точке (x2, y2) в прямоугольную область с вершиной в точке (x1, y1):
BitBlt(hdc, x1, y1, xSize, ySize, hdc, x2, y2, SRCCOPY);
Третья копирует прямоугольник из контекста памяти в прямоугольную область с вершиной в точке (x2, y2):
BitBlt(hdc, x2, y2, xSize, ySize, hdcMem, 0, 0, SRCCOPY);
Этот процесс эффективно меняет местами содержимое двух прямоугольников на дисплее. SCRAMBLE делает это 200 раз, что может привести к полному беспорядку на экране. Но этого не происходит, потому что программа SCRAMBLE отслеживает свои действия, и перед завершением восстанавливает экран.
Вы можете также использовать контекст памяти для копирования содержимого одного битового образа в другой. Предположим, вы хотите создать битовый образ, содержащий только левый верхний квадрант другого битового образа. Если исходный битовый образ имеет описатель hBitmap, то вы можете скопировать его размеры в структуру типа BITMAP:
GetObject(hBitmap, sizeof(BITMAP),(LPVOID) &bm);
и создать новый неинициализированный битовый образ размером в одну четверть исходного:
hBitmap2 = CreateBitmap(bm.bmWidth / 2, bm.bmHeight / 2, bm.bmPlanes, bm.bmBitsPixel, NULL);
Теперь создаются два контекста памяти и в них выбираются исходный и новый битовые образы:
hdcMem1 = CreateCompatibleDC(hdc); hdcMem2 = CreateCompatibleDC(hdc);
SelectObject(hdcMem1, hBitmap);
SelectObject(hdcMem2, hBitmap2);
Теперь копируем левый верхний квадрант первого битового образа во второй: