Смекни!
smekni.com

Основы графического вывода (стр. 25 из 26)

&dwWritten, (LPOVERLAPPED)0L);}

// пишем палитру при ее наличии

if (nColors) {

// палитра присутствует

WriteFile (

hf, (LPVOID) (rgbs), sizeof (RGBQUAD) * nColors,

&dwWritten, (LPOVERLAPPED)0L);}

// записываем полученное от GDIизображение

// строго говоря, надо убедиться, что это не битмап OS/2 по ds.dsBmih.biSize

// и либо использовать ds.dsBmih.biSizeImage, либо вычислять самим, если это

// битмап OS/2

WriteFile (

hf, (LPVOID) (ds.dsBm.bmBits), ds.dsBmih.biSizeImage,

&dwWritten, (LPOVERLAPPED)0L);

// вместо ds.dsBm.bmBitsможно воспользоваться lpDataиз предыдущего примера

CloseHandle (hf);}

// считаем, что больше DIB–секция нам не нужна

DeleteObject (hbmpDibSection);

3) Загрузка DIB–файла с помощью DIB–секции 1:

HWND hwnd; // предположим, что мы будем отображать

// DIB в этом окне

HANDLE hfDib; // данные для проецирования DIB файла

HANDLE hmapDib;

BY_HANDLE_FILE_INFORMATION finfo;

LPBITMAPFILEHEADER lpbmfh;

HANDLE hmapTemp; // packed DIB

LPBITMAPINFO lpbmi;

HBITMAP hbmpDibSection;

HDC hdcMem;

HDC hdcDisplay;

LONG dwOffBits; // смещение до данных изображения

LPVOID lpData;

lpbmi = (LPBITMAPINFO)0L; // инициализируем указатель — дальше проверим

// скопируем временное проецирование для "PackedDIB"

hfDib = CreateFile (

"TestDIB.bmp", GENERIC_READ, 0, 0,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (hfDib != INVALID_HANDLE_VALUE) {

hmapDib = CreateFileMapping (hfDib, 0, PAGE_READ|SEC_COMMIT, 0,0, 0);

lpbmfh = (LPBITMAPFILEHEADER)MapViewOfFile (hmapDib, FILE_MAP_READ, 0,0, 0);

// вычислим смещение данных изображения в "PackedDIB" для использования позже

dwOffBits = lpbmfh->bfOffBits - sizeof (BITMAPFILEHEADER);

// создадимвременноепроецирование

GetFileInformationByHandle (hfDib, &finfo);

finfo.nFileSizeLow -= sizeof (BITMAPFILEHEADER);

hmapTemp = CreateFileMapping (

INVALID_HANDLE_VALUE, 0, PAGE_READWRITE|SEC_COMMIT, 0,finfo.nFileSizeLow, 0);

lpbmi = (LPBITMAPINFO)MapViewOfFile (hmapTemp, FILE_MAP_WRITE, 0,0, 0);

if (lpbmi && lpbmfh) {

// собственно копирование файла...

CopyMemory (

(LPVOID)lpbmi,

(LPVOID) (lpbmfh + 1),

finfo.nFileSizeLow);}

UnmapViewOfFile ( (LPVOID)pbmfh);

CloseHandle (hmapDib);

CloseHandle (hfDib);}

// если проецирование создано и файл скопирован, то отобразим его в окне

if (lpbmi) {

hdcDisplay = GetWindowDC (hwnd);

hbmpDibSection = CreateDIBSection (

hdcDisplay, lpbmi, DIB_RGB_COLORS, &lpData, hmap, dwOffBits);

hdcMem = CreateCompatibleDC (hdcDisplay);

SelectObject (hdcMem, hbmpDibSection);

BitBlt (

hdcDisplay, 0,0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight,

hdcMem, 0,0,

SRCCOPY);

ReleaseDC (hwnd, hdcDisplay);

DeleteDC (hdcMem);

DeleteObject (hbmpDibSection);

UnmapViewOfFile ( (LPVOID)lpbmi);

CloseHandle (hmapTemp);}

Метафайлы

Сохранять изображения можно не только в виде битмапов. Альтернативный, причем достаточно интересный, метод представлен с помощью так называемых метафайлов (metafile).

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

Метафайл можно рассматривать как специфичное записывающее устройство, которое запоминает все функции GDI, которые выполняются над ним. То есть, когда вы пытаетесь, скажем, нарисовать эллипс, метафайл запомнит команду для рисования эллипса в данном месте и данного размера. Позже вы сможете с помощью специальной функции осуществить воспроизведение метафайла на требуемом контексте устройства.

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

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

метафайл компактнее битмапов

время отображения битмапов меньше времени воспроизведения метафайла

при использовании обычных, зависимых от устройства битмапов возможны проблемы с передачей цветов и с переносом на другие устройства, с метафайлом таких проблем меньше

при изменении размера битмапа возможно существенное снижение качества изображения, метафайл менее чувствителен к этому

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

Полезно, кроме того, узнать, каких вещей сделать с помощью метафайла невозможно:

Метафайл не может использовать переменных. Запоминается выполняемая функция GDI не с выражениями, использованными для вычисления аргументов, а с их численными значениями. При записи метафайла сохраняется список вызываемых функций GDI с использованными числовыми значениями аргументов. Чему они соответствуют, выяснится при воспроизведении метафайла.

Метафайл не сохраняет никаких GDI функций, начинающихся со слова Get... более того, метафайл не выполняет никаких действий, связанных с возвращением информации в программу — так как во время воспроизведения метафайла многих переменных может не быть, или они могут быть по другим адресам.

Метафайл не использует функций, ссылающихся на него как на реальный контекст устройства: CreateCompatibleDC, ReleaseDC, DeleteDC, CreateCompatibleBitmap, CreateDiscardableBitmap, PlayMetaFile (имеется в виду что на одном метафайле нельзя воспроизвести другой[13]).

С метафайлами не работают также функции: GrayString, DrawIcon, SetBrushOrg, FillRect, FrameRect.

Однако не все ограничения метафайла являются непреодолимыми, многие из них легко обходятся. Например, мы хотим с помощью метафайла выводить изображение, занимающее все окно. При создании метафайла мы предположим, что изображение размещается в прямоугольнике, скажем, 100 x 100 единиц. Таким образом созданный метафайл будет воспроизводить изображение в области 100 x 100, независимо от размеров окна; но перед воспроизведением метафайла мы можем выбрать собственную систему координат такой, что бы размер внутренней области окна в этой системе координат был равен 100 x 100, и тогда изображение займет все окно.

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

А как можно работать с метафайлом? Сначала метафайл должен быть создан — это делается с помощью функции:

HDC CreateMetaFile (lpszFileName);

HDC CreateEnhMetaFile (hdcRef, lpszFileName, lprectBound, lpszDescription); 1

Данная функция создает контекст устройства, связанный с метафайлом. Параметр lpszFileName указывает имя файла, в который будет происходить запись команд. Здесь надо ввести дополнительное понятие. Метафайлы разделяют на метафайлы, использующие диск (файл) для записи команд (disk-based), и метафайлы, использующие блок памяти (memory-based) для хранения набора команд. Если вы указали имя создаваемого файла, то создается метафайл, использующий диск. Однако в качестве lpszFileName может быть указан NULL, тогда будет создан метафайл, использующий память.

Функция CreateEnhMetaFile создает метафайл в формате Win32, обладающий несколько большими возможностями, чем обычный. В частности в заголовке метафайла будет сохранена информация о размерах записанного (параметр lprectBound) в нем изображения и о разрешающей способности устройства (параметр hdcRef), для которого метафайл создан. Помимо этого в заголовок включается небольшая строка, поясняющая название сохраненного рисунка и название приложения, осуществившего запись этого рисунка (параметр lpszDescription). При вызове функции CreateEnhMetaFile допускается задавать нулевые значения hdcRef (по умолчанию будут использованы характеристики дисплея) и lprectBound (размер изображения будет вычисляться в процессе записи рисунка).

Создаваемые этими функциями метафайлы — разные объекты, так что для работы с метафайлами Win32 необходимо использовать свой набор функций, а для работы с метафайлами Windows 3.x — свой. В именах функций, работающих с метафайлами Win32 как правило присутствует текст ...EnhMetaFile.

В процессе записи метафайла Win32 можно задать текст комментария:

BOOL GdiComment (hdcEnhMF, cbSize, lpData);

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

HMETAFILE CloseMetaFile (hdcMetaFile);

HENHMETAFILECloseEnhMetaFile (hdcEnhMetaFile); 1

При этом уничтожается связанный с метафайлом контекст устройства и возвращается хендл метафайла, который может использоваться для его воспроизведения. Этот метафайл является объектом GDI, так что надо очень внимательно следить за удалением этого объекта. В отличие от большинства объектов GDI вместо функции DeleteObject должна быть использована функция:

BOOL DeleteMetaFile (hMF);

BOOL DeleteEnhMetaFile (hEnhMF); 1

Где hMF (hEnhMF) — хендл метафайла (а не контекста устройства). При этом метафайл как объект GDI уничтожается, а файл, содержащий его, остается на диске. Если вы хотите удалить и этот файл тоже, то либо воспользуйтесь функцией для удаления файлов стандартной библиотеки времени выполнения, например unlink, либо функцией Win32 APIDeleteFile.

Если вы уже записали метафайл на диск, то вы можете легко создать объект GDI, соответствующий этому метафайлу с помощью функции:

HMETAFILE GetMetaFile (lpszFileName);

HENHMETAFILE GetEnhMetaFile (lpszFileName); 1

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

HMETAFILE SetMetaFileBits (HGLOBAL hGMemMetaFile);

HMETAFILE SetMetaFileBitsBetter (HGLOBAL hGMemMetaFile);

HENHMETAFILE SetEnhMetaFileBits (cbBuffer, lpData); 1

HENHMETAFILE SetWinMetaFileBits (cbBuffer, lpData, hdcRef, lpMETAFILEPICT); 1

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