Запись метафайла
Практически все графические редакторы, такие как 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);