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. JavaScript学习笔记-数组

    数组 数组中的每个元素的位置是索引,索引是基于32位的由0开始的数值,最大索引为(2的32次方-2),最大长度为(2的32次方-3) 数组是无类型的:元素可为任意类型:动态的:可根据需要自动增长.缩减 ...

  2. Web前端开发工具总结

    前端开发工具: web前端开发乃及其它的相关开发, 推荐sublime text, webstorm(jetbrains公司系列产品)这两个的原因在于,有个技术叫emmet, http://docs. ...

  3. 一款实用的viewer.js 图片相册

    Viewer.js 是一款强大的图片相册插件,像SNS交友网站一般都会用到点击缩略图,弹出层大图片,而且弹出层有多个控制按钮,比如放大缩小.旋转.撤回等,底部有缩略图列表可切换. 支持移动设备触摸事件 ...

  4. 如何在silverlight中以同步方式 获取sharepoint2013站点的当前登录账号

    最近有个项目用到了silverlight要同步方式获取当前登录账号.异步的方式无法跟其他应用结合.主要先后顺序问题.但是silverlight非常不好获取到当前登录账号.即使获取到了也是异步方式获取. ...

  5. SharePoint 2013 和卷影复制服务(VSS)概述

    对备份供应商而言,卷影复制服务 (VSS) 使用集中式 API 简化了 Microsoft 服务器解决方案的备份.Microsoft SharePoint Foundation 包括一个参考 VSS ...

  6. crm2013关于contentIFrame不能使用

    在CRM2011里面,我们可以在页面的控制台里面输入: contentIFrame.Xrm.Page.data.entity.getEntityName(); contentIFrame.Xrm.Pa ...

  7. Day Tips:分布式缓存的删除和重建

    遇到cacheHostInfo is null 错误时,必须将这台服务器上的实例删除重新创建 $instanceName ="SPDistributedCacheService Name=A ...

  8. 你真的了解NSNotificationCenter吗?

    一:首先查看一下关于NSNotificationCenter的定义 @interface NSNotificationCenter : NSObject { @package void * __str ...

  9. 你真的了解UIApplication吗?

    一:首先查看一下关于UIApplication的定义 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIApplication : UIResponder //获得单例 ...

  10. NSJSONSerialization

    /*     总结:   json格式的读写: 解析: data =   NSData  dataWithContentsOfUrl:XXX id obj  =  [ NSJsonSerializat ...