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); }