18.2 增強型圖元文件(emf)
18.2.1 創建並顯示增強型圖元文件的步驟
(1)創建:hdcEMF = CreateEnhMetaFile(hdcRef,szFilename,lpRect,lpDescription);
| 參數 |
含義 |
| hdcRef |
參考設備環境,NULL時表示以屏幕為參考 |
| szFileName |
指定文件名時,創建磁盤文件(.EMF)。為NULL時創建內存圖元文件 |
| lpRect |
用於描述圖元文件的大小和位置(以0.01mm為單位),可用它精確定義圖元文件的物理尺寸 |
| lpDescription |
對圖元文件的一段說明。包括創建應用程序的名字、一個NULL字符、對圖元文件的一段說明以及兩個NULL字符。 |
| 返回值 |
增強型圖元文件DC。(注意不是圖元文件的句柄,要獲得實際的圖元文件句柄,得調用CloseEnhMetaFile函數) |
(2)關閉圖元文件hEmf = CloseEnhMetaFile(hdcEMF);返回圖元文件句柄
(3)顯示圖元文件 PlayEnhMetaFile(hdc,hEmf,&rect);
| 參數 |
含義 |
| hdc |
設備環境句柄 |
| hEmf |
圖元文件句柄 |
| lpRect |
指定顯示區域(邏輯單位),GDI會縮放圖像以適應該矩形范圍 |
(4)刪除圖元文件 DeleteEnhMetaFile(hEmf);
【Emf1程序】
①創建圖元文件時,矩形和畫線的坐標大小並不重要,重要的是坐標間的對應關系。可以將他們同時加倍或同時減去一個常數,結果是一樣的。
②圖像會被拉伸,以滿足PlayEnhMetaFile函數中指定的矩形尺寸。
③這個例子中,圖形的對角線會出現不完全落在頂點上,這是Windows在存儲圖元文件中坐標的處理方式造成的,會在后面加以解決。

/*------------------------------------------------------------ EMF1.C -- Enhanced Metafile Demo #1 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("EMF1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Enhanced Metafile Demo #1"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HENHMETAFILE hEmf; HDC hdc,hdcEMF; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, NULL, NULL, NULL); //4個參數全為NULL Rectangle(hdcEMF, 100, 100, 200, 200); MoveToEx(hdcEMF, 100, 100, NULL); //左上——右下 LineTo(hdcEMF, 200, 200); MoveToEx(hdcEMF, 200, 100, NULL); //右上——左下 LineTo(hdcEMF, 100, 200); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,並保存在靜態變量中 return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect區域內顯示圖元文件 EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: DeleteEnhMetaFile(hEmf); //刪除內存中的圖元文件 PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
18.2.2 窺探增強型圖元文件的內部機制
(1)實例圖解增強型EMF文件結構
(2)文件結構:頭記錄(ENHMETAHEADER)、各記錄(ENHMETARECORD)、文件結尾(EMR_EOF)
①頭記錄ENHMETAHEADER
| 偏移量 |
字段 |
含義 |
| 0x00 |
DWORD iType; |
總是等於EMR_HEADER(即1) |
| 0x04 |
DWORD nSize; |
結構的大小。如本例0x90,注意這個大小是含描述字符串的長度,即等於sizeof(ENHMETAHEADER)+nDescription*2 |
| 0x08 |
RECTL rclBounds; |
以像素為單位的矩形邊框: 如左上角(100,100),右下角(200,200) |
| 0x18 |
RECTL rclFrame; |
以0.01mm為單位的圖像尺寸 如本例(0x09D6,0x09DE)即(2518,2526) 由HORZSIZE/HORZRES或VERTSIZE/VERTRES換算得來。 |
| 0x28 |
DWORD dSignature; |
ENHMETA_SIGNATURE=“EMF”,即0x464D4520 |
| 0x2C |
DWORD nVersion; |
0x00010000 |
| 0x30 |
DWORD nBytes; |
以字節為單位的文件總長度。本例0xFC |
| 0x34 |
DWORD nRecords; |
文件含有的記錄數。本例0x0000007。一個頭記錄、五個GDI函數調用和一個文件結束記錄 |
| 0x38 |
WORD nHandles; |
句柄表中的句柄數。本例為0x0001。通常表示在圖元文件中使用的GDI對象(如畫筆、畫刷、字體)的非默認句柄的數目。GDI為自己保留了第一個,所以本例為1。 |
| 0x3A |
WORD sReserved; |
|
| 0x3C |
DWORD nDescription; |
描述字符串的字符的個數。本例為0x12(18)個,注意含\0。 |
| 0x40 |
DWORD offDescription; |
描述字符串在文件中的起始偏移位置,跟在szlMicrometers字段的后面。本例為0x0000006C。注意,每個字符用UNICODE編碼(占2個字節)。 |
| 0x44 |
DWORD nPalEntries; |
調色板的顏色條目的個數。本例為0 |
| 0x48 |
SIZEL szlDevice; |
以像素為單位的設備分辨率。這里的設備由CreatEnhMetaFile函數的第一個參數,為NULL時表示屏幕設備1366×768,該值等於GetDeviceCaps的HORZRES和VERTRES。 |
| 0x50 |
SIZEL szlMillimeters; |
以mm為單位的設備分辨率。本例為344×194。該值等GetDeviceCaps的HORZSIZE和VERTSIZE。 |
| 0x58 |
DWORD cbPixelFormat; |
像素格式的尺寸 |
| 0x5C |
DWORD offPixelFormat; |
像素格式的起始偏移位置 |
| 0x60 |
DWORD bOpenGL; |
在不含OpenGL記錄時,該值為FALSE |
| 0x64 |
SIZEL szlMicrometers |
參考設備的尺寸(單位:微米)。本例344000和194000 |
②每條記錄(ENHMEARECORD)——一般記錄GDI函數的調用
| 字段 |
含義 |
| DWORD iType |
記錄類型(如Rectangle、MoveToEx、SetWindowExtEx等) 在WINGDI.H中定義,以EMR_開頭 |
| DWORD nSize |
該記錄的長度 |
| DWORD dParm[1] |
存放參數的數組(一個或多個dParam字段) |
③文件結尾(EMR_EOF):20字節
【EMF2程序】
/*------------------------------------------------------------ EMF2.C -- Enhanced Metafile Demo #2 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("EMF2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Enhanced Metafile Demo #2"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HENHMETAFILE hEmf; HDC hdc,hdcEMF; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf2.emf"), NULL, TEXT("EMF2\0EMF Demo #2\0")); if (!hdcEMF) return 0; Rectangle(hdcEMF, 100, 100, 200, 200); MoveToEx(hdcEMF, 100, 100, NULL); //左上——右下 LineTo(hdcEMF, 200, 200); MoveToEx(hdcEMF, 200, 100, NULL); //右上——左下 LineTo(hdcEMF, 100, 200); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,這里不用靜態. DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf2.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect區域內顯示圖元文件 DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
18.2.3 圖元文件和GDI對象
(1)如何存儲GDI對象(如,畫筆、畫刷,注意不是GDI繪圖命令)
(2)創建畫筆、畫刷的調用會被存儲到圖元文件內部。這些非備用的GDI對象會被從1開始編號。
(3)圖元文件也會存儲SelectObject和DeleteObject等函數調用
(4)EMREXTCREATEPEN結構體
| 字段 |
含義 |
| EMR emr |
包含emr.iType和emr.nSize,是圖元文件的基本結構 |
| DWORD ihPen |
圖元文件GDI對象句柄表中的索引值 |
| DWORD offBmi |
如果指定位圖時,表示位圖相對於該記錄的偏移 |
| DWORD cbBmi |
如果指定位圖時,表示位圖的大小(單位:字節) |
| DWORD offBits |
如果指定位圖時,表示畫筆位圖的像素數據的相對該記錄的偏移 |
| DWORD cbBits |
位圖像素數據的大小(單位:字節) |
| EXTLOGPEN elp; |
擴展的邏輯畫筆 |
(5)EXTLOGPEN結構體
| 字段 |
含義 |
| DWORD elpPenStyle |
可取PS_GEOMETRIC、PS_COSMETIC、PS_DASH等 |
| DWORD elpWidth |
當指定為PS_GEOMETRIC時,表示畫筆的寬度(邏輯單位),否則為1,表示1像素的寬度。 |
| UINT elpBrushStyle |
畫筆的畫刷樣式,如BS_HATCHED、BS_SOLID |
| COLORREF elpColor |
畫筆顏色 |
| ULONG_PTR elpHatch |
當elpBrushStyle為BS_PATTERN時,指向位圖的句柄。 當elpBrushStyle為BS_SOLID或BS_HOLLOW時,則忽略。 |
| DWORD elpNumEntries |
調色板的條目數。如果elpPenStyle沒有指定為PS_USERSTYLE,則該值為0 |
| DWORD elpStyleEntry[1] |
調色板顏色數組 |
【圖解GDI對象存儲】

【EMF3程序】

/*------------------------------------------------------------ EMF3.C -- Enhanced Metafile Demo #3 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("EMF3") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Enhanced Metafile Demo #3"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HENHMETAFILE hEmf; HDC hdc,hdcEMF; PAINTSTRUCT ps ; RECT rect ; LOGBRUSH lb; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf3.emf"), NULL, TEXT("EMF3\0EMF Demo #3\0")); if (!hdcEMF) return 0; //創建並選入畫刷 SelectObject(hdcEMF, CreateSolidBrush(RGB(0, 0, 255))); lb.lbStyle = BS_SOLID; lb.lbColor = RGB(255, 0, 0); lb.lbHatch = 0; //創建並選入畫筆 SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, 5, &lb, 0, NULL)); #if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, 100, 100, 201, 201); #else Rectangle(hdcEMF, 101, 101, 202, 202); #endif Rectangle(hdcEMF, 100, 100, 200, 200); MoveToEx(hdcEMF, 100, 100, NULL); //左上——右下 LineTo(hdcEMF, 200, 200); MoveToEx(hdcEMF, 200, 100, NULL); //右上——左下 LineTo(hdcEMF, 100, 200); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN))); DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,這里不用靜態. DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf3.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect區域內顯示圖元文件 DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
18.2.4 圖元文件和位圖
(1)GDI會與設備相關的位圖自動轉換 為與設備無關的位圖(DIB)。
(2)調用StretchBlt時,圖元文件內部使用的是StretchDIBits函數,而不是StretchBlt。
(3)EMR_STRETCH記錄的長度達4024字節,這里包含了緊縮型的DIB位圖數據。
(4)EMRSTRETCH結構體
| 字段 |
含義 |
| EMR emr |
包含兩個DWORD型的字段emr.iType和emr.nSize,該字段是所有圖元文件記錄的基本結構。 |
| RECTL rclBounds |
邊界矩形(單位:設備單位) |
| LONG xDest |
目標矩形的左上角x坐標(邏輯單位) |
| LONG yDest |
目標矩形的左上角y坐標(邏輯單位) |
| LONG cxDest |
目標矩形的寬度(邏輯單位) |
| LONG cyDest |
目標矩形的高度(邏輯單位) |
| DWORD dwRop |
光柵操作碼 |
| LONG xSrc |
源矩形的左上角x坐標(邏輯單位) |
| LONG ySrc |
源矩形的左上角y坐標(邏輯單位) |
| XFORM xformSrc |
世界坐標到邏輯坐標的變換矩陣 typedef struct tagXFORM { /* xfrm */ FLOAT eM11; FLOAT eM12; FLOAT eM21; FLOAT eM22; FLOAT eDx; FLOAT eDy; } XFORM; |
| COLORREF crBkColorSrc |
源設備環境DC的背景顏色,是個RGB值。 |
| DWORD iUsageSrc |
BITMAPINFO結構體的bmiColors字段。可取如下值:DIB_PAL_COLORS或DIB_RGB_COLORS。 |
| DWORD offBmiSrc |
BITMAPINFO結構的偏移 |
| DWORD cbBmiSrc |
BITMAPINFO結構體的大小 |
| DWORD offBitsSrc |
位圖像素數據的偏移 |
| DWORD cbBitsSrc |
像素數據的多少(單位:字節) |
| LONG cxSrc |
源矩形的寬度 |
| LONG cySrc |
源矩形的高度 |
【EMF4程序】

/*------------------------------------------------------------ EMF4.C -- Enhanced Metafile Demo #4 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #define OEMRESOURCE //要定義這個,才能使用OBM_CLOSE圖標 #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("EMF4") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Enhanced Metafile Demo #4"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HENHMETAFILE hEmf; HDC hdc,hdcEMF,hdcMem; PAINTSTRUCT ps ; RECT rect ; HBITMAP hbm; BITMAP bm; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf4.emf"), NULL, TEXT("EMF4\0EMF Demo #4\0")); if (!hdcEMF) return 0; hbm = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CLOSE)); GetObject(hbm, sizeof(BITMAP), &bm); hdcMem = CreateCompatibleDC(hdcEMF); SelectObject(hdcMem, hbm); //只有這個GDI函數才會被寫到圖元文件中 StretchBlt(hdcEMF, 100, 100, 100, 100, hdcMem,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY); DeleteDC(hdcMem); DeleteObject(hbm); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,這里不用靜態. DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf4.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect區域內顯示圖元文件 DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
【 圖解EMF4.emf文件結構】

18.2.5 枚舉圖元文件
(1)枚舉函數EnumEnhMetaFile
| 參數 |
含義 |
| HDC hdc |
顯示圖元文件的設備環境句柄 |
| HENHMETAFILE hemf |
圖元文件句柄 |
| ENHMFENUMPROC lpEnhMetaFunc |
枚舉回調函數,每讀取一條記錄,會調用一次該函數。包括頭記錄和文件結束記錄。通常返回TRUE,返回FALSE時結束枚舉過程。 |
| LPVOID lpData |
傳給枚舉回調函數的額外參數 |
| CONST RECT *lpRect |
在指定的矩形區內顯示圖元文件 |
(2)枚舉回調函數——自定義的,要作為EnumEnhMetaFile函數的第3個參數。
| 參數 |
含義 |
| HDC hdc |
顯示圖元文件的設備環境句柄 |
| HANDLETABLE *lpHTable |
指向HANDLETABLE結構體,這里存儲了圖元文件中用到的所有的非備用GDI對象的句柄。可以用指針讀取出來,如lpHTable->objectHandles[2]讀取編號為2的GDI對象。 Typedef struct tagHANDLETABLE { HGIDOBJ objectHandle[1]; }HANDLETABLE。 |
| CONST ENHMETARECORD *lpEMFR |
該結構前面己經解釋過來,主要用來描述每個記錄的類型,長度及一個或多個參數。 |
| int nObj |
圖元文件中用到的非備用GDI對象的句柄數量。如用到了畫筆和畫刷,則nObj=3。(2+1) |
| LPARAM lpData |
額外數據 |
▲本例中共有3個GDI對象的句柄,編號為0、1、2。
①當第1次調用枚舉回調函數時,HANDLETABLE中的第1個元素為圖元文件的句柄,第2、3個元素設為0,表示為本例中用到的畫筆和畫刷預留位置。
②PlayEnhMetaFileRecord時讀取到EMR_CREATEBRUSHINDIRECT時,會創建第1個GDI對象(編號為1),即新建一個畫刷,並把這個畫刷存儲在lpHTable->objectHandles[1]中。
③當PlayEnhMetaFileRecord到EMR_SELECTOBJECT記錄時,GDI會從HANDLETABLE結構體中讀取到這個GDI對象的實際句柄,並傳給SelectObject函數。
④當PlayEnhMetaFileRecord讀到EMR_DELETEOBJECT時,會將該數組lpHTable->objectHandles[1]置0.
(3)PlayEnhMetaFileRecord函數——回放單獨一條增強型圖元文件記錄。
【Emf5程序】
/*------------------------------------------------------------ EMF5.C -- Enhanced Metafile Demo #5 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("EMF5") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("Enhanced Metafile Demo #5"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } int CALLBACK EnhMetaFileProc(HDC hdc, HANDLETABLE* pHandleTable, CONST ENHMETARECORD* pEmfRecord, int iHandles, LPARAM pData) { PlayEnhMetaFileRecord(hdc, pHandleTable, pEmfRecord, iHandles); return TRUE; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HENHMETAFILE hEmf; HDC hdc,hdcEMF; PAINTSTRUCT ps ; RECT rect ; LOGBRUSH lb; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf5.emf"), NULL, TEXT("EMF5\0EMF Demo #5\0")); if (!hdcEMF) return 0; //創建並選入畫刷 SelectObject(hdcEMF, CreateSolidBrush(RGB(0, 0, 255))); lb.lbStyle = BS_SOLID; lb.lbColor = RGB(255, 0, 0); lb.lbHatch = 0; //創建並選入畫筆 SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, 5, &lb, 0, NULL)); #if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, 100, 100, 201, 201); #else Rectangle(hdcEMF, 101, 101, 202, 202); #endif Rectangle(hdcEMF, 100, 100, 200, 200); MoveToEx(hdcEMF, 100, 100, NULL); //左上——右下 LineTo(hdcEMF, 200, 200); MoveToEx(hdcEMF, 200, 100, NULL); //右上——左下 LineTo(hdcEMF, 100, 200); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN))); DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,這里不用靜態. DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf5.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { //與EMF3程序不同,這里使用枚舉函數,而不使用PlayEnhMetaFile //PlayEnhMetaFile(hdc, hEmf, &rect); EnumEnhMetaFile(hdc, hEmf, EnhMetaFileProc, NULL, &rect); DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
【Emf6程序】

/*------------------------------------------------------------ EMF6.C -- Enhanced Metafile Demo #6 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("EMF6"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("Enhanced Metafile Demo #6"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //自定義的枚舉圖元文件的回調函數。 int CALLBACK EnhMetaFileProc(HDC hdc, HANDLETABLE* pHandleTable, CONST ENHMETARECORD* pEmfRecord, int iHandles, LPARAM pData) { ENHMETARECORD* pEmfr; //因為pEmfRecord所指的對象為CONST類型,不可修改。 //因記錄為CONST不可修改,為了修改該記錄,需復制一個記錄出來,並保存指針到pEmfr中 pEmfr = malloc(pEmfRecord->nSize); CopyMemory(pEmfr, pEmfRecord, pEmfRecord->nSize); //將原來的矩形改為橢圓 if (pEmfr->iType == EMR_RECTANGLE) pEmfr->iType = EMR_ELLIPSE; if (pEmfr->iType != EMR_LINETO) //去掉“X”中的兩條線,只畫出橢圓,並填充 { PlayEnhMetaFileRecord(hdc, pHandleTable, pEmfr, iHandles); //回放顯示某條記錄 } free(pEmfr); return TRUE; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HENHMETAFILE hEmf; HDC hdc, hdcEMF; PAINTSTRUCT ps; RECT rect; LOGBRUSH lb; switch (message) { case WM_CREATE: hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf6.emf"), NULL, TEXT("EMF6\0EMF Demo #6\0")); if (!hdcEMF) return 0; //創建並選入畫刷 SelectObject(hdcEMF, CreateSolidBrush(RGB(0, 0, 255))); lb.lbStyle = BS_SOLID; lb.lbColor = RGB(255, 0, 0); lb.lbHatch = 0; //創建並選入畫筆 SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, 5, &lb, 0, NULL)); #if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, 100, 100, 201, 201); #else Rectangle(hdcEMF, 101, 101, 202, 202); #endif Rectangle(hdcEMF, 100, 100, 200, 200); MoveToEx(hdcEMF, 100, 100, NULL); //左上——右下 LineTo(hdcEMF, 200, 200); MoveToEx(hdcEMF, 200, 100, NULL); //右上——左下 LineTo(hdcEMF, 100, 200); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN))); DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回圖元文件句柄,這里不用靜態. DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf6.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { //與EMF3程序不同,這里使用枚舉函數,而不使用PlayEnhMetaFile EnumEnhMetaFile(hdc, hEmf, EnhMetaFileProc, NULL, &rect); DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
18.2.6 嵌入圖像的方法
(1)將源圖元文件的設備環境句柄當作第1個參數傳遞給函數EnumEnhMetaFile。
(2)在枚舉回調函數里,就可以在這個設備環境里進行繪圖。(也可以用PlayEnhMetaFile函數,將整個圖元文件插件到現有的圖元文件( PlayEnhMetaFile(hdcEMF,hemfOld,&rect))
(3)當使用自定義的畫筆或畫刷繪制某個圖形后,要進行恢復原來的畫筆、畫刷。
【Emf7程序】

/*------------------------------------------------------------ EMF7.C -- Enhanced Metafile Demo #7 (c) Charles Petzold, 1998 ------------------------------------------------------------*/ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("EMF7"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("Enhanced Metafile Demo #7"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //自定義的枚舉圖元文件的回調函數。 int CALLBACK EnhMetaFileProc(HDC hdc, HANDLETABLE* pHandleTable, CONST ENHMETARECORD* pEmfRecord, int iHandles, LPARAM pData) { HBRUSH hBrush; HPEN hPen; LOGBRUSH lb; //回放舊文件中除頭記錄和文件結束記錄外的所有記錄。 if (pEmfRecord->iType != EMR_HEADER && pEmfRecord->iType != EMR_EOF) PlayEnhMetaFileRecord(hdc, pHandleTable, pEmfRecord, iHandles); if (pEmfRecord->iType == EMR_RECTANGLE) { hBrush = SelectObject(hdc, GetStockObject(NULL_BRUSH)); //透明畫刷 //創建新畫筆,並保留舊畫筆到hPen中。 lb.lbStyle = BS_SOLID; lb.lbColor = RGB(0, 255, 0); //綠色畫筆 lb.lbHatch = 0; hPen = SelectObject(hdc, ExtCreatePen(PS_SOLID|PS_GEOMETRIC,5,&lb,0,NULL)); Ellipse(hdc, 100, 100, 200, 200); DeleteObject(SelectObject(hdc, hPen)); //選入舊畫筆,並刪除自己創建的畫筆 SelectObject(hdc, hBrush); //備用畫刷不用刪除 } return TRUE; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { ENHMETAHEADER emh; HENHMETAFILE hEmf,hEmfOld; HDC hdc, hdcEMF; PAINTSTRUCT ps; RECT rect; switch (message) { case WM_CREATE: //獲取emf3圖元文件和頭記錄 hEmfOld = GetEnhMetaFile(TEXT("emf3.emf")); GetEnhMetaFileHeader(hEmfOld, sizeof(ENHMETAHEADER), &emh); //唯一目標是要獲取rclBounds字段 //創建新的圖元文件DC hdcEMF = CreateEnhMetaFile(NULL, TEXT("emf7.emf"), NULL, TEXT("EMF7\0EMF Demo #7\0")); if (!hdcEMF) return 0; //將舊的emf3圖元文件繪制新的hdcEMF中。(即回放) EnumEnhMetaFile(hdcEMF, hEmfOld, EnhMetaFileProc, NULL,(RECT*)&emh.rclBounds); //返回圖元文件句柄,這里不用靜態. hEmf = CloseEnhMetaFile(hdcEMF); DeleteEnhMetaFile(hEmfOld); DeleteEnhMetaFile(hEmf); //直接刪除,在WM_PAINT中,要從磁盤中讀取該文件,與EMF1程序不同 return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); rect.left = rect.right / 4; rect.right = 3 * rect.right / 4; rect.top = rect.bottom / 4; rect.bottom = 3 * rect.bottom / 4; if (hEmf = GetEnhMetaFile(TEXT("emf7.emf"))) //這里從磁盤中加載進來,與EMF1程序不同 { PlayEnhMetaFile(hdc, hEmf, &rect); DeleteEnhMetaFile(hEmf); //顯示完就刪除該圖元文件 } EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
