第18章 图元文件_18.1 老式图元文件格式(wmf)

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剪贴板会自动进行老格式和增强型格式之间的转换。是否转换,要看用户从剪贴板请求的文件格式。

时间: 2024-10-26 09:42:26

第18章 图元文件_18.1 老式图元文件格式(wmf)的相关文章

第18章 图元文件_18.2 增强型图元文件(emf)(1)

18.2 增强型图元文件(emf) 18.2.1 创建并显示增强型图元文件的步骤 (1)创建:hdcEMF = CreateEnhMetaFile(hdcRef,szFilename,lpRect,lpDescription); 参数 含义 hdcRef 参考设备环境,NULL时表示以屏幕为参考 szFileName 指定文件名时,创建磁盘文件(.EMF).为NULL时创建内存图元文件 lpRect 用于描述图元文件的大小和位置(以0.01mm为单位),可用它精确定义图元文件的物理尺寸 lpDe

第18章 图元文件_18.2 增强型图元文件(emf)(2)

18.2.7 增强型图元文件的查看和打印程序 (1)传递EMF到剪贴板,剪贴板类型应为:CF_ENHMETAFILE (2)CopyEnhMetaFile用于复制图元文件 (3)剪贴板中的图元文件会自动在老式与增强型图元文件间转换. (4)自定义函数CreatePaletteFromMetaFile用于从图元文件中创建逻辑调色板. [EmfView程序]      /*------------------------------------------------------------ EMF

《TCP/IP详解卷1:协议》第17、18章 TCP:传输控制协议(2)-读书笔记

章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP:网际协议(1)-读书笔记 <TCP/IP详解卷1:协议>第3章 IP:网际协议(2)-读书笔记 <TCP/IP详解卷1:协议>第4章 ARP:地址解析协议-读书笔记 <TCP/IP详解卷1:协议>第5章 RARP:逆地址解析协议-读书笔记 <TCP/IP详解卷1:协

设计模式之第18章-观察者模式(Java实现)

设计模式之第18章-观察者模式(Java实现) 话说曾小贤,也就是陈赫这些天有些火,那么这些明星最怕的,同样最喜欢的是什么呢?没错,就是狗仔队.英文的名字比较有意思,是paparazzo,这一说法据说来自意大利电影<滴露牡丹开>中一个专门偷拍明星照片的一个摄影师的名字,“Paparazzo”,中文译为帕帕拉齐,俗语就是狗仔队.这些明星因狗仔队而荣,获得曝光率,也因狗仔队而损,被曝光负面新闻,不管怎么说,总之是“火起来了”,让明星们又爱又恨.(众人:鱼哥,你扯远了).咳咳,这个狗仔队其实嘛,也就

《Cracking the Coding Interview》——第18章:难题——题目10

2014-04-29 04:22 题目:给定一堆长度都相等的单词,和起点.终点两个单词,请从这堆单词中寻找一条变换路径,把起点词变成终点词,要求每次变换只能改一个字母. 解法:Leetcode中有Word Ladder,这题基本思路一致. 代码: 1 // 18.10 Given a list of words, all of same length. Given a source and a destionation words, you have to check if there exis

《Cracking the Coding Interview》——第18章:难题——题目9

2014-04-29 04:18 题目:有一连串的数被读入,设计一个数据结构,能随时返回当前所有数的中位数. 解法:用一个大顶堆,一个小顶堆将数分成数量最接近的两份,就能轻松得到中位数了. 代码: 1 // 18.9 A stream of integers are passed to you, you have to tell me the median as they keep coming in. 2 #include <climits> 3 #include <iostream&

《Cracking the Coding Interview》——第18章:难题——题目13

2014-04-29 04:40 题目:给定一个字母组成的矩阵,和一个包含一堆单词的词典.请从矩阵中找出一个最大的子矩阵,使得从左到右每一行,从上到下每一列组成的单词都包含在词典中. 解法:O(n^3)级别的时间和空间进行动态规划.这道题目和第17章的最后一题很像,由于这题的时间复杂度实在是高,我动手写了字典树进行加速.如果单纯用哈希表来作为词典,查询效率实际会达到O(n)级别,导致最终的算法复杂度为O(n^4).用字典树则可以加速到O(n^3),因为对于一个字符串"abcd",只需要

《Cracking the Coding Interview》——第18章:难题——题目11

2014-04-29 04:30 题目:给定一个由'0'或者'1'构成的二维数组,找出一个四条边全部由'1'构成的正方形(矩形中间可以有'0'),使得矩形面积最大. 解法:用动态规划思想,记录二维数组每个元素向上下左右四个方向各有多少个连续的'1',然后用O(n^3)时间计算出满足条件的最大正方形.时间复杂度O(n^3),空间复杂度O(n^2). 代码: 1 // 18.11 Given an NxN matrix of 0s and 1s, find out a subsquare whose

《Cracking the Coding Interview》——第18章:难题——题目12

2014-04-29 04:36 题目:最大子数组和的二位扩展:最大子矩阵和. 解法:一个维度上进行枚举,复杂度O(n^2):另一个维度执行最大子数组和算法,复杂度O(n).总体时间复杂度为O(n^3),还需要O(n)额外空间. 代码: 1 // 18.12 Given an n x n matrix, find the submatrix with largest sum. Return the sum as the result. 2 #include <algorithm> 3 #inc