在正式开始学习绘图之前, 还应该先了解下在屏幕上绘图的相关流程, 首先, 获取设备环境的句柄, 当获取成功时就意味着你的应用程序有了在屏幕上绘图的权限, 然后你就可以调用GDI中的绘图函数通过设备环境句柄对屏幕进行绘制, 等绘制结束后你应该释放这个句柄。
- 获取设备环境句柄
Windows提供了许多种方法用来获取不同的设备环境句柄, 这里不能一次性讲全, 当前我们需要使用的主要有以下几种:
1>. 使用BeginPaint函数和EndPaint函数:
BeainPaint函数原型:
HDC BeginPaint(
HWND hwnd,
LPPAINTSTRUCT lpPaint
);
参数二为PAINTSTRUCT类型的结构, 函数的返回值就是设备环境句柄, PAINTSTRUCT结构定义在WINUSER.H头文件中, 如下:
typedef struct tagPAINTSTRUCT
{
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT ;
在该结构中的第一个成员HDC的值正是设备环境句柄, 函数返回的设备环境句柄也正是来源于此。 通过BeginPaint函数来获取设备环境句柄通常用于处理WM_PAINT消息时, 一般的使用结构:
hdc = BeginPaint( hwnd, &ps ) ;
[相关的处理语句]
EndPaint( hwnd, &ps ) ;
2>. 使用GetDC函数
GetDC函数可以用来获取制定窗口句柄的设备环境句柄, 同时也可以获取整个屏幕的设备环境句柄, 其函数原型为:
HDC GetDC( HWND hWnd ) ; //函数参数为窗口句柄 当函数调用成功时返回设备环境的句柄, 失败返回NULL, 当函数参数为NULL时返回的就是整个屏幕的设备环境句柄。
当设备环境句柄使用完成后应该及时使用ReleaseDC函数释放该设备环境句柄, 值得一提的是, GetDC和ReleaseDC函数不能使无效的客户区变成有效, 当使用GetDC方式重绘完成后可以显性调用ValidateRect函数使其有效, ValidateRect原型:
BOOL ValidateRect(
HWND hWnd,
CONST RECT *lpRect
) ;
参数一为被有效化的窗口句柄, 若该参数为NULL, 系统将更新所有的窗口; 参数二为一个包含需要生效的矩形的更新区域坐标的RECT结构体, 当参数为NULL时, 整个客户区都将有效化。
使用的一般形式:
hdc = GetDC( hwnd ) ;
[相关的处理语句]
ReleaseDC( hwnd, hdc ) ;
3>. 使用GetWindowDC
与GetDC不同, GetDC可以用来获取窗口的客户区部分的设备环境句柄, 而GetWindowDC是用来获取整个窗口的设备环境句柄, 整个窗口是指包括窗口的标题栏、菜单栏、滚动条、状态栏以及客户区和客户区的外缘边框部分, 函数原型:
HDC GetWindowDC( HWND hWnd
在使用完成后同样要使用ReleaseDC对设备环境句柄进行释放, 使用的一般形式:
hdc = GetWindowDC( hwnd ) ;
[相关的处理语句]
ReleaseDC( hwnd, hdc ) ;
4>. 使用CreateDC
CreateDC的作用是通过使用指定的名字为一个设备创建一个设备环境句柄, 在使用完成后应当由DeleteDC函数进行删除释放, 而不是ReleaseDC。 函数原型:
HDC CreateDC(
LPCTSTR lpszDriver,
LPCTSTR lpszDevice,
LPCTSTR lpszOutput,
const DEVMODE *lpInitData
);
参数一LPCTSTR lpszDriver指向一个以NULL结尾的字符串的指针, 当字符串为TEXT("DISPLAY")时,是获取整个屏幕的设备环境, 为TEXT("WINSPOOL")则是访问打印驱动的设备环境;
注意: 当参数为TEXT("WINSPOOL")时其他参数均为NULL。
参数二LPCTSTR lpszDevice指向一个以null结尾的字符串的指针, 该字符串指定了正在使用的特定输出设备的名字;
参数三LPCTSTR lpszOutput在32位环境下通常被忽略, 并置为NULL, 该参数的存在主要是为了提供与16位应用程序兼容;
参数四const DEVMODE *lpInitData指向包含设备驱动程序的设备指定初始化数据的DEVMODE结构的指针, DEVMODE数据结构中包含了有关设备初始化和打印机环境的信息 , 如果设备驱动程序使用用户指定的缺省初始化值。则lplnitData参数必须为NULL。
对于这个函数可能暂时有点难以理解, 不过暂时也用不到他, 现在只需要记住一条用法:
hdc = CreateDC( TEXT( "DISPLAY" ), NULL, NULL, NULL ) ; //获取当前整个屏幕的设备环境句柄
在使用完成后记住要使用DeeteDC进行释放, 而不是ReleaseDC。
- 获取设备环境信息
获取设备环境信息, 也成属性, 通常是指物理硬件的的某些信息, 比如显示器的分辨率、尺寸、色彩能力等, 要获取设备环境的信息首先要得到设备环境句柄, 然后通过GetDeviceCaps函数获取, GetDeviceCaps的原型:
int GetDeviceCaps(
HDC hdc,
int nIndex
);
参数nIndex用来指明需要获取的信息类型, 例如当参数为HORZRES时, 函数返回以像素为单位的设备环境的宽度, HORZRES是定义在WINGDI.H中的29个标识符之一, 这29个标识符如下:
#define DRIVERVERSION 0
#define TECHNOLOGY 2
#define HORZSIZE 4
#define VERTSIZE 6
#define HORZRES 8
#define VERTRES 10
#define BITSPIXEL 12
#define PLANES 14
#define NUMBRUSHES 16
#define NUMPENS 18
#define NUMMARKERS 20
#define NUMFONTS 22
#define NUMCOLORS 24
#define PDEVICESIZE 26
#define CURVECAPS 28
#define LINECAPS 30
#define POLYGONALCAPS 32
#define TEXTCAPS 34
#define CLIPCAPS 36
#define RASTERCAPS 38
#define ASPECTX 40
#define ASPECTY 42
#define ASPECTXY 44
#if(WINVER >= 0x0500)
#define SHADEBLENDCAPS 45
#endif
#define LOGPIXELSX 88
#define LOGPIXELSY 90
#define SIZEPALETTE 104
#define NUMRESERVED 106
#define COLORRES 108
关于这些标识符的详细介绍可查阅MSDN -> GetDeviceCaps函数对参数int nIndex的解释。
3. 绘图函数
绘图函数就是GDI中最重要的部分了, 其中有许多的绘图函数, 这里同样不能一次讲完, 也不打算讲完, 这些都没有什么意义, 只需要查阅下MSDN或Google下找到这些函数并尝试着使用它就行了, 这里仅描述几个最基本的绘图函数。
1>. SetPixel绘制像素点
这是一个十分不常用的函数, 如果你想拿这个来绘制一条直线, 那么效率就太低了, 这个函数为 SetPixel, 其函数的原型为:
COLORREF SetPixel(
HDC hdc,
int X,
int Y,
COLORREF crColor
);
RGB宏定义在WINGDI.H中, 定义如下:
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
RGB宏有三个参数, 将红?、绿(G)、蓝(B)组合成一个无符号型的长整形用来表示颜色, 当三个参数都为0时颜色为黑色, 都为255时颜色为白色。
使用示例:
SetPixel( hdc, 100, 100, RGB( 0, 0, 0 ) ) ;
2>. LineTo绘制直线
BOOL LineTo(
HDC hdc,
int nXEnd,
int nYEnd
);
该函数需要和MoveToEx函数配合使用才能制定设备环境上的任意两点间绘制一条直线, MoveToEx函数的作用就是规定直线的起点坐标, 其函数原型为:
BOOL MoveToEx(
HDC hdc,
int X,
int Y,
LPPOINT lpPoint
);
使用示例:
MoveToEx( hdc, 100, 100, NULL );
LineTo( hdc, 500, 100 );
3>. Polyline绘制折线
函数原型:
BOOL Polyline(
HDC hdc,
const POINT *lppt,
int cPoints
);
用法示例:
POINT apt[5] = { {100, 100}, {300, 200}, {100, 400}, {400, 300}, {500, 200} } ;
Polyline( hdc, apt, 5 ) ;
4>. PolyPolyline绘制多条折线
PolyPolyline实际上是对Polyline函数中使用的坐标组提供了分组功能, 分为几组就意味着绘制多少条折线, PolyPolyline函数的原型:
BOOL PolyPolyline(
HDC hdc,
const POINT *lppt,
const DWORD *lpdwPolyPoints,
DWORD cCount
);
用法示例:
POINT apt[8] = { { 100, 100}, {200, 200}, {300, 200}, {200, 300}, {400, 200}, {600, 300}, {300, 400}, {500, 200} } ;
DWORD lpPts[] = { 4, 4 };
PolyPolyline( hdc, apt, lpPts, 2 ) ;
5>. Rectangle绘制矩形
绘制一个矩形十分简单, 只需要提供矩形左上角坐标与右下角坐标即可, 函数原型如下:
BOOL Rectangle(
HDC hdc,
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect
);
例如我们绘制一个正方形:
Rectangle( hdc, 50, 50, 200, 200 ) ;
看下效果图: 可以看到, 确实是一个由四条直线围成的矩形, 但是需要提醒的是, 这个矩形是经过自动填充的, 填充的颜色就是默认的白色颜色, 在这个图中填充颜色为白色, 和背景颜色相同, 所以我们才不易觉察到这是一个经过填充的图形, 被填充意思上就是说你以前在该矩形里或者说经过该矩形的图形会被白色给覆盖掉, 这点需要注意。
6>. Ellipse绘制椭圆
函数原型:
BOOL Ellipse(
HDC hdc,
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect
);
绘制椭圆的参数和绘制矩形的参数是相同的, 那么他是如何绘制的呢? 这里用图示说明下:
示例中的矩形参数与椭圆的参数是相同的, 很明显的可以看出椭圆是根据矩形的大小自动填充的, 需要提示的是, 它填充的是整个矩形, 而不仅仅是自身的椭圆。
7>. RoundRect画圆角矩形
函数原型:
BOOL RoundRect(
HDC hdc,
int nLeftRect,
int nTopRect,
int nRightRect,
int nBottomRect,
int nWidth,
int nHeight
);
相对于画矩形函数这里又多出两个参数int nWidth和int nHeight, 实际上, 这两个参数是用来描述圆角的宽和高, 如图:
8>. Arc弧线、Chord拱形与Pie扇形
之所以将这三个函数放在一起讲是因为他们三个拥有相同的参数, 他们三个的函数原型如下:
BOOL Arc ( HDC hdc, int xLeft, int yTop, int xRight, int yBottom, int xStart, int yStart, int xEnd, int yEnd ) ;
BOOL Chord( HDC hdc, int xLeft, int yTop, int xRight, int yBottom, int xStart, int yStart, int xEnd, int yEnd ) ;
BOOL Pie ( HDC hdc, int xLeft, int yTop, int xRight, int yBottom, int xStart, int yStart, int xEnd, int yEnd ) ;
这些参数都表示什么意思呢? 用文字恐怕不太好描述, 图示如下:
在指定好外部矩形的坐标后剩下的就是指定绘制的起点坐标与终点了, 多尝试几次就能掌握其使用规律。
以上的这些就是GDI种较为基础的绘图函数了, 更多的绘图函数请自行查阅MSDN Library。
下面练习使用下这些函数:
窗口过程函数部分:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
static TCHAR szBuffer[128];
POINT apn[4] = { {150, 50}, {200, 200}, {150, 300}, {150, 500} };
POINT apt[8] = { { 200, 50}, {300, 200}, {250, 200}, {200, 300}, {250, 300}, {300, 350}, {250, 400}, {250, 500} };
DWORD lpPts[] = { 4, 4 };
PAINTSTRUCT ps;
size_t iStrLength;
static int cxChar, cyChar, cxClient, cyClient;
switch (message)
{
case WM_SIZE:
hdc = GetDC(hWnd);
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
StringCchPrintf(szBuffer, 128, TEXT("当前客户区的分辨率:%d * %d px"), cxClient, cyClient);
StringCchLength(szBuffer, 128, &iStrLength);
SetTextAlign(hdc, TA_CENTER | TA_TOP);
TextOut(hdc, cxClient / 2, 0, szBuffer, iStrLength);
ReleaseDC(hWnd, hdc);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
int i;
for (i = 0; i < 90; i++)
SetPixel(hdc, 50, 50 + i * 5, RGB(0, 0, 0));
MoveToEx(hdc, 100, 50, NULL);
LineTo(hdc, 100, 500);
Polyline(hdc, apn, 4);
PolyPolyline(hdc, apt, lpPts, 2);
Arc(hdc, 350, 50, 500, 500, 400, 100, 400, 500);
Rectangle(hdc, 450, 50, 500, 500);
Ellipse(hdc, 550, 50, 600, 500);
Ellipse(hdc, 800, 200, 1000, 400);
RoundRect(hdc, 650, 50, 700, 500, 20, 20);
Pie(hdc, 750, 50, 850, 150, 850, 80, 850, 160);
Chord(hdc, 750, 400, 850, 500, 850, 450, 750, 450);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
|