基本介紹
- 中文名:元檔案
- 外文名:Metafiles
- 類型:存儲圖像的集合
- 釋義:一個可變長結構數組
關於元檔案,增強型元檔案,
關於元檔案
在內部,元檔案是一個可變長結構數組,稱為:元檔案記錄(metafile records)。元檔案中的第一個記錄描述了一些公共的信息,比如:創建圖像的設備的解析度、圖像的尺寸等等。剩下的記錄則是用於描述圖像的GDI函式操作記錄。當元檔案設備描述表(DC)被創建之後,GDI的操作記錄就會被存儲到元檔案中。這個DC是被在創建圖像期間所有的繪圖操作所需要的。當windows處理一個與元檔案DC相關聯的GDI函式時,它將轉換這個函式到適當的格式並以記錄的形式追加(存儲)到相關的元檔案中。
當圖像建立完畢,最後一個記錄也加到元檔案中之後,這個元檔案就可以採用剪貼簿來傳遞給其它的應用程式,或是嵌入到其它的檔案中、存儲到磁碟檔案中,也可以用於以後重複顯示之用。如果元檔案的記錄已經轉換為設備命令並且已被適當的設備處理了,那么該元檔案已經被顯示了(played)。
有兩種類型的元檔案:增強型元檔案和Windows型元檔案。增強型元檔案一般被用於用Win32 API編寫的應用程式中。它的格式包括:元檔案頭、GDI目標的句柄表、私有調色板和元檔案記錄數組。增強型元檔案提供真正的設備無關性。
Windows元檔案一般用於用Windows version 3.X API編寫的應用程式中。這種格式的元檔案是由一個元檔案頭和元檔案記錄數組組成。Windows元檔案在技術上有一定的限制,現在已經很少被使用了—它之所以現在還被支持,只是為了系統的兼容性。
增強型元檔案
增強型元檔案格式
================
程式設計師可以使用增強型元檔案來存儲用Win32 GDI函式創建的圖像(包括新的路徑和轉換函式)。因為增強型元檔案對於Win32 API是一種標準,以這種格式存儲的圖像可以從一個Win32應用程式拷貝到另一個應用程式。而且,因為這種格式是真正與設備無關的,所以保證圖像的尺寸與形狀在其它設備中也保持不變。
增強型元檔案
============
增強型元檔案是一個記錄的數組。而元檔案記錄是一個可變長的ENHMETARECORD結構。這個結構標識了記錄的類型、記錄的長度和包含的附加數據。
在增強型元檔案中的第一個記錄總是增強型元檔案頭。這個檔案頭描述了以下信息:
* 元檔案的尺寸 (以位元組為單位)
* 圖像的框線尺寸 (設備單位)
* 圖像的框線尺寸 (0.01 mm 為單位)
* 在元檔案中記錄的個數
* 到可選的文本描述的偏移
* 可選調色板的尺寸
* 原始設備分辯率 (像素單位)
* 原始設備分辯率 (mm單位)
可選的文本描述將用於描述圖像以及作者的一些信息,它一般放在元檔案頭的後面。可選的調色板指出了創建增強型元檔案所使用的顏色。剩下的記錄標識了用於創建圖像的GDI函式。下面的範例描述了系統是怎樣將GDI函式轉換為一個元檔案記錄的:
假設用戶調用了SetMapMode(4)這個GDI函式,經系統轉換後,它
將變為一個記錄存入元檔案,記錄的內容如下:
00000011 0000000C 00000004
其中值00000011指明了記錄的類型(11表示GDI函式SetMapMode函式),0C則是這個記錄的長度
(以位元組為單位),04標識了入口參數4。
增強型元檔案操作
================
要想在輸出設備上維護圖像的尺寸,Win32 API需要參考設備的分辯率(參考設備是指第一次顯示這個元
檔案時所在的設備),以及參考DC(就是關聯到參考設備上的DC)。當調用CreateEnhMetaFile()函式的
時候,應用程式必需提供一個DC句柄(參考DC),應用程式可以使用GetDC()或CreateDC()函式來獲得一
個參考DC,也可以將參考DC指定為NULL,此時系統將當前的顯示設備作為參考設備。
大多數應用程式創建元檔案圖像最後都會存儲到磁碟中,但這並不是必需的。如果應用程式想將圖像存儲到磁碟中,則它在調用CreateEnhMetaFile()函式時必需提供一個有效的檔案名稱。如果開發者不提供檔案名稱,則系統將自動的為其創建一個臨時檔案,並將元檔案存儲於記憶體中。
程式設計師也可以加一些描述本圖像及作者的一些信息到增強型元檔案中。應用程式可以在File Open dialog box(檔案打開對話框)中顯示這些信息,以給用戶一些有益提示。如果應用程式想向增強型元檔案中加入一個這樣的描述串,則他必需在調用CreateEnhMetaFile()函式時提供一個指向該描述串的指針。
當調用CreateEnhMetaFile()函式成功之後,該函式將返回一個標識元檔案的DC句柄。這個DC在與之關聯
的檔案(或者說是輸出設備)上是唯一的。當Windows系統在處理一個GDI函式時,如果它收到的是一個元檔案DC句柄,則系統將轉換GDI函式為一個增強型元檔案記錄,並將其追加到增強型元檔案的尾部。
當最後一個記錄追加到增強型元檔案之後,圖像就算繪製完畢。此時,應用程式可以調用
CloseEnhMetaFile()函式來關閉並刪除該元檔案DC,並返回一個標識增強型元檔案的句柄。應用程式可以使用該句柄來完成以下任務:
* 顯示存儲在增強型元檔案中的圖像(回放)
* 創建增強型元檔案的拷貝
* 枚舉、編輯、或拷貝增強型元檔案中的單個記錄
* 獲取增強型元檔案中可選的描述文本
* 獲得增強型元檔案頭的拷貝
* 獲取增強型元檔案的二進制版
* 枚舉可選調色板中的顏色
* 轉換增強型元檔案格式為Windows元檔案格式
應用程式可以使用CopyEnhMetaFile()函式來拷貝增強型元檔案,它支持標識增強型元檔案的句柄,也支持指向新檔案名稱的指針。
很多繪圖軟體或CAD軟體都需要編輯存儲在增強型元檔案中圖像的手段。雖然編輯增強型元檔案是一項比較複雜的任務,但系統仍提供這種方法。開發者可以使用EnumEnhMetaFile()函式,再結合其它的一些Win32函式就可以對單個的增強型元檔案記錄進行編輯(該函式有一個回調函式很有用)。
一些應用程式可能需要在FileOpen對話框裡顯示增強型元檔案的文本描述串,此時,程式設計師可以使用
GetEnhMetaFileDescription()函式來獲取該文本串。
一些應用程式使用GetEnhMetaFileBits()函式來獲取元檔案的內容,但是,在獲取內容之前,應用程式必需提供檔案的尺寸。獲取這個尺寸值,可調用GetEnhMetaFileHeader()函式先取得檔案頭,該檔案頭中的一個成員變數標識了檔案的尺寸。
要想使增強型元檔案在不同的輸出設備上保持顏色一致,應用程式可以調用CreatePalette()函式來創建調色板,並將其存儲到增強型元檔案中。其它的程式在顯示這個增強型元檔案之前,可獲取該調色板,並調用RealizePalette()函式來實現該調色板。應用程式可用增強型元檔案頭中的某個成員的值來判斷是否有調色板。如果有,可調用GetEnhMetaFilePaletteEntries()函式來獲取這個邏輯調色板。
Windows元檔案格式
=================
Win32 API支持Windows元檔案主要是為了保持系統向下兼容。以下內容是這種格式的一些技術限制:
* Windows元檔案是與應用程式、特定設備相關的。修改應用程式的映射模式或設備的分辯率,將影響
Windows元檔案的回放效果(圖像的顏色、尺寸可能會改變)。
* Windows元檔案頭包含的信息不足。它不包含諸如圖像的尺寸、創建圖像的設備的分辯率、描述文本串、調色板等等信息。
* Windows元檔案不支持新的曲線、通道、轉換函式。
* 一些Windows元檔案記錄不能被scaled。
* 與Windows元檔案相關聯的元檔案DC不能被查詢(也就是說,應用程式不能獲取該設備的分辯率、字型等等信息)。
* 要想轉換一個Windows元檔案到增強型元檔案,應用程式可以調用GetMetaFileBitsEx()函式來獲取
Windows元檔案數據,然後調用SetWinMetaFileBits()函式將數據轉換為增強型元檔案。
* 程式設計師在編寫Win32應用程式時,應儘量避免使用Windows元檔案格式,而應使用增強型元檔案格式。
創建一個增強型元檔案
====================
下面這節將描述一個範例,用以說明存放到磁碟上的增強型元檔案的創建方法。檔案名稱由用戶指定。
範例中使用了應用程式的視窗DC作為元檔案的參考DC(Windows將該設備的分辯率數據存儲於增強型元文
件頭中)。應用程式通過調用GetDC()函式來獲取這個DC.
範例使用了應用程式客戶區的大小作為圖像的框架大小(通過調用GetClientRect()函式來獲得)。然後
應用程式轉換設備單位為0.01mm單位,並將該值代入CreateEnhMetaFile()函式中。
範例顯示另外儲存為對話框,用戶可指定新檔案的名字。系統將向檔案名稱子串尾部追加三個字元:.EMF作為檔案的擴展名。然後將該名字傳送給CreateEnhMetaFile()函式。
範例也向增強型元檔案中加入了文本描述串,這個描述串是作為資源存放在串表中的。
以下是範例代碼:
/* Obtain a handle to a reference DC. */
hdcRef = GetDC(hWnd);
/*
* Determine the picture frame dimensions.
* iWidthMM is the display width in millimeters.
* iHeightMM is the display height in millimeters.
* iWidthPels is the display width in pixels.
* iHeightPels is the display height in pixels
*/
iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);
iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);
iWidthPels = GetDeviceCaps(hdcRef, HORZRES);
iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
/*
* Retrieve the coordinates of the client
* rectangle, in pixels.
*/
GetClientRect(hWnd, &rect);
/*
* Convert client coordinates to .01-mm units.
* Use iWidthMM, iWidthPels, iHeightMM, and
* iHeightPels to determine the number of
* .01-millimeter units per pixel in the x-
* and y-directions.
*/
rect.left = (rect.left * iWidthMM * 100)/iWidthPels;
rect.top = (rect.top * iHeightMM * 100)/iHeightPels;
rect.right = (rect.right * iiWidthMM * 100)/iWidthPels;
rect.bottom = (rect.bottom * iHeightMM * 100)/iHeightPels;
/* Load the filename filter from the string table. */
LoadString(hInst, IDS_FILTERSTRING,
(LPSTR)szFilter, sizeof(szFilter));
/*
* Replace the '%' separators that are embedded
* between the strings in the string-table entry
* with '\0'.
*/
for (i=0; szFilter[i]!='\0'; i++)
if (szFilter[i] == '%')
szFilter[i] = '\0';
/* Load the dialog title string from the table. */
LoadString(hInst, IDS_TITLESTRING,
(LPSTR)szTitle, sizeof(szTitle));
/* Initialize the OPENFILENAME members. */
szFile[0] = '\0';
Ofn.lStructSize = sizeof(OPENFILENAME);
Ofn.hwndOwner = hWnd;
Ofn.lpstrFilter = szFilter;
Ofn.lpstrFile= szFile;
Ofn.nMaxFile = sizeof(szFile);
Ofn.lpstrFileTitle = szFileTitle;
Ofn.nMaxFileTitle = sizeof(szFileTitle);
Ofn.lpstrInitialDir = (LPSTR)NULL;
Ofn.Flags = OFN_SHOWHELP | OFN_OVERWRITEPROMPT;
Ofn.lpstrTitle = szTitle;
/*
* Display the Filename common dialog box. The
* filename specified by the user is passed
* to the CreateEnhMetaFile function and used to
* store the metafile on disk.
*/
GetSaveFileName(&Ofn);
/* Load the description from the string table. */
LoadString(hInst, IDS_DESCRIPTIONSTRING,
(LPSTR)szDescription, sizeof(szDescription));
/*
* Replace the '%' string separators that are
* embedded between strings in the string-table
* entry with '\0'.
*/
for (i=0; szDescription[i]!='\0'; i++)
if (szDescription[i] == '%')
szDescription[i] = '\0';
/* Create the metafile DC. */
hdcMeta = CreateEnhMetaFile(hdcRef,
(LPTSTR) Ofn.lpstrFile,
&rect, (LPSTR)szDescription);
if (!hdcMeta)
errhandler("CreateEnhMetaFile", hWnd);
/* Release the reference DC. */
ReleaseDC(hWnd, hdcRef);
顯示圖像並將其存入增強型元檔案中
================================
這一節描述元檔案圖像的創建及存儲。範例繪製一個圖像到螢幕或元檔案。如果給出了顯示DC,則函式將
圖像繪製到螢幕上,如果給出了元檔案DC,則函式將圖像繪製到元檔案中。
以下是範例代碼:
void DrawOrStore(HWND hwnd, HDC hdcMeta, HDC hdcDisplay)
{
RECT rect;
HDC hDC;
int fnMapModeOld;
HBRUSH hbrOld;
/* Draw it to the display DC or store it in the metafile DC. */
if (hdcMeta)
hDC = hdcMeta;
else
hDC = hdcDisplay;
/* Set the mapping mode in the DC. */
fnMapModeOld = SetMapMode(hDC, MM_LOENGLISH);
/* Find the midpoint of the client area. */
GetClientRect(hwnd, (LPRECT)&rect);
DPtoLP(hDC, (LPPOINT)&rect, 2);
/* Select a gray brush. */
hbrOld = SelectObject(hDC, GetStockObject(GRAY_BRUSH));
/* Draw a circle with a one inch raduis. */
Ellipse(hDC, (rect.right/2 - 100), (rect.bottom/2 + 100),
(rect.right/2 + 100), (rect.bottom/2 - 100));
/* Perform additional drawing here. */
/* Set the device context back to its original state. */
SetMapMode(hDC, fnMapModeOld);
SelectObject(hDC, hbrOld);
}
打開一個增強型元檔案並顯示它的內容
==================================
這一節描述了應用程式怎樣打開一個存放於磁碟上的增強型元檔案,並將元檔案圖像顯示在視窗客戶區的方法。
範例通過使用OpenFile對話框來讓用戶選擇一個增強型元檔案,並將選擇的檔案名稱發給GetEnhMetaFile()函式,該函式返回一個標識該檔案的句柄。這個句柄就可以傳給PlayEnhMetaFile()函式來顯示元檔案圖像。
以下是範例代碼:
LoadString(hInst, IDS_FILTERSTRING,
(LPSTR)szFilter, sizeof(szFilter));
/*
* Replace occurrences of '%' string separator
* with '\0'.
*/
for (i=0; szFilter[i]!='\0'; i++)
if (szFilter[i] == '%')
szFilter[i] = '\0';
LoadString(hInst, IDS_DEFEXTSTRING,
(LPSTR)szDefExt, sizeof(szFilter));
/*
* Use the OpenFilename common dialog box
* to obtain the desired filename.
*/
szFile[0] = '\0';
Ofn.lStructSize = sizeof(OPENFILENAME);
Ofn.hwndOwner = hWnd;
Ofn.lpstrFilter = szFilter;
Ofn.lpstrCustomFilter = (LPSTR)NULL;
Ofn.nMaxCustFilter = 0L;
Ofn.nFilterIndex = 1L;
Ofn.lpstrFile = szFile;
Ofn.nMaxFile = sizeof(szFile);
Ofn.lpstrFileTitle = szFileTitle;
Ofn.nMaxFileTitle = sizeof(szFileTitle);
Ofn.lpstrInitialDir = (LPSTR) NULL;
Ofn.lpstrTitle = (LPSTR)NULL;
Ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
Ofn.nFileOffset = 0;
Ofn.nFileExtension = 0;
Ofn.lpstrDefExt = szDefExt;
GetOpenFileName(&Ofn);
/* Open the metafile. */
hemf = GetEnhMetaFile(Ofn.lpstrFile);
/* Retrieve a handle to a window DC. */
hDC = GetDC(hWnd);
/* Retrieve the client rectangle dimensions. */
GetClientRect(hWnd, &rct);
/* Draw the picture. */
PlayEnhMetaFile(hDC, hemf, &rct);
/* Release the metafile handle. */
DeleteEnhMetaFile(hemf);
/* Release the window DC. */
ReleaseDC(hWnd, hDC);
編輯一個增強型元檔案
====================
要想編輯存儲於增強型元檔案中的圖像,應用程式必需執行下面的一些任務:
1 使用HIT-TESTING去捕捉游標坐標並獲取用戶想編輯的目標的位置(line, arc, rectangle, ellipse etc.)
2 轉換這些坐標為邏輯單位(或全局單位)
3 調用EnumEnhMetaFile()函式,並且檢查每一個元檔案記錄
4 判斷給定的記錄是否與GDI繪製函式相符合
5 如果符合,則判斷元檔案中該記錄的坐標是否與截獲的用戶坐標相同。
6 如果坐標也相同,則說明找到了用戶想編輯的記錄,可在螢幕上清除該目標
7 從元檔案中刪除這個記錄,並保存該記錄的地址到一個指針變數中
8 允許用戶重畫該目標或替換目標
9 轉換新繪製的GDI函式為一個或多個增強型元檔案的記錄
10 保存這些記錄到增強型元檔案中
元檔案函式
==========
下面的函式用於增強型元檔案:
CreateEnhMetaFile
DeleteEnhMetaFile
EnhMetaFileProc
EnumEnhMetaFile
GdiComment
GetWinMetaFileBits
SetEnhMetaFileBits
SetWinMetaFileBits
下面的函式為提供系統兼容而被保留:
DeleteMetaFile
EnumMetaFile
EnumMetaFileProc
GetMetaFile
GetMetaFileBitsEx
PlayMetaFile
SetMetaFileBitsEx
增強型元檔案所用到的結構
========================
結構:ENHMETAHEADER
typedef struct tagENHMETAHEADER { // enmh
DWORD iType;
DWORD nSize;
RECTL rclBounds;
RECTL rclFrame;
DWORD dSignature;
DWORD nVersion;
DWORD nBytes;
DWORD nRecords;
WORD nHandles;
WORD sReserved;
DWORD nDescription;
DWORD offDescription;
DWORD nPalEntries;
SIZEL szlDevice;
SIZEL szlMillimeters;
DWORD cbPixelFormat;
DWORD offPixelFormat;
DWORD bOpenGL;
} ENHMETAHEADER;
結構:ENHMETARECORD
typedef struct tagENHMETARECORD { // enmr
DWORD iType;
DWORD nSize;
DWORD dParm[1];
} ENHMETARECORD;
結構:HANDLETABLE
typedef struct tagHANDLETABLE { // ht
HGDIOBJ objectHandle[1];
} HANDLETABLE;
結構:METAHEADER
typedef struct tagMETAHEADER { // mh
WORD mtType;
WORD mtHeaderSize;
WORD mtVersion;
DWORD mtSize;
WORD mtNoObjects;
DWORD mtMaxRecord;
WORD mtNoParameters;
} METAHEADER;
結構:METARECORD
typedef struct tagMETARECORD { // mr
DWORD rdSize;
WORD rdFunction;
WORD rdParm[1];
} METARECORD;