这一篇就来了解一下windows的绘图API。
在windows应用程序中如果想要画图,就必须调用一定的接口,而这个接口需要有一定的库来支持。
这里,我们就用比较简单的GDI32.dll这个库提供的接口来进行画图。这个库默认在系统中有,并不需要包含进来。
先看下这一篇能够实现的效果:
很简洁。它的代码如下:
//窗口过程处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
//1.获得设备环境
//2.调用GDI接口
//3.释放设备环境
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
//矩形
Rectangle(hdc, 0, 0, 100, 100);
//扇形
Pie(hdc, 100, 0, 200, 100, 100, 0, 150, 50);
//椭圆 不过这里因为长宽相同,所以变成了圆,其实圆算是椭圆中一种特殊的圆了
Ellipse(hdc, 200, 0, 300, 100);
//圆角矩形
RoundRect(hdc, 300, 0, 400, 100, 10, 10);
//三角形
MoveToEx(hdc, 400, 100, NULL);
LineTo(hdc, 500, 100);
LineTo(hdc, 450, 0);
LineTo(hdc, 400, 100);
MoveToEx(hdc,0, 0, NULL);
//三角形的另一种画法
POINT apt[4] = { 300, 300, 400, 300, 350, 200 ,300,300};
MoveToEx(hdc,300, 300,NULL);
PolylineTo(hdc, apt, 4);
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
//主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
//设计窗口类
WNDCLASS wndclass = {
CS_HREDRAW | CS_VREDRAW,
WndProc,
0,
0,
hInstance,
LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW),
(HBRUSH)GetStockObject(WHITE_BRUSH),
NULL,
TEXT("MYDEMO")
};
//注册窗口类
RegisterClass(&wndclass);
//创建窗口类
HWND hwnd = CreateWindow(
TEXT("MYDEMO"),
TEXT("MyDemo"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
//显示窗口
ShowWindow(hwnd, nCmdShow);
//更新窗口
UpdateWindow(hwnd);
//消息循环
MSG Msg;
BOOL bRet;
while ((bRet = GetMessage(&Msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
return Msg.wParam;
}
可以复制粘贴看看运行效果是否和我一样。
之所以能够实现这些,是因为我们直接调用了windows的GDI接口。GDI是什么呢?
GDI,图形设备接口(Graphics Device Interface)。
嗯,既然知道用这个接口了,就应该开始画了吧?其实不急,因为要通过我们的接口来实现硬件的配合。这当中需要执行一定的顺序,我们来看一张图:
由图中我们可以看出,应用程序调用的接口之后需要与windows设备驱动程序打交道,然后再由windows设备驱动程序在与不同硬件实现出来。这当中,我们注意到了右边有个设备描述表,没错,其实,如果我们要在不同硬件中实现我们的效果,需要定义出一个设备描述表。然后再当中进行接口的调用。关于这个设备描述表具体不多说,我们只需要知道,如果你要绘图,就需要创建出一个使用设备的环境出来,只有这个环境搭建好了,我们才可以进行绘画,而这个设备描述表就类似于这个环境。
好了,实现绘图,最简单的绘图步骤有三步:
1.设备环境的准备;
2.调用GDI接口;
3.释放设备环境。
在前面的篇章中,我们说到了windows的消息机制。这里便关联到了。当窗口被刷新的时候,其实系统会发送一个WM_PAINT消息给窗口处理函数。而这个消息就是窗口重新绘制的消息。
从刚刚展示的图中很明显的看出这里有五种图形。其实,还可以画其它的一些,不过,这里就简单的画一些比较简单的图形。
现在直接对上面的绘图代码进行简单的解析吧。
主函数就不用看了,主要是看窗口处理函数中WM_PAINT里面的。
首先,我们是设备环境的准备,即定义一个HDC句柄,然后再这个句柄中调用BeginPaint()函数。这个函数有两个参数,第一个是窗口句柄,就是你要在那个窗口获得设备描述表的句柄,这里当然就是我们过程处理函数的窗口句柄了,第二个参数是一个绘画结构体的指针,什么是绘画结构?其实就是一个结构体PAINTSTRUCT,把它的结构贴出来给你们看下:
typedef struct tagPAINTSTRUCT {
HDC hdc;//设备环境句柄
BOOL fErase;//是否擦除
RECT rcPaint;//无效区域
BOOL fRestore;//系统保留
BOOL fIncUpdate;//系统保留
BYTE rgbReserved[32];//系统保留
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
通过这么一提,我们知道,我们之前创建的空白窗口其区域均为有效区域,那么我们在绘图的时候就需要营造出一块无效区域出来,而这个这个BeginPaint()函数就是根据窗口来传入一个PAINTSTRUCT结构来营建这么一个无效的区域。
所以,在我们绘图之前,我们需要再定义出一个PAINTSTRUCT结构。然后传入到BeginPaint()中使用即可。这样,设备环境便创建完成。
现在就可以调用GDI接口来进行绘画了,既然是绘画,当然要有画笔画刷什么的,其实这在我们创建了一个hdc的时候已经给我们提供一个默认值了。比如笔的颜色默认为黑色,画刷默认为白色等等,这里我们暂时不必深究,只管使用就行了。
GDI接口我们可以看到如果是矩形就调用:Rectangle(),具体的接口就不多说了,注释也有。主要需要说明的是它的参数。
对于矩形,我们先来看一张图:
这当中的四个参数对应图中的:Rectangle(hdc,xLeft,yTop,xRight,yBottom);
如果是点(xLeft,yTop),那么就是左上角的那个点,如果是点(xRight,yBottom),就是指右下角的那个点。
这样就应该明白了矩形的创建过程了,指定矩形的左上点和右下点,就可创建一个矩形。
好了,既然明白了矩形的创建,那么,椭圆也如此,就不多说了。
至于圆角矩形,还有两个参数,我们再来看一张图:
后面的那两个参数就是xCornerEllipse和yCornerEllipse。
如果这两个值越大,代表圆角越圆,如果当这两个值达到了矩形的宽度和高度,那么便会变成椭圆。
然后,来说下那个扇形,在说之前,需要说下的就是关于窗口的坐标问题,就二维平面来说:原点(0,0)在窗口的左上点,x轴由左到右自增,y轴由上至下自增。
好,现在再来看下这张图:
扇形的参数中,在传入了设备环境句柄之后,后面的四个参数就是和矩形一样。关键的是最后的四个参数,这里我们可以明显的看出它分为两个点:(xStart,yStart),(xEnd,yEnd),然后从圆心连接到这两个点形成两条线,其中,开始的那条线通过圆心逆时钟旋转到结束那条线为所需要的扇形区域。
最后,来说说这个三角形的创建吧。
其实,三角形之所以能够形成,是通过画线来形成的,我们调用了MoveToEx来把开始的点移动到作为画线的起点,然后再通过LIneTo来指定到线的终点。最后,这个点便成了下一次画线的七点。所以,如果要画三角形,则只需调用三次lineTo即可画出。
那么,如果我们觉得这样麻烦,那么,我们就用一个点的数组来保存这些点,然后通过PolylineTo()来把它们一个个的连接起来。其中在传入设备环境句柄之后,第二个参数便是这个数组了,然后指定画线的条数即可。
最后,在调用完接口后记得要释放设备环境,不然,绘图的区域一直无效就不好了~调用的接口便是EndPaint()这个函数。
关于GDI的简单介绍与使用就介绍到这儿。