第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)個,注意含\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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM