Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы

       

Запись метафайла


Практически все графические редакторы, такие как Paintbrush, Micrografix Designer, Photo Finish и т. п., при записи фрагмента изображения в Clipboard сохраняют данные не только в своих собственных форматах, но и в формате метафайла. В этом разделе мы рассмотрим процесс записи метафайла в Clipboard, а немного позже расскажем об особенностях чтения метафайла из Clipboard и проигрывания его в контексте отображения.

Сначала перечислим шаги, которые ваше приложение должно выполнить для записи метафайла в Clipboard.

Создать метафайл в оперативной памяти (но не в виде файла на диске)

С помощью функций SetWindowOrgEx и SetWindowExtEx установить в контексте метафайла размер и начало логической системы координат и размеры изображения

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

Закрыть метафайл

Заказать глобальный блок памяти для заголовка метафайла (структура типа METAFILEPICT) и зафиксировать этот блок памяти

Заполнить поля структуры METAFILEPICT, указав режим отображения, размеры изображения и идентификатор метафайла

Расфиксировать блок памяти, содержащий заголовок метафайла METAFILEPICT

Открыть Clipboard функцией OpenClipboard (если Clipboard не был открыт ранее при записи данных в других форматах)

Сбросить содержимое Clipboard функцией EmptyClipboard (если Clipboard не был очищен ранее при записи данных в других форматах)

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

Закрыть Clipboard функцией CloseClipboard (если в дальнейшем не предполагается сохранять в Clipboard данные, пользуясь другими форматами)



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




Первые шаги процедуры записи метафайла в Clipboard (создание метафайла и настройка контекста метафайла) несложны.

Для создания метафайла в оперативной памяти следует воспользоваться функцией CreateMetaFile, передав ей в качестве параметра значение NULL:

hdcMeta = CreateMetaFile((LPSTR)NULL);

Далее необходимо выбрать начало логической системы координат и размеры изображения. Как правило, для метафайла, сохраняемого в Clipboard, используются режимы отображения MM_ISOTROPIC или MM_ANISOTROPIC. В этом случае при записи графического изображения размером (ptSize.x, ptSize.y) вы можете определить начало логической системы координат и размеры изображены следующим образом:

SetWindowOrgEx(hdcMeta, 0, 0, NULL); SetWindowExtEx(hdcMeta, ptSize.x, ptSize.y, NULL);

Отметим, что на данном этапе не следует устанавливать режим отображения в контексте метафайла явным вызовом функции SetMapMode. Режим отображения устанавливается позже при заполнении заголовка метафайла.

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

// Создаем палитру из DIB hPal = DIBCreatePalette(hDib); if(hPal) { // Выбираем и реализуем палитру в контекст // метафайла hOldPal = SelectPalette(hdcMeta, hPal, FALSE); RealizePalette(hdcMeta); } // Рисуем DIB в контексте метафайла DIBPaint(hdcMeta, 0, 0, hDib);

// Выбираем старую палитру if(hPal) SelectPalette(hdcMeta, hOldPal, FALSE);

Процедура рисования DIB в контексте метафайла ничем не отличается от аналогичной процедуры для контекста отображения. Эта процедура, а также функции DIBCreatePalette и DIBPaint были описаны в 14 томе "Библиотеки системного программиста" (см. разделы "Рисование изображений DIB" и "Приложение BMPINFO").

После того как изображение нарисовано, следует закрыть метафайл:

hMF = CloseMetaFile(hdcMeta);

Теперь нужно создать и заполнить заголовок метафайла.



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

hMeta = GlobalAlloc(GHND, sizeof(METAFILEPICT)); lpMeta = (LPMETAFILEPICT)GlobalLock(hMeta);

Структура METAFILEPICT и указатель на нее описаны в файле windows.h следующим образом:

typedef struct tagMETAFILEPICT

{

int mm;

int xExt;

int yExt;

HMETAFILE hMF;

} METAFILEPICT;

typedef METAFILEPICT FAR* LPMETAFILEPICT;

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

lpMeta->mm = MM_ANISOTROPIC;

Поле hMF должно содержать идентификатор метафайла, полученный от функции CloseMetaFile:

lpMeta->hMF = hMF;

Наибольшую трудность вызывает заполнение полей xExt и yExt. Эти поля заполняются по-разному в зависимости от выбранного режима отображения.

Если используются режимы отображения, отличные от MM_ISOTROPIC или MM_ANISOTROPIC, в поля xExt и yExt следует записать размеры изображения в тех единицах измерения, которые соответствуют режиму отображения, указанному в поле mm.

Однако, как мы уже говорили, для обеспечения возможности масштабирования изображения после вставки из Clipboard практически все приложения устанавливают в заголовке метафайла режимы отображения MM_ISOTROPIC или MM_ANISOTROPIC. Для этих режимов возможны несколько вариантов заполнения полей xExt и yExt.

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

Во-вторых, приложение может записать в поля xExt и yExt положительные значения - предпочтительный размер изображения в сотых долях миллиметра (такая единица измерения используется в режиме отображения MM_HIMETRIC). Если приложение прочитало из Clipboard изображение, для которого установлены предпочтительные размеры, оно может использовать эти размеры для рисования.Оно также может проигнорировать предпочтительные размеры и нарисовать изображение по-своему, что, однако, приведет в некоторых случаях к искажению изображения (например, при уменьшении размеров битового изображения).

В-третьих, приложение может записать в поля xExt и yExt отрицательные значения. Отношение этих отрицательных значений передает информацию об отношении ширины к высоте изображения. При этом информация об абсолютных размерах изображения не передается.

Итак, выбрав один из перечисленных выше вариантов, необходимо заполнить поля xExt и yExt, завершив таким образом формирование заголовка метафайла:

lpMeta->xExt = xPicSize; lpMeta->yExt = yPicSize;

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

GlobalUnlock(hMeta); if(hMeta) SetClipboardData(CF_METAFILEPICT, hMeta);


Содержание раздела