47952 (597372), страница 12
Текст из файла (страница 12)
Независимый от устройства битмап (DIB) объектом GDI не является. Он загружается в виде одного или нескольких блоков данных (подробнее о работе с блоками памяти см. в разделе «Диспетчер памяти »), его идентифицируют либо указатели на эти данные, либо хендл блока памяти, в котором этот битмап располагается (это зависит от применяемых функций). Если битмап сохраняется на диске (в виде отдельного файла или в виде ресурса приложения), то это непременно DIB.
После загрузки в память DIB может быть представлен как непосредственно в виде независимого от устройства битмапа, так и он может быть превращен в DDB. В том случае, если его представление соответствует DIB, то он реализован в виде нескольких взаимосвязанных структур данных, описывающих информацию о битмапе (заголовок и палитра) и непосредственно изображение. Иногда эти структуры размещаются в разных областях данных (тогда битмап идентифицируется указателями на эти данные), а иногда в одной — в этом случае структура этой области данных примерно соответствует содержимому файла с DIB, но без заголовка файла — такое представление называется упакованный DIB (packed DIB). В последнем случае для идентификации независимого от устройства битмапа может хватить одного хендла блока, содержащего упакованный DIB, однако этот хендл не является хендлом битмапа (HBITMAP).
При работе с DIB следует выбрать один из трех путей:
1) Работать с DIB непосредственно. Для этого предназначен достаточно большой набор функций, реализованный и в Windows API и в Win32 API.
Получение битмапа из файла или ресурса приложения;
Если битмап представлен в виде файла, то его загрузку необходимо выполнить самостоятельно; по счастью эта процедура может быть сведена всего к нескольким строчкам исходного кода. Можно, конечно, воспользоваться функциями LoadImage 1 или LoadBitmap, но это приведет к получению DDB, причем с организацией, соответствующей дисплею и соответствующего текущей выбранной палитре. Это может быть неудобно, особенно при необходимости этот битмап позже вывести на печать — для этого лучше воспользоваться либо самим DIB, либо DDB, созданным под характеристики и под палитру, применяемую на устройстве отображения. Такую операцию можно выполнить, используя вместо функции LoadBitmap пару функций FindResource и LoadResource, что позволит получить непосредственно сам DIB. (Вместо LoadImage надо прочитать файл с битмапом).
Сохранение битмапа в файле;
Эту операцию надо выполнить самостоятельно, разобравшись со структурами, описывающими DIB, и записав их в файл в необходимом порядке.
Отображение битмапа на контексте устройства;
В отличие от DDB для DIB предусмотрены специальные функции, передающие изображение битмапа на указанный контекст устройства, близкие к функциям передачи растровых изображений между контекстами устройств. В отличие от DDB для DIB не требуется предварительно связывать битмап с контекстом устройства, эти функции осуществляют перенос изображения непосредственно из загруженного в память DIB на контекст устройства.
2) Работать с создаваемым промежуточным DDB. При этом DIB считывается и записывается также, как и в случае (1), однако дальше он преобразуется в DDB с которым и осуществляется все необходимая работа. Этот вариант является одним из самых эффективных, хотя и громоздких — он позволяет получить обычный битмап с организацией, наиболее точно соответствующей устройству отображения, что позволяет как сэкономить ресурсы, так и ускорить процесс вывода.
3) Создать ассоциацию DIB с контекстом устройства. Этот путь похож на выбор обычного битмапа в контекст устройства с помощью функции SelectObject, однако осуществляется другими средствами и разным образом в разных API (Windows или Win32).
В случае 16ти разрядной платформы Windows 3.x вы можете использовать специальный DIB драйвер, поставляемый в составе многих компиляторов
В случае Win32 вы можете использовать так называемую DIB–секцию, которая является своеобразным гибридом обычного битмапа (для нее возвращается HBITMAP, что позволяет применять ее как объект GDI) и независимого от устройства — в памяти она представлена как нормальный DIB.
Обзор метафайлов
Альтернативный метод сохранения изображений представлен в виде метафайлов (metafile). Метафайл в строгом смысле рисунка не хранит, он сохраняет только последовательность команд (вызовов функций GDI) формирующих изображение.
Для работы с метафайлами в Windows содержится достаточно полный набор функций, практически исключающий необходимость делать что–либо самостоятельно. Общие операции над метафайлами можно разделить на операции по созданию метафайла, то есть его записи и операции по воспроизведению метафайла на нужном контексте устройства.
Создание метафайлов;
Для создания метафайла создается специальный контекст устройства; все дальнейшие операции по рисованию на этом контексте запоминаются и сохраняются в виде метафайла. При удалении контекста устройства, связанного с метафайлом, создается объект GDI, представляющий данный метафайл. Этот метафайл может быть представлен либо в оперативной памяти, либо в виде файла — смотря по тому, как вы создавали контекст устройства метафайла. Никаких специальных шагов для записи метафайла на диск предпринимать не надо.
Воспроизведение метафайла;
Для воспроизведения метафайла предназначена специальная функция, которая последовательно выполняет все сохраненные в метафайле команды на указанном контексте устройства. Для ее применения необходимо указать хендл метафайла (объекта GDI), который возвращается либо при удалении контекста устройства, либо при вызове специальной функции, загружающей метафайл с диска в оперативную память.
В какой-то степени можно сравнить метафайл с микропрограммой, осуществляющей вывод изображения.
Зависимые от устройства битмапы
Обычный, зависимый от устройства битмап является простым образом видеопамяти. Его организация отражает особенности аппаратуры, на которой он должен воспроизводиться. DDB в приложении представляется как объект GDI; аналогично описанию перьев или кистей DDB описывается с помощью специальной структуры (BITMAP) и доступен посредством хендла этого объекта типа (HBITMAP).
typedef struct tagBITMAP {
int bmType;
int bmWidth;
int bmHeight;
int bmWidthBytes;
BYTE bmPlanes;
BYTE bmBitsPixel;
LPSTR bmBits;
} BITMAP;
Поле bmType должно быть 0, поля bmWidth и bmHeight определяют размеры изображения, bmPlanes и bmBitsPixel используются для указания способа кодирования информации о цвете точки и для указания максимального количества цветов.
Использование двух полей bmPlanes и bmBitsPixel связано с особенностями хранения цветного изображения разными видеоадаптерами. Например, CGA, IBM 8514 или SVGA в некоторых режимах для задания цвета пикселя отводят несколько последовательных бит памяти (CGA — 2 бита, IBM — 8 бит, SVGA — до 32 бит на каждый пиксель). А адаптеры типа EGA или VGA (и, конечно, в некоторых режимах SVGA) содержат несколько так называемых битовых плоскостей, или планов (planes). В каждом плане одному пикселю соответствует только один бит, а цвет задается комбинацией бит в разных планах. Поле bmPlanes структуры BITMAP определяет количество цветовых планов, а поле bmBitsPixel — количество последовательных бит, отведенных для задания цвета пикселя а одном плане. По крайней мере один из этих параметров равен 1 (или оба — для монохромных битмапов).
Изображение в битмапе хранится разделенным на строки растра (scan line). Длина каждой строки округляется в большую сторону до ближайшей четной границы (кратна 2 байтам) и задается полем bmWidthBytes. Для вычисления длины строки надо произведение bmWidth * bmBitsPixel разделить на 8 и округлить в сторону завышения до ближайшего четного числа.
Если битмап содержит несколько цветовых планов, то строки разных цветовых планов одной строки растра размещаются последовательно друг за другом, затем для другой строки и так далее.
Поле bmBits теоретически должно указывать на собственно данные битмапа (массив строк растра). Однако оно используется далеко не всегда — вы его можете задавать, но когда информацию о битмапе вам возвращает система (функция GetObject), то это поле не инициализируется — для получения данных битмапа существует специальная функция — GetBitmapBits.
Создание зависимого от устройства битмапа
Создание зависимых от устройства битмапов — случай сравнительно редкий; обычно битмапы готовятся специальным графическим редактором и позже загружаются в память либо из файла, либо из ресурсов приложения. В связи с этим предлагаемый здесь материал нужен, в основном, для более близкого «фамильярного» знакомства с DDB; на практике использоваться этот материал будет редко. Предположим, что мы хотим создать битмап для использования в качестве кисти. Кисть всегда имеет размер 8x8 пикселей. Кроме того, для упрощения, мы будем предполагать, что используется монохромный битмап. Монохромный — так как изображение этого битмапа мы опишем сами, а описание цветного битмапа непосредственно в приложении — крайне неэффективное решение. Кроме того, монохромные битмапы обрабатываются несколько особым образом, так что есть повод обратить внимание на их особенности. Итак, пусть мы хотим получить следующую картинку:
Рисунок 16. Подготавливаемый рисунок монохромного битмапа (при использовании такого битмапа в качестве кисти мы получим "кирпичную стену").
Обратите внимание на нумерацию байтов и бит в байте — байты (не слова) нумеруются слева–направо, а биты в байте — справа–налево. Подготовим для этой картинки данные битмапа: так как ширина битмапа 8, количество бит на пиксель 1, то в одной строке растра должно быть 8 бит, значит ее длина 2 байта (четное число) или одно слово.
static WORD wBits[]={
0x00FF, 0x00C0, 0x00C0, 0x00C0, 0x00FF, 0x000C, 0x000C, 0x000C};
Далее мы можем создать битмап, содержащий эту картинку. Для этого можно воспользоваться одной из следующих функций:
HBITMAP CreateBitmap (cxWidth, cyHeight, nPlanes, nBitsPixel, lpBits);
HBITMAP CreateBitmapIndirect (lpBitmap);
HBITMAP CreateCompatibleBitmap (hDC, cxWidth, cyHeight);
HBITMAP CreateDiscardableBitmap (hDC, cxWidth, cyHeight);
Функция CreateBitmap позволяет создать битмап с заданными характеристиками. В нашем случае это будет выглядеть так:
HBITMAP hBmp= CreateBitmap (8, 8, 1, 1, wBits);
Функция CreateBitmapIndirect позволяет сначала описать структуру типа BITMAP, а затем создать битмап по этой структуре. В нашем примере эту функцию можно использовать, например, таким образом:
static BITMAP stBmp= {
0, // bmType
8,8, // bmWidth, bmHeight
2, // bmWidthBytes
1,1, // bmPlanes, bmBitsPixel
NULL // bmBits};
HBITMAP hBmp= CreateBitmapIndirect (&stBmp);
SetBitmapBits (hBmp, sizeof (wBits), wBits);
Конечно, мы могли установить адрес образа (wBits) в поле bmBits и обойтись без функции SetBitmapBits но так мы рассмотрим на одну функцию больше.
В некоторых случаях мы будем создавать битмапы, которые должны быть по своим характеристикам совместимы с конкретным контекстом устройства. Для этого предназначена функция CreateCompatibleBitmap, которая создает битмап указанного размера и такой же организации, как указанный контекст устройства. Изображение при этом не задается — битмап содержит набор случайных данных. Позже вы можете воспользоваться, например, функцией SetBitmapBits для задания данных битмапа.
Внимание! При создании совместимого битмапа надо указывать контекст реального устройства, совместимость с которым требуется. Если указать совместимый контекст устройства в качестве прототипа, то будет создан монохромный битмап.
Функция CreateDiscardableBitmap используется крайне редко, так как создаваемый с ее помощью битмап может быть удален из памяти диспетчером при необходимости.
Независимо от способа создания, существует одна особенность в применении монохромных битмапов: при его отображении на цветном устройстве для представления точек битмапа будут использоваться не черный и белый цвета, а текущий цвет фона (для всех бит, равных 0, — обычно это белый цвет) и текущий цвет текста (для всех бит, равных 1, обычно это черный цвет). Таким образом вы можете в определенной мере управлять отображением монохромного битмапа с помощью функций SetTextColor и SetBkColor.
При необходимости вы можете узнать характеристики битмапа, используя функцию GetObject:
BITMAP stBmp;
GetObject (hBmp, sizeof (stBmp), &stBmp);
Однако эта функция не устанавливает поле bmBits структуры BITMAP. Для получения данных битмапа надо воспользоваться функцией:
LPSTR GetBitmapBits (hBitmap, dwMaxSize, lpBuffer);
Еще несколько функций могут использоваться совершенно специфичным способом:















