• 第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为单位),可用它精确定义图元文件的物理尺寸

    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)个,注意含。

    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("EMF2EMF Demo #2"));
              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("EMF3EMF Demo #3"));
              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("EMF4EMF Demo #4"));
              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("EMF5EMF Demo #5"));
              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("EMF6EMF Demo #6"));
            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("EMF7EMF Demo #7"));
            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);
    }
  • 相关阅读:
    引用 Geoprocessing调用ArcToolBox工具使用总结
    MySQL权限改观何时生效
    Ubuntu下安装*.bin挨次
    创立初始的MySQL权限
    MySQL的日期和光阴范例
    MySQL存取节制, 阶段1:连接证明
    让Linux操作零碎可以Mount NTFS分区
    Citrix进级XenDesktop桌面虚拟化产物
    如何在两台MySQL数据库间完成同步
    MySQL的数字
  • 原文地址:https://www.cnblogs.com/5iedu/p/4706327.html
Copyright © 2020-2023  润新知