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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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, , , , ); MoveToEx(hdcEMF, , , NULL); //左上——右下
LineTo(hdcEMF, , ); MoveToEx(hdcEMF, , , NULL); //右上——左下
LineTo(hdcEMF, , ); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,并保存在静态变量中
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect区域内显示图元文件 EndPaint (hwnd, &ps) ;
return ; case WM_DESTROY:
DeleteEnhMetaFile(hEmf); //删除内存中的图元文件
PostQuitMessage () ;
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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 ; Rectangle(hdcEMF, , , , ); MoveToEx(hdcEMF, , , NULL); //左上——右下
LineTo(hdcEMF, , ); MoveToEx(hdcEMF, , , NULL); //右上——左下
LineTo(hdcEMF, , ); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,这里不用静态. DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; if (hEmf = GetEnhMetaFile(TEXT("emf2.emf"))) //这里从磁盘中加载进来,与EMF1程序不同
{
PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect区域内显示图元文件
DeleteEnhMetaFile(hEmf); //显示完就删除该图元文件
} EndPaint (hwnd, &ps) ;
return ; case WM_DESTROY:
PostQuitMessage () ;
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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 ; //创建并选入画刷
SelectObject(hdcEMF, CreateSolidBrush(RGB(, , ))); lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(, , );
lb.lbHatch = ; //创建并选入画笔
SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, , &lb, , NULL));
#if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, , , , );
#else Rectangle(hdcEMF, , , , );
#endif
Rectangle(hdcEMF, , , , ); MoveToEx(hdcEMF, , , NULL); //左上——右下
LineTo(hdcEMF, , ); MoveToEx(hdcEMF, , , NULL); //右上——左下
LineTo(hdcEMF, , ); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN)));
DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,这里不用静态. DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; if (hEmf = GetEnhMetaFile(TEXT("emf3.emf"))) //这里从磁盘中加载进来,与EMF1程序不同
{
PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect区域内显示图元文件
DeleteEnhMetaFile(hEmf); //显示完就删除该图元文件
} EndPaint (hwnd, &ps) ;
return ; case WM_DESTROY:
PostQuitMessage () ;
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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 ; hbm = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CLOSE)); GetObject(hbm, sizeof(BITMAP), &bm); hdcMem = CreateCompatibleDC(hdcEMF);
SelectObject(hdcMem, hbm); //只有这个GDI函数才会被写到图元文件中
StretchBlt(hdcEMF, , , , ,
hdcMem,,,bm.bmWidth,bm.bmHeight,SRCCOPY); DeleteDC(hdcMem);
DeleteObject(hbm); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,这里不用静态. DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; if (hEmf = GetEnhMetaFile(TEXT("emf4.emf"))) //这里从磁盘中加载进来,与EMF1程序不同
{
PlayEnhMetaFile(hdc, hEmf, &rect); //在指定的rect区域内显示图元文件
DeleteEnhMetaFile(hEmf); //显示完就删除该图元文件
} EndPaint (hwnd, &ps) ;
return ; case WM_DESTROY:
PostQuitMessage () ;
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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 ; //创建并选入画刷
SelectObject(hdcEMF, CreateSolidBrush(RGB(, , ))); lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(, , );
lb.lbHatch = ; //创建并选入画笔
SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, , &lb, , NULL));
#if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, , , , );
#else Rectangle(hdcEMF, , , , );
#endif
Rectangle(hdcEMF, , , , ); MoveToEx(hdcEMF, , , NULL); //左上——右下
LineTo(hdcEMF, , ); MoveToEx(hdcEMF, , , NULL); //右上——左下
LineTo(hdcEMF, , ); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN)));
DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,这里不用静态. DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; 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 ; case WM_DESTROY:
PostQuitMessage () ;
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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 ; //创建并选入画刷
SelectObject(hdcEMF, CreateSolidBrush(RGB(, , ))); lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(, , );
lb.lbHatch = ; //创建并选入画笔
SelectObject(hdcEMF, ExtCreatePen(PS_SOLID | PS_GEOMETRIC, , &lb, , NULL));
#if(WINVER < 0x0400) //win98 Rectangle(hdcEMF, , , , );
#else Rectangle(hdcEMF, , , , );
#endif
Rectangle(hdcEMF, , , , ); MoveToEx(hdcEMF, , , NULL); //左上——右下
LineTo(hdcEMF, , ); MoveToEx(hdcEMF, , , NULL); //右上——左下
LineTo(hdcEMF, , ); DeleteObject(SelectObject(hdcEMF, GetStockObject(BLACK_PEN)));
DeleteObject(SelectObject(hdcEMF, GetStockObject(WHITE_BRUSH))); hEmf = CloseEnhMetaFile(hdcEMF); //返回图元文件句柄,这里不用静态. DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; if (hEmf = GetEnhMetaFile(TEXT("emf6.emf"))) //这里从磁盘中加载进来,与EMF1程序不同
{
//与EMF3程序不同,这里使用枚举函数,而不使用PlayEnhMetaFile
EnumEnhMetaFile(hdc, hEmf, EnhMetaFileProc, NULL, &rect);
DeleteEnhMetaFile(hEmf); //显示完就删除该图元文件
} EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
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 = ;
wndclass.cbWndExtra = ;
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 ;
} 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, , ))
{
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(, , ); //绿色画笔
lb.lbHatch = ;
hPen = SelectObject(hdc,
ExtCreatePen(PS_SOLID|PS_GEOMETRIC,,&lb,,NULL)); Ellipse(hdc, , , , ); 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 ; //将旧的emf3图元文件绘制新的hdcEMF中。(即回放)
EnumEnhMetaFile(hdcEMF, hEmfOld, EnhMetaFileProc, NULL,(RECT*)&emh.rclBounds); //返回图元文件句柄,这里不用静态.
hEmf = CloseEnhMetaFile(hdcEMF); DeleteEnhMetaFile(hEmfOld);
DeleteEnhMetaFile(hEmf); //直接删除,在WM_PAINT中,要从磁盘中读取该文件,与EMF1程序不同
return ; case WM_PAINT:
hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); rect.left = rect.right / ;
rect.right = * rect.right / ;
rect.top = rect.bottom / ;
rect.bottom = * rect.bottom / ; if (hEmf = GetEnhMetaFile(TEXT("emf7.emf"))) //这里从磁盘中加载进来,与EMF1程序不同
{
PlayEnhMetaFile(hdc, hEmf, &rect);
DeleteEnhMetaFile(hEmf); //显示完就删除该图元文件
} EndPaint(hwnd, &ps);
return ; case WM_DESTROY:
PostQuitMessage();
return ;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

第18章 图元文件_18.2 增强型图元文件(emf)(1)的更多相关文章

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

    18.2.7 增强型图元文件的查看和打印程序 (1)传递EMF到剪贴板,剪贴板类型应为:CF_ENHMETAFILE (2)CopyEnhMetaFile用于复制图元文件 (3)剪贴板中的图元文件会自 ...

  2. 第18章 集合框架(2)-Set接口

    第18章 集合框架(2)-Set接口 Set是Collection子接口,模拟了数学上的集的概念 Set集合存储特点 1.不允许元素重复 2.不会记录元素的先后添加顺序 Set只包含从Collecti ...

  3. Java 第18章 多态

    18 章  --> 多态 继承: extends 抽象类 abstract (限制类的实例化) 抽象方法 public abstract void show(); //抽象方法只有方法的声明,没 ...

  4. LPTHW 笨方法学python 18章

    看完18章以后,发现第一个练习中,使用了*args读取全部的的输入参数作为一个元组,但是在他的练习中只给了两个变量去赋值,当用户不清楚这个函数的定义时,就可能会给出过多的变量进这个函数,那么就会出现如 ...

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

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

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

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  7. Linux就这个范儿 第18章 这里也是鼓乐笙箫 Linux读写内存数据的三种方式

    Linux就这个范儿 第18章  这里也是鼓乐笙箫  Linux读写内存数据的三种方式 P703 Linux读写内存数据的三种方式 1.read  ,write方式会在用户空间和内核空间不断拷贝数据, ...

  8. 【C#4.0图解教程】笔记(第9章~第18章)

    第9章 语句 1.标签语句 ①.标签语句由一个标识符后面跟着一个冒号再跟着一条语句组成 ②.标签语句的执行完全如同标签不存在一样,并仅执行冒号后的语句. ③.给语句添加一个标签允许控制从代码的另一部分 ...

  9. 第18章 备忘录模式(Memento Pattern)

    原文  第18章 备忘录模式(Memento Pattern) 备忘录模式       概述: 备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Toke ...

随机推荐

  1. Ajax基本概念和原理

    什么是Ajax Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术. Ajax的全称是Asynchronous JavaScript and XML,即异步JavaScript+X ...

  2. 全信号高清DVI编码器|上海视涛科技

    高清DVI编码器(E700)简介 高清DVI编码器是上海视涛科技出品的高性能全信号DVI编码产品.该DVI编码器是上海视涛科技完全自主研发,并适用于DVI信号的编码采集及网络传输的专用硬件设备.可兼容 ...

  3. SharePoint 2013 配置基于AD的Form认证

    前 言 配置SharePoint 2013基于AD的Form认证,主要有三步: 1. 修改管理中心的web.config: 2. 修改STS Application的web.config: 3. 修改 ...

  4. [outlook]打开以后就自动进入安全模式的解决方法。Outlook start in safe mode.

    给客户写了一个Outlook的add-in, 用现在时髦的话应该叫outlook的app. 这个add-in的作用就是把outlook中的email,直接上传到SharePoint中.想要代码的联系我 ...

  5. 关于iOS中的时间

    两类 绝对时间 [NSDate date].CFAbsoluteTimeGetCurrent(),或者gettimeofday(). 返回的是从某一个时刻开始,度过的秒数.会随着用户设置的系统时间更改 ...

  6. PHP学习之登录以及后台商品展示

    1.3用户登录 用户登录成功后跳转到商品显示页面 1.3.1设计界面 1.新建一个login.php页面,用来做用户的登录 2.登录业务原理 通过输入的用户名和密码查询对应的记录,表示登陆成功,否则登 ...

  7. 【转】Android NFC学习笔记

    一:NFC的tag分发系统 如果想让android设备感应到NFC标签,你要保证两点 1:屏幕没有锁住 2:NFC功能已经在设置中打开 当系统检测到一个NFC标签的时候,他会自动去寻找最合适的acti ...

  8. Android pull解析xml文件

    本文介绍android中使用pull来解析xml文件 先自己写一个xml文件,存一些天气信息 <?xml version="1.0" encoding="UTF-8 ...

  9. Android Studio 2.2新功能预览

    升级SDK可用Background 多加了个按钮,可用一边写代码一边下载SDK Instant Run 修改代码一秒启动 APK analyzer 分析任何的APK 查看APK下载包的大小,解压后的实 ...

  10. 1.9 基础知识——GP2.10 高级别的领导检查(Higher level management)

    GP2.10 Review the activities,status,and results of XXX process with highter level management and res ...