18.1 老式图元文件格式(wmf)
(1)创建图元文件:HDC hdcMeta = CreateMetaFile(lpszFile);
①如果lpszFile为NULL则图元文件存储在内存中,如果指定文件名(XXX.WMF)则存储为磁盘文件。
②返回值为图元文件的设备环境句柄,可以使用内存DC一样,用GDI绘图函数在其上绘图。
(2)关闭图元文件:HMETAFILE hmf= CloseMetaFile(hdcMeta);
①这里不像一般的DC,创建DC后要删除DC,而是关闭。
②关闭图元文件的设备环境会返回该图元文件的句柄。
(3)显示(绘制)图元文件:PlayMetaFile
①绘制图元文件中的各对象,即在CreateMetaFile和CloseMetaFile之间所有的GDI调用。
②图元文件中的函数坐标,其具体含义将由目标设备环境的当前坐标转换设置来确定。
(4)删除图元文件句柄:DeleteMetaFile(hmf);//
★注意只删除图元文件句柄,并没删除磁盘上的文件。要删除文件可调用DeleteFile。
【MetaFile程序】
/*------------------------------------------------------------ METAFILE.C -- Metafile Demonstration Program (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 ("Metafile") ; 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 ("Metafile Demonstration"), // 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) { PAINTSTRUCT ps ; static HMETAFILE hmf; static int cxClient, cyClient; HDC hdc,hdcMeta; HBRUSH hBrush; int x, y; switch (message) { case WM_CREATE: //创建图元文件设备环境(内存中) hdcMeta = CreateMetaFile(NULL); //创建蓝色画刷 hBrush = CreateSolidBrush(RGB(0, 0, 255)); Rectangle(hdcMeta, 0, 0, 100, 100); MoveToEx(hdcMeta, 0, 0, NULL); //左上 LineTo(hdcMeta, 100, 100); //右下 MoveToEx(hdcMeta, 0, 100, NULL);//左下 LineTo(hdcMeta, 100, 0); //右上 SelectObject(hdcMeta, hBrush); Ellipse(hdcMeta, 20, 20, 80, 80); //关闭图元文件设备环境,并返回图元文件句柄 hmf = CloseMetaFile(hdcMeta); DeleteObject(hBrush); return 0 ; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; //设置图元文件里面对象的坐标单位。 SetMapMode(hdc, MM_ANISOTROPIC); SetWindowExtEx(hdc, 1000, 1000, NULL); SetViewportExtEx(hdc, cxClient, cyClient, NULL); //重复画图元文件100次,按10*10排列 for (y = 0; y < 10; y++) for (x = 0; x < 10; x++) { SetWindowOrgEx(hdc, -100 * x, -100 * y, NULL); PlayMetaFile(hdc, hmf); } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: DeleteMetaFile(hmf); PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } void PrepareMetaFile(HDC hdc, LPMETAFILEPICT pmfp, int cxClient,int cyClient) { int xScale, yScale, iScale; SetMapMode(hdc, pmfp->mm); //根据mm字段设置映射模式 //处理MM_ISOTROPIC和MM_ANISOTROPIC情况 if (pmfp->mm == MM_ISOTROPIC || pmfp->mm == MM_ANISOTROPIC) { if (pmfp->xExt ==0 ) //xExt\yExt表示不指定图像的大小时 { //在图元文件内部己经会自己调用SetWindowExtEx函数,这里无须设置。 SetViewportExtEx(hdc, cxClient, cyClient, NULL); } else if (pmfp->xExt >0) //xExt\yExt表示图像的大小,单位是0.01mm { SetViewportExtEx(hdc, pmfp->xExt* GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) / 100, //将逻辑单位转为像素大小 pmfp->yExt* GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) / 100, //将yExt*0.01mm转为像素大小 NULL); } else if (pmfp->xExt < 0) //xExt、yExt表示图像的比值表示纵横比。 { //将图像显示区域除以图像的大小,获得缩放比 //首先cxClient的单位为像素,乘以HORZSIZE/HORZRES,转换为像素规模所对应的毫米总数,再乘以100 //转为单位为0.01毫米,最后再除以图像的大小,得到缩放比 xScale = 100 * cxClient * GetDeviceCaps(hdc, HORZSIZE) / //单位都化为0.01毫米进行比较 GetDeviceCaps(hdc, HORZRES) / -pmfp->xExt; yScale = 100 * cyClient * GetDeviceCaps(hdc, VERTSIZE) / //单位都化为0.01毫米进行比较 GetDeviceCaps(hdc, VERTRES) / -pmfp->yExt; iScale = min(xScale, yScale); //设置视口范围,将图像大小映射到整个显示区域 SetViewportExtEx(hdc, -pmfp->xExt *iScale* GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) /100, -pmfp->yExt *iScale* GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) /100, NULL); } } //如果不是MM_ISOTROPIC或MM_ANISOTROPIC模式,则xExt和yExt的值表示图像的宽度和 //高度,单位由mm字段的设置决定。 }
18.1.1 把图元文件存储到磁盘
(1)获取磁盘中的图元文件:hmf = GetMetaFile(szFileName);
(2)在WM_PAINT中处理完后,可以直接DeleteMetaFile(hmf);
(3)当把图元文件定义为资源时,可以将其作为一个数据块加载。如果己经有图元文件内容的数据块,可以使用如下语句来创建图元文件。
hmf = SetMetaFileBitsEx(iSize,pData);//根据pData的内容来创建图元文件。
GetMetaFileBitsEx(hmf,iSize,pData);//将图元文件拷到pData内存块中。iSize为图元文件的大小。
18.1.2 老式的图元文件和剪贴板
(1)老式图元文件的缺点:
①老式图元文件中的坐标是逻辑坐标。显示时,图像大小很难确定。
②如果在图元文件内部设置了MM_ISOTROPIC或MM_ANISOTROPIC映射模式时,那么程序在PlayMetaFile期间用户很难干预,也就不能设置映射模式了。只能在PlayMetaFile之前或之后设置映射模式了。但图元文件内部会调用SetWindowExtEx等函数,所以,用户设置的无法真正工作。
(2)解决方案——传入剪贴板的不是图元文件句柄,而是METAFILEPIC结构体。
字段 |
含义 |
LONG mm |
映射模式 |
LONG xExt |
图元文件的宽度和高度 1. 在除MM_ISOTROPIC和MM_ANISOTROPIC映射模式下,其单位为mm字段中设置映射模式的单位。 2. 在MM_ISOTROPIC和MM_ANISOTROPIC中,单位:0.01mm。 ①xExt=0时,表示不指定图像的尺寸或纵横比 ②xExt>0时,表示图像的宽度和高度。 ③xExt<0时,其值单位仍然是0.01mm,但这种情况下,更有意义的是xExt/yExt的比值,即纵横比。 |
LONG yExt |
|
LONG hMF |
图元文件句柄 |
注意:当得到图元文件设备环境后,要在其上绘图前,一般还用调用SetWindowExtEx来指定绘图逻辑单位。但一般不包含SetMapMode、SetViewportExtEx或SetViewportOrgEx的调用。因为这些是基于显示平面的设备单位。即图元文件本身,内部一般要调用SetWindowExtEx函数,但不包含SetViewportExtEx等函数。
(3)创建图元文件并复制到剪贴板
//创建图元文件设备环境 hdcMeta = CreateMetaFile(NULL); //基于内存的 SetWindowExtEx(hdcMeta,…); //在图元文件内部设置映射模式,但不包含 SetWindowOrgEx(hdcMeta,…); // SetMapMode或SetViewportExtEx函数。 //获取该图元文件的句柄 hmf = CloseMetaFile(hdcMeta); //复制到剪贴板 GLOBALHANDLE hGlobal; LPMETAFILEPICT pMFP; //指向METAFILEPICT结构的指针 hGlobal = GlobalAlloc(GHND|GMEM_SHARE,sizeof(METAFILEPICT)); pMFP = (LPMETAFILEPICT)GlobalLock(hGlobal); //设置结构体设置 pMFP->mm = MM_...; //使用的映射模式; pMFP->xExt = …; //设置图像的宽度(逻辑单位) pMFP->yExt =…; pMPF->hMF = hmf; GlobalUnlock(hGlobal); //传送到剪贴板 OpenClipboard(hwnd); EmptyClipboard(); SetClipboardData(CF_METAFILEPICT,hGlobal);//这里传入的是METAFILEPIC结构,而不是hmf; CloseClipboard(); //至此,hGlobal和hmf句柄失效,不能再使用它们了。
(4)从剪贴板中获取图元文件,并显示。
//获取METAFILEPICT结构体 OpenClipboard(hwnd); hGlobal = GetClipboardData(CF_METAFILEPICT); pMFP->(LPMETAFILEPICT)GlobalLock(hGlobal); SaveDC(hdc); //根据mm、xExt、yExt设置映射模式 PrepareMetaFile(hdc,pMFP,cxClient,cyClient); //其中cxClient,cyClient为图元文件要显示的像素宽度和高度。该函数是自定义的,见后面的代码实现。 PlayMetaFile(pMFP->hMF); RestoreDC(hdc,-1); GlobalUnlock(hGlobal); CloseClipboard(); (4)PrepareMetaFile函数的分析 void PrepareMetaFile(HDC hdc, LPMETAFILEPICT pmfp, int cxClient,int cyClient) { int xScale, yScale, iScale; SetMapMode(hdc, pmfp->mm); //根据mm字段设置映射模式 //处理MM_ISOTROPIC和MM_ANISOTROPIC情况 if (pmfp->mm == MM_ISOTROPIC || pmfp->mm == MM_ANISOTROPIC) { if (pmfp->xExt ==0 ) //xExt\yExt表示不指定图像的大小时 { //在图元文件内部己经会自己调用SetWindowExtEx函数,这里无须设置。 SetViewportExtEx(hdc, cxClient, cyClient, NULL); } else if (pmfp->xExt >0) //xExt\yExt表示图像的大小,单位是0.01mm { SetViewportExtEx(hdc, pmfp->xExt* GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) / 100, //将逻辑单位转为像素大小 pmfp->yExt* GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) / 100, //将yExt*0.01mm转为像素大小 NULL); } else if (pmfp->xExt < 0) //xExt、yExt表示图像的比值表示纵横比。 { //将图像显示区域除以图像的大小,获得缩放比 //首先cxClient的单位为像素,乘以HORZSIZE/HORZRES,转换为像素规模所对应的毫米总数,再乘以100 //转为单位为0.01毫米,最后再除以图像的大小,得到缩放比 xScale = 100 * cxClient * GetDeviceCaps(hdc, HORZSIZE) / //单位都化为0.01毫米进行比较 GetDeviceCaps(hdc, HORZRES) / -pmfp->xExt; yScale = 100 * cyClient * GetDeviceCaps(hdc, VERTSIZE) / //单位都化为0.01毫米进行比较 GetDeviceCaps(hdc, VERTRES) / -pmfp->yExt; iScale = min(xScale, yScale); //设置视口范围,将图像大小映射到整个显示区域 SetViewportExtEx(hdc, -pmfp->xExt *iScale* GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) /100, -pmfp->yExt *iScale* GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) /100, NULL); } } //如果不是MM_ISOTROPIC或MM_ANISOTROPIC模式,则xExt和yExt的值表示图像的宽度和 //高度,单位由mm字段的设置决定。 }
(5)Windows剪贴板会自动进行老格式和增强型格式之间的转换。是否转换,要看用户从剪贴板请求的文件格式。