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. 怎么把多个GridView和Repeater导入到word或者excel中

    最近开发网上商城有发现我们会涉及到订单打印的问题,本来想到直接显示打印即可但是考虑还得下载到本地,最后只有直接选择控件按流方式输出. 导入到word中代码如下: protected void Butt ...

  2. 强大的<canvas>

    <canvas> 个人认为<canvas>是h5最重量级的新标签了,现在各种h5小游戏都是基于<canvas>的,它为游戏提供了一个功能强大的画布,可在画布上绘制丰 ...

  3. Hadoop 2.5.1编译

    1.环境安装 gcc.gcc-c++.make.cmake.svn yum install lzo-devel zlib-devel gcc gcc-c++ make cmake autoconf a ...

  4. SQLMAP使用笔记

    -u #注入点-f #指纹判别数据库类型-b #获取数据库版本信息-p #指定可测试的参数(?page=1&id=2 -p “page,id”)-D “” #指定数据库名-T “” #指定表名 ...

  5. tomcat内存溢出处理

    tomcat内存溢出设置JAVA_OPTS  答案1设置Tomcat启动的初始内存 其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4.可以利用JVM提供的-Xmn ...

  6. 浅谈JSON数据解析方法

    JSON数据解析 JSON是什么?? 如何把JSON数据解析出来 如何把一个字典转换为JSON JSON详细介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交 ...

  7. Listener监听器与Filter过滤器

    1.Listener     [1]监听器简介         > Listener是JavaWeb的三大组件之一,Servlet.Filter.Listener         > Li ...

  8. 一个简单的Java web服务器实现

    前言 一个简单的Java web服务器实现,比较简单,基于java.net.Socket和java.net.ServerSocket实现: 程序执行步骤 创建一个ServerSocket对象: 调用S ...

  9. selenium+python测试

    pip install selenium, 得有图形界面, 这里简单的先演示一个打开浏览器,输入网址的demo,以百度为例 # encoding = utf-8 from selenium impor ...

  10. Git中文版教程

    1. 起步 1.1 关于版本控制 1.2 Git 简史 1.3 Git 基础 1.4 命令行 1.5 安装 Git 1.6 初次运行 Git 前的配置 1.7 获取帮助 1.8 总结 2. Git 基 ...