Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming_Windows_95_Part_I.pdf
Скачиваний:
96
Добавлен:
05.06.2014
Размер:
4.61 Mб
Скачать

129

Существуют также две функции для установки и чтения битов битового образа. Первая функция устанавливает биты:

SetDIBits(hdc, hBitmap, iStart, iNum, pBits, &bmi, iUsage);

Последние три параметра такие же, как у функции CreateDIBitmap. Параметр iStart определяет начальную сканлинию, адресуемую pBits. Он лежит в интервале от 0 (для нижней скан-линии) до высоты битового образа в пикселях — 1 (для верхней скан-линии). Параметр iNum задает число скан-линий, устанавливаемых в битовом образе.

Функция GetDIBits имеет такие же параметры:

GetDIBits(hdc, hBitmap, iStart, iNum, pBits, &bmi, iUsage);

В этом случае pBits указывает на буфер для записи битов битового образа. Функция устанавливает поля структуры BITMAPINFO для того, чтобы можно было определить размеры битового образа и таблицы цветов.

Битовый образ — объект GDI

Формат битового образа, предложенный в Windows 1.0, очень ограничен и почти полностью зависит от устройства вывода, для которого он создан. Для хранения файлов битовых образов на диске вам следует использовать формат DIB, а не устаревший формат битового образа. Однако, когда вам необходим битовый образ исключительно для использования в вашей программе, работа с битовым образом, зависящим от устройства, окажется значительно проще и производительней.

Создание битовых образов в программе

Windows содержит пять функций, которые позволяют вам в программе создать зависящий от устройства битовый образ — объект GDI. Первая функция CreateDIBitmap рассматривалась выше. Вот другие функции:

hBitmap = CreateBitmap(cxWidth, cyHeight, iPlanes, iBitsPixel, pBits); hBitmap = CreateBitmapIndirect(&bitmap);

hBitmap = CreateCompatibleBitmap(hdc, cxWidth, cyHeight); hBitmap = CreateDiscardableBitmap(hdc, cxWidth, cyHeight);

Во всех случаях параметры cxWidth и cyHeight — это ширина и высота битового образа в пикселях. В функции CreateBitmap параметры iPlanes и iBitsPixel — это число цветовых плоскостей и число битов цвета на пиксель в битовом образе. Хотя бы один из этих двух параметров должен быть равен 1. Если оба параметра равны 1, то функция строит монохромный битовый образ. (Мы вскоре остановимся на том, как цветовые плоскости и биты цвета представляют цвет.)

В функции CreateBitmap параметр pBits может быть установлен в NULL, если вы создаете неинициализированный битовый образ. Созданный битовый образ будет содержать случайные данные. В функциях CreateCompatibleBitmap и CreateDiscardableBitmap Windows использует контекст устройства, описываемый параметром hdc, для получения числа цветовых плоскостей и числа битов цвета на пиксель. Битовый образ, создаваемый этими функциями, будет неинициализированным.

Функция CreateBitmapIndirect схожа с функцией CreateBitmap за исключением того, что она использует структуру типа BITMAP для задания битового образа. Следующая таблица показывает поля этой структуры:

Поле

Тип

Описание

bmType

LONG

Установлено в 0

bmWidth

LONG

Ширина битового образа в пикселях

bmHeight

LONG

Высота битового образа в пикселях

bmWidthBytes

LONG

Ширина битового образа в байтах

bmPlanes

 

(должна быть четной)

WORD

Число цветовых плоскостей

bmBitsPixel

WORD

Число битов цвета на пиксель

bmBits

LPVOID

Указатель на массив битов

Поле bmWidthBytes должно быть четным числом — минимальным четным числом байтов, необходимым для хранения одной скан-линии. Массив битов, на который указывает bmBits, должен быть организован на базе поля bmWidthBytes. Если bm — структура типа BITMAP, то вы можете вычислить значение поля bmWidthBytes, используя следующее выражение:

bm.bmWidthBytes =(bm.bmWidth * bm.bmBitsPixel + 15) / 16 * 2;

Если Windows не может создать битовый образ (в основном из-за недостатка памяти), она возвратит NULL. Вам следует проверять возвращаемые значения каждой из функций создания битовых образов, особенно, если вы строите большие битовые образы.

130

После создания битового образа вы уже не можете изменить размер, число цветовых плоскостей или число битов цвета на пиксель. Вам надо будет создать новый битовый образ и передать биты из исходного битового образа в новый. Если вы имеете описатель битового образа, вы можете узнать его размер и цветовую организацию следующим образом:

GetObject(hBitmap, sizeof(BITMAP),(LPVOID) &bitmap);

Эта функция копирует информацию о битовом образе в структуру (с именем bitmap) типа BITMAP. Эта функция не устанавливает поле bmBits. Для доступа к битам битового образа вам нужно вызвать функцию:

GetBitmapBits(hBitmap, dwCount, pBits);

Она копирует dwCount бит в символьный массив по указателю pBits. Для того, чтобы быть уверенными, что все биты битового образа скопируются в этот массив, вы можете вычислить параметр dwCount на основе значений полей структуры битового образа:

dwCount =(DWORD) bitmap.bmWidthBytes * bitmap.bmHeight * bitmap.bmPlanes;

Вы можете также задать Windows скопировать символьный массив, содержащий биты битового образа, обратно в существующий битовый образ, используя функцию:

SetBitmapBits(hBitmap, dwCoint, pBits);

Поскольку битовые образы — это объекты GDI, вы должны удалить каждый созданный вами битовый образ:

DeleteObject(hBitmap);

Формат монохромного битового образа

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

Вы можете записать ряды битов (0 для черного и 1 для белого), которые в точности соответствуют этой сетке. Читая эти биты слева направо, вы можете записать каждые восемь битов в виде байта в шестнадцатеричной системе счисления. Если значение ширины битового образа не кратно 16, то добавьте справа столько нулевых байтов, сколько необходимо для того, чтобы получить четное число байтов:

0 1 0 1 0 0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 = 51 77 10 00 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00 0 0 0 1 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 13 77 50 00 0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00 0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 = 51 11 10 00

Ширина равна 20 пикселям, высота в скан-линиях равна 5 и ширина в байтах равна 4. Вы можете задать структуру BITMAP для этого битового образа следующим выражением:

static BITMAP bitmap = { 0, 20, 5, 4, 1, 1};

и сохранить биты в массиве типа BYTE:

static BYTE byBits [] = { 0x51, 0x77, 0x10, 0x00, 0x57, 0x77, 0x50, 0x00, 0x13, 0x77, 0x50, 0x00, 0x57, 0x77, 0x50, 0x00, 0x51, 0x11, 0x10, 0x00 };

Создание битового образа с помощью функции CreateBitmapIndirect осуществляется в два этапа:

bitmap.bmBits =(LPVOID) byBits;

hBitmap = CreateBitmapIndirect(&bitmap);

Другой вариант:

hBitmap = CreateBitmapIndirect(&bitmap); SetBitmapBits(hBitmap, sizeof(byBits), byBits);

Вы можете также создать битовый образ в одной инструкции:

131

hBitmap = CreateBitmap(20, 5, 1, 1, byBits);

Формат цветного битового образа

Цветной битовый образ в предыдущих версиях Windows немного более сложен и очень сильно зависим от устройства. Цветной битовый образ организован так, чтобы облегчить вывод битов на конкретное физическое устройство. Битовый образ организован или как набор цветовых плоскостей или имеет набор цветовых битов на пиксель. Это зависит от устройства вывода, для которого предназначен битовый образ.

Давайте сначала рассмотрим битовый образ, у которого bmBitsPixel равно 1 (что означает, что в нем 1 бит цвета на пиксель), а bmPlanes больше 1. Цветной битовый образ для 16-цветного VGA — это хороший пример. Windows использует четыре цветовых плоскости VGA для представления 16 цветов, поэтому bmPlanes равно 4. Массив битов начинается с верхней скан-линии. Цветовые плоскости для каждой скан-линии хранятся последовательно: красная плоскость — первой, зеленая плоскость — второй, затем голубая плоскость и плоскость интенсивности. Затем следует вторая скан-линия битового образа.

Битовый образ может также представлять цвет как некоторое число битов на пиксель. Предположим, что видеомонитор может представить 256 цветов, используя 8 бит (1 байт) на пиксель. Для каждой скан-линии первый байт представляет цвет самого левого пикселя, второй байт представляет цвет следующего пикселя, и т. д. Значение bmWidthBytes структуры BITMAP отражает увеличенную ширину каждой скан-линии в байтах, а значение bmWidth — это число пикселей в скан-линии.

Здесь есть одна тонкость: битовый образ не содержит никакой информации о том, как эти цветовые плоскости или биты цвета соответствуют реальным цветам устройства вывода. Конкретный битовый образ предназначен только для устройства, имеющего такую же организацию, как и битовый образ. Поэтому не рекомендуется использовать функции CreateBitmap или CreateBitmapIndirect для создания инициализированного цветного битового образа. Вам следует применять эти функции только для создания инициализированных или неинициализированных монохромных битовых образов.

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

При создании цветного битового образа, для которого совместимость с реальным графическим устройством вывода необязательна, следует использовать DIB.

Контекст памяти

Две функции — SetDIBitsToDevice и StretchDIBits позволяют вам воспроизвести массив битов на устройстве вывода. Тем не менее, даже если у вас есть описатель битового образа, то нет функции для рисования битового образа на поверхности отображения контекста устройства. Вы будете тщетно искать функцию, имеющую такой вид:

DrawBitmap(hdc, hBitmap, xStart, yStart); // Такой функции нет !!!

Эта функция копировала бы битовый образ в контекст устройства, заданный параметром hdc, начиная с логической точки (xStart, yStart). Мы напишем свою собственную функцию DrawBitmap позднее в этой главе. Однако для этого вам необходимо познакомиться с некоторыми концепциями, начиная с контекста памяти.

Контекст памяти (memory device context) — это контекст, имеющий поверхность отображения (display surface), существующую только в памяти. Вы можете создать контекст памяти, используя функцию CreateCompatibleDC:

hdcMem = CreateCompatibleDC(hdc);

Описатель hdc — описатель действительного открытого контекста устройства. Функция CreateCompatibleDC возвращает описатель контекста памяти. При создании контекста памяти все атрибуты устанавливаются в значения по умолчанию. Вы можете делать почти все, что вы захотите с этим контекстом памяти. Вы можете устанавливать атрибуты в значения, отличные от значений по умолчанию, получать текущие значения атрибутов, выбирать в него перья, кисти и регионы. И, конечно, вы можете даже рисовать на нем. Но это не имеет смысла делать прямо сейчас. И вот почему.

Когда вы впервые создаете контекст памяти, он имеет поверхность отображения, содержащую только 1 монохромный пиксель. Это очень маленькая поверхность отображения. (Не полагайтесь на то, что функция

GetDeviceCaps сообщит вам это. Все значения HORZSIZE, VERTSIZE, HORZRES, VERTRES, BITSPIXEL и PLANES для hdcMem будут установлены в значения, связанные с исходным hdc. Если бы функция GetDeviceCaps в действительности возвращала истинные значения, связанные с контекстом памяти, когда он был впервые создан, то для индексов HORZRES, VERTRES, BITSPIXEL и PLANES эти значения были бы равны 1.) Вам надо

132

увеличить поверхность отображения контекста памяти. Это осуществляется выбором битового образа в контекст памяти:

SelectObject(hdcMem, hBitmap);

Теперь поверхность отображения hdcMem имеет те же ширину, высоту и организацию цвета, что и битовый образ, описываемый hBitmap. Если начало координат окна и области вывода установлены по умолчанию, то логическая точка (0, 0) контекста памяти соответствует левому верхнему углу битового образа.

Если битовый образ уже содержит некоторое изображение, то это изображение становится частью поверхности отображения контекста памяти. Любые изменения битового образа (например, с использованием функции SetBitmapBits для установки другого массива битов в битовом образе) отразятся на поверхности отображения. Все, что вы рисуете в контексте памяти, немедленно отражается в битовом образе. Короче, битовый образ — это поверхность отображения контекста памяти.

Ранее были рассмотрены различные функции для создания битовых образов. Вот одна из них:

hBitmap = CreateCompatibleBitmap(hdc, xWidth, yHeight);

Если hdc — это обычный описатель контекста устройства для дисплея или принтера, то число цветовых плоскостей и число битов на пиксель созданного битового образа совпадает с соответствующими характеристиками устройства. Однако, если hdc — это описатель контекста памяти (и в него еще не выбран битовый образ), то функция CreateCompatibleBitmap возвращает монохромный битовый образ шириной xWidth и высотой yHeight пикселей.

Битовый образ — объект GDI. Ранее в этой главе было показано, как использовать функцию SelectObject для выбора перьев, кистей или регионов в контекст устройства, а далее мы узнаем, как применять эту функцию для выбора шрифта в контекст устройства. С помощью функции SelectObject можно выбирать эти четыре объекта GDI в контекст памяти. Однако, вы не можете выбирать битовый образ в обычный контекст устройства — только в контекст памяти.

Когда закончится работа с контекстом памяти, его надо удалить:

DeleteDC(hdcMem);

Итак, вы можете сказать: "Очень хорошо. Но мы еще не решили проблему отображения битового образа на экране. Все что мы сделали — научились выбирать его в контекст памяти. Что теперь?" Теперь мы должны научиться как переносить биты из одного контекста устройства в другой с помощью функции BitBlt.

Мощная функция BitBlt

Компьютерная графика включает в себя процедуру записи пикселей на устройство отображения. Ранее мы уже рассматривали некоторые пути выполнения этой задачи, но для больших и сложных манипуляций с пикселями в Windows есть только функции BitBlt, PatBlt и StretchBlt. BitBlt означает перенос блоков битов (bit block transfer). Функция BitBlt переносит пиксели, другими словами, это — универсальная растровая функция (raster blaster). Термин "transfer" не совсем справедлив по отношению к функции BitBlt. Она делает больше, чем просто перенос пикселей, — она осуществляет одну из 256 логических растровых операций над тремя наборами пикселей.

Функция PatBlt

PatBlt (pattern block transfer) — это простейшая из трех blt-функций. Она существенно отличается от функций BitBlt и StretchBlt тем, что использует только один контекст устройства. Но для начала функция PatBlt подходит вполне.

Раньше мы уже встречались с атрибутом контекста устройства, называемым режимом рисования. Этот атрибут может быть установлен в одно из 16 значений, соответствующих бинарной растровой операции (ROP2). Когда вы рисуете линию, режим рисования определяет тип логической операции, которую Windows реализует над пикселями пера и пикселями приемного контекста устройства. Функция PatBlt похожа на функции рисования линий с тем исключением, что она изменяет содержимое прямоугольной области приемного контекста устройства, а не только линии. Она выполняет логическую операцию с пикселями в этом прямоугольнике и в шаблоне (pattern). Шаблон — это просто другое название кисти (brush). Поэтому функция PatBlt использует кисть, выбранную в данный момент в контексте устройства.

Синтаксис вызова функции PatBlt таков:

PatBlt(hdc, xDest, yDest, xWidth, yHeight, dwROP);

Параметры xDest, yDest, xWidth, yHeight задаются в логических координатах. Логическая точка (xDest, yDest) задает левый верхний угол прямоугольника. Он имеет ширину xWidth и высоту yHeight единиц. (Смотри следующий раздел, озаглавленный "Координаты Blt", для более подробного определения этих величин.) Это и есть

133

та прямоугольная область, которую изменяет функция PatBlt. Логическая операция, которую выполняет функция PatBlt над кистью и приемным контекстом устройства, определяется параметром dwROP, представляющим собой двойное слово (32-битное целое) ROP кода. Этот код не имеет отношения ни к одному из кодов ROP2, используемых в режиме рисования.

В Windows существует 256 ROP2 кодов. Они определяют всевозможные логические комбинации исходной (source) области отображения, приемной (destination) области отображения и шаблона (или кисти). Драйвер устройства для видеомонитора поддерживает все 256 растровых операций, посредством использования "компилятора" (compiler) типов. Этот компилятор использует 32-разрядный ROP код для генерации последовательности машинных инструкций, реализующих эту логическую операцию над пикселями дисплея, а затем выполняет эти инструкции. Старшее слово 32-разрядного ROP кода — число от 0 до 255. Младшее слово — число, которое помогает компилятору драйвера устройства в генерации машинных кодов для этой логической операции. Пятнадцать из этих 256 кодов имеют имена.

Поскольку функция PatBlt использует только приемный контекст устройства и шаблон (и не использует исходный контекст устройства), она может реализовать только подмножество этих 256 ROP кодов — 16 ROP кодов, использующих только приемный контекст устройства и шаблон. Поддерживаемые функцией PatBlt растровые операции приведены ниже в таблице. Обратите внимание, что эта таблица очень похожа на таблицу ROP2 кодов.

Шаблон (Pattern (P))

1

1

0

0

Булева

ROP код

Имя

Приемник (Destination (D))

1

0

1

0

операция

 

 

 

 

 

 

 

(Boolean

 

 

 

 

 

 

 

operation)

 

 

Результаты: (Results)

0

0

0

0

0

0x000042

BLACKNESS

 

0

0

0

1

~(P | D)

0x0500A9

 

 

0

0

1

0

~P & D

0x0A0329

 

 

0

0

1

1

~P

0x0F0001

 

 

0

1

0

0

P & ~D

0x500325

 

 

0

1

0

1

~D

0x550009

DSTINVERT

 

0

1

1

0

P ^ D

0x5A0049

PATINVERT

 

0

1

1

1

~(P & D)

0x5F00E9

 

 

1

0

0

0

P & D

0xA000C9

 

 

1

0

0

1

~(P ^ D)

0xA50065

 

 

1

0

1

0

D

0xAA0029

 

 

1

0

1

1

~P | D

0xAF0229

 

 

1

1

0

0

P

0xF00021

PATCOPY

 

1

1

0

1

P | ~D

0xF50225

 

 

1

1

1

0

P | D

0xFA0089

 

 

1

1

1

1

1

0xFF0062

WHITENESS

Для монохромного контекста устройства бит равный 1 соответствует белому пикселю, а бит равный 0 — черному пикселю. Целиком черный или целиком белый приемник и шаблон — наиболее простой пример для начала рассмотрения работы функции PatBlt. Например, если вы вызываете:

PatBlt(hdc, xDest, yDest, xWidth, yHeight, 0x5F00E9L);

то прямоугольная область с вершиной в логической точке (xDest, yDest) и имеющая ширину xWidth пикселей и высоту yHeight пикселей, будет закрашена черным цветом, только если приемник был белым и в контекст устройства была выбрана кисть WHITE_BRUSH. В противном случае приемник будет закрашен белым. Конечно, даже в монохромном контексте устройства приемник и кисть могут быть полутоновыми комбинациями черных и белых пикселей. В этом случае Windows выполняет логическую операцию по принципу "pixel by pixel", что может привести к некоторым странным результатам. Например, если приемник был закрашен кистью GRAY_BRUSH, и она является текущей выбранной в контексте устройства, то:

PatBlt(hdc, xDest, yDest, xWidth, yHeight, PATINVERT);

установит приемник в чисто белый или в чисто черный цвет, в зависимости от того, как пиксели приемника совпадут с пикселями полутоновой кисти.

Цвет добавляет больше сложностей. Windows осуществляет отдельную логическую операцию для каждой цветовой плоскости или для каждого набора битов цвета, в зависимости от того, как организована память устройства.

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

PatBlt(hdc, xDest, yDest, xWidth, yHeight, BLACKNESS);

Если вам необходимо нарисовать белый прямоугольник, вы вызываете:

134

PatBlt(hdc, xDest, yDest, xWidth, yHeight, WHITENESS);

Функция:

PatBlt(hdc, xDest, yDest, xWidth, yHeight, DSTINVERT);

всегда инвертирует цвет прямоугольника. Если кисть WHITE_BRUSH выбрана в контексте устройства, то функция:

PatBlt(hdc, xDest, yDest, xWidth, yHeight, PATINVERT);

также инвертирует прямоугольник.

Вспомните функцию FillRect, закрашивающую кистью прямоугольную область:

FillRect(hdc, &rect, hBrush);

Следующий код является эквивалентным функции FillRect:

hBrush = SelectObject(hdc, hBrush); PatBlt(hdc, rect,left, rect.top,

rect.right — rect.left, rect.bottom — rect.top, PATCOPY);

SelectObject(hdc, hBrush);

Фактически, это код, используемый Windows для реализации функции FillRect. Когда вы вызываете функцию:

InvertRect(hdc, &rect);

Windows транслирует ее в функцию:

PatBlt(hdc, rect,left, rect.top, rect.right — rect.left, rect.bottom — rect.top, DSTINVERT);

Координаты Blt

Когда описывался синтаксис функции PatBlt, упомянулось, что точка (xDest, yDest) задает верхний левый угол прямоугольника, и этот прямоугольник имеет ширину xWidth и высоту yHeight единиц. Это не совсем корректно. Только в функциях GDI BitBlt, PatBlt и StretchBlt логические координаты прямоугольника задаются в терминах логической ширины и высоты относительно одной вершины. Все другие функции GDI для рисования, использующие ограничивающий прямоугольник, требуют задания координат в терминах левого верхнего и правого нижнего углов. В режиме отображения MM_TEXT указанное описание параметров функции PatBlt верно. Однако, для метрических режимов отображения — не верно. Если вы используете положительные значения xWidth и yHeight, то точка с координатами (xDest, yDest) должна быть левым нижним углом прямоугольника. Если вы хотите, чтобы точка (xDest, yDest) была левым верхним углом прямоугольника, то параметр yHeight должен быть установлен равным высоте прямоугольника, взятой со знаком минус.

Более точно, прямоугольник, с которым работает функция PatBlt, имеет логическую ширину, задаваемую абсолютным значением xWidth и логическую высоту, задаваемую абсолютным значением yHeight. Эти два параметра могут быть отрицательными. Прямоугольник определяется двумя углами, имеющими логические координаты (xDest, yDest) и (xDest + xWidth, yDest + yHeight). Верхний левый угол прямоугольника всегда включается в область, изменяемую функцией PatBlt. Правый нижний угол — всегда за ее пределами. В зависимости от режима отображения и знаков параметров xWidth и yHeight левым верхним углом прямоугольника может быть точка:

(xDest, yDest)

или

(xDest, yDest + yHeight)

или

(xDest + xWidth, yDest)

или

(xDest + xWidth, yDest + yHeight)

Если вы установите режим отображения MM_LOENGLISH и захотите использовать функцию PatBlt, изменяющую зону квадратного дюйма в левом верхнем углу рабочей области, вы можете использовать:

PatBlt(hdc, 0, 0, 100, -100, dwROP);

или

Соседние файлы в предмете Операционные системы