HDC hCompatDC;
HBITMAP hBmp;
hCompatDC = CreateCompatibleDC (hDC);
hBmp = CreateCompatibleBitmap (hDC, 500, 300)
SelectObject (hCompatDC, hBmp);
PatBlt (hCompatDC, 0,0, 500,300, PATCOPY);
// ... здесь мы можем выполнять любые операции по рисованию на нашем битмапе
// ... или передавать изображения между разными контекстами устройств.
DeleteDC (hCompatDC);
// ... Работаем с битмапом как с объектом GDI
DeleteObject (hBmp);
В этом примере надо отметить два момента: Во–первых, при создании битмапа в качестве прототипа задается обязательно контекст реального устройства (с заданной цветовой организацией), а не совместимого (который соответствует одному монохромному пикселю). Битмап, совместимый с совместимым контекстом устройства будет монохромным! Во–вторых, созданный совместимый битмап содержит произвольные данные, поэтому перед его использованием изображение надо очистить. В этом примере функция PatBlt закрашивает битмап текущей кистью (операция PATCOPY), иногда для начальной закраски используют не текущую кисть (по умолчанию — WHITE_BRUSH может быть не белой), а белый или черный цвета (операции WHITENESS, BLACKNESS). Это зависит от дальнейшего использования: фон битмапа должен совпадать с фоном окна или должен быть конкретного цвета.
Эта схема действительно удобна, если желательно, что бы цветовая организация битмапа соответствовала цветовой организации устройства, на котором он будет отображаться. Как правило это так и есть, кроме сравнительно редких случаев применения монохромных битмапов.
До тех пор, пока битмап остается выбран в совместимый контекст устройства, вы можете применять все функции GDI для рисования и коррекции изображения. Но одна из нужнейших задач — отображение битмапа на нужном устройстве — остается нерешенной. Специальных функций для отображения зависимых от устройства битмапов на контексте устройства в GDI нет, однако предусмотрен более универсальный и мощный механизм, обеспечивающий выполнение этой задачи — механизм передачи растровых изображений между контекстами устройств.
Операции передачи образов
Рассматривая применение битмапов мы обратили внимание на специальный механизм, осуществляющих передачу растровых изображений между различными контекстами устройств. Этот механизм называется операции по обмену блоками бит (bit block transfer, BLT) или тернарными растровыми операциями (ternary raster operation).
Основная идея растровых операций (слово тернарные часто опускают, в отличие от слова бинарные — см. раздел «Режим рисования», стр. 26) заключается в организации обмена данными между двумя контекстами устройств. Эти операции универсальны — они работают с любыми контекстами устройств, поддерживающими обмен растровыми изображениями (например, устройства типа плоттера такими возможностями, естественно, не обладают). Таким образом вы можете осуществить передачу изображения и между битмапом, выбранным в совместимый контекст устройства и реальным устройством на котором хотите это изображение показать, между двумя битмапами или передать имеющееся изображение с реального устройства в битмап или на другое устройство.
GDI содержит 3 функции, осуществляющих такую передачу изображений — PatBlt, BitBlt и StretchBlt (заметьте, что аббревиатура BLT произносится как БЛИТ):
BOOL PatBlt (
hDC, nX, nY, nWidth, nHeight, dwROP);
BOOL BitBlt (
hDestDC, nDestX, nDestY, nDestWidth, nDestHeight,
hSrcDC, nSrcX, nSrcY, dwROP);
BOOL StretchBlt (
hDestDC, nDestX, nDestY, nDestWidth, nDestHeight,
hSrcDC, nSrcX, nSrcY, nSrcWidth, nSrcHeight, dwROP);
Все три функции выполняют сходные операции — они строят результирующее изображение на контексте–приемнике, используя в качестве исходных данных:
изображение, создаваемое на приемнике при закраске фона текущей кистью, выбранной в контекст–приемник (это называется образцом,pattern).
изображение, существующее на контексте–источнике (исходное изображение, source).
изображение, существующее в данный момент на контексте–приемнике (имеющееся изображение, destination).
В процессе выполнения растровой операции эти три исходных изображения (битовых последовательности) комбинируются и получается результирующее изображение. Так как в операции участвуют три исходных последовательности, то операция получила название тернарной (ternary).
Код выполняемой операции задается параметром dwROP — индексом тернарной растровой операции.
В документации по SDK можно найти таблицу, перечисляющую индексы 256 возможных растровых операций, их имена и короткое пояснение к каждой операции. Причем имена присвоены только 15 наиболее употребляемым операциям. Таблица, представленная в документации имеет следующий вид:
Number | Hex ROP | Boolean function | Common Name |
0 | 00000042 | 0 | BLACKNESS |
... | |||
0D | 000D0B25 | PDSnaon | |
... |
Поле «Hex ROP» содержит индекс тернарной растровой операции, который вы должны использовать в качестве параметра dwROP. Поле «Boolean function» содержит пояснение к выполняемой операции, а поле «Common name» — имя растровой операции, если оно назначено. Однако разобраться в том, какая конкретно операция выполняется в процессе переноса изображения не так–то просто.
Попробуем пояснить это на примере: операция с индексом 000D0B25 обозначает операцию PDSnaon. Это обозначение содержит в обратной польской записи логические операции над битами, выполняемые в процессе растровой операции. Сначала указаны большими буквами используемые компоненты:
P:образец, (кисть,pattern)
D:существующее изображение (destination)
S:исходное изображение (source),
в общем случае для обозначения компонент операции используются эти три больших буквы, но порядок их перечисления зависит от выполняемой операции. После перечисления компонентов следуют маленькие буквы, указывающие выполняемые операции:
n:инверсия, not; операция использует 1 аргумент
a:пересечение, and; операция использует 2 аргумента
o:объединение, or; операция использует 2 аргумента
x:исключающее ИЛИ, xor; операция использует 2 аргумента
Для того, что бы понять как выполняются эти операции представим, что у нас есть стек, и каждая буква в записи указывает операцию: большая буква выполняет запись в стек, маленькая — операцию над нижними данными в стеке, причем они из стека извлекаются, а результат операции размещается в стеке. Посмотрим на примере:
Рисунок 17. Пример расшифровки обозначения растровой операции.
Таким образом, получаем последовательность операций для формирования результата. Однако использовать такую табличку со списком тернарных растровых операций может быть удобно только при получении справок. А вот если мы можем словами описать нужную последовательность действий, а для нее надо определить индекс растровой операции, то такая таблица совершенно неудобна (одного результата можно достичь, выполняя операции различным образом; даже если вы запишите требуемые действия в рассмотренной форме, нет никакой гарантии, что в таблице такая запись найдется).
Попробуем научиться как-то иначе получать индекс тернарной растровой операции.
Мы уже встречались с бинарными растровыми операциями (ROP2) когда рассматривали рисование линий. Сейчас мы воспользуемся примерно таким же подходом — мы будем исходить из предположения монохромных контекстов устройств (для простоты) и попробуем составить табличку, аналогичную той, что применялась для бинарных растровых операций:
Образец, кисть (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 |
Такая табличка позволяет описать все 256 тернарных операций, поэтому приводить ее целиком не имеет смысла. Однако нам будет удобно использовать подобную запись для определения индекса тернарной операции.
Попробуем, например, найти индекс растровой операции, в результате которой мы получим светлую точку, если:
а) контекст–источник имеет светлую точку
б) контекст–источник и контекст–приемник имеют темные точки
в) только в том случае, когда образец содержит темную точку
Имеется в виду операция ( (а) или (б)) и (в). Составим табличку:
Образец, кисть (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 |
Желаемый результат | 0 0 0 0 1 1 0 1 |
Как и в случае бинарных растровых операций мы можем использовать этот результат как номер операции (и заодно как старшее слово индекса). Этот номер равен 0b00001101 = 0x0D. Это уже рассмотренная нами операция с индексом 0x000D0B25 (PDSnaon).
Разобравшись с растровыми операциями, самое время разобраться с функциями, выполняющими эти операции. Самая простая из трех рассмотренных — функция PatBlt. Она не использует контекст–источник и выполняет операцию только над контекстом–приемником и образцом (фоном, полученным в результате закраски текущей кистью).
BOOL PatBlt (hDC, nX, nY, nWidth, nHeight, dwROP);
Эта функция может использоваться со всеми растровыми операциями, не применяющими контекст–источник. Из именованных растровых операций это:
BLACKNESS | — закрасить все черным |
DSTINVERT | — инвертировать изображение (сделать "негатив") |
PATCOPY | — закрасить кистью |
PATINVERT | — закрасить инвертированной кистью |
WHITENESS | — закрасить все белым |
Эта функция часто используется для начальной закраски областей (операции BLACKNESS, WHITENESS, PATCOPY) и для выделения фрагментов (DSTINVERT).
Следующая функция, которую мы рассмотрим:
BOOLBitBlt (
hDestDC, nDestX, nDestY, nDestWidth, nDestHeight,
hSrcDC, nSrcX, nSrcY, dwROP);
Она осуществляет передачу изображений между двумя контекстами устройств, при этом передается прямоугольный фрагмент, который на контексте-приемнике и на контексте-источнике имеет одинаковые размеры. При использовании этой функции надо быть достаточно осторожным — для задания координат и размеров используется логическая система координат, и логический размер изображения в обеих системах может быть различным.