这一篇文章分享本人学习win32绘图编程,其中包括GDI绘图对象,绘图基础,基本图形的绘制,画笔画刷的使用,文本绘制,以及文本字体的更改。
一.绘图基础
- 绘图设备DC(Device Context),有时也叫做绘图上下文/绘图描述表/显示设备上下文
- HDC-DC句柄,表示绘图设备
- GDI-Windows graphics device interface (win32提供的绘图API)
- 颜色:
- RGB函数:
- 红黄蓝按照一定比例配比,每个点颜色都是3个字节,24位保存,一共有0~2^24-1种
- 16位操作系统通常使用三个参数来表示颜色,如:5,5,6
- 32位操作系统通常使用4个参数来表示颜色,如:8,8,8,8.最后一个参数表示透明度
- 绘图设备句柄:我们使用绘图设备,必须拿到绘图设备句柄
我们来想想这样一个场景:我们需要绘图,但是我们自己不会,我们把绘图设备比喻为一个画家,那么拿到绘图设备句柄的过程就相当于是把这个画家抓过来,抓过来让他完成工作后,还需要把画家放走。
PAINTSTRUCT ps = {0}; //在绘图之前,我们需要定义一个PAINTSTURCT结构体,我们不需要关注结构体内的内容 HDC hdc = BeginPaint(...); //抓绘图设备句柄 TextOut(hdc,100,100,"Hello",...); //绘制文本 EndPaint(...); //释放绘图设备
我们来细讲一下BeginPaint
函数和EndPaint
函数:
1.BeginPaint函数
BeginPaint 函数准备用于绘制的指定窗口,并使用有关绘图的信息填充 PAINTSTRUCT 结构。
HDC BeginPaint( HWND hWnd, LPPAINTSTRUCT lpPaintStruct
其中,hWnd参数指定了要重新绘制的窗口句柄,lpPaintStruct指向我们定义的PAINTSTRUCT结构,这个函数会填上该结构中所需要的信息。
- 返回值:如果创建成功,返回值是指定窗口的显示设备上下文的句柄,如果失败,返回NULL,指示没有显示设备上下文可用。
2.EndPaint函数
EndPaint函数标记指定窗口中绘制的结尾,每次调用BeginPaint函数时都需要此函数,但仅在绘制完成后才需要此函数。
语法:
BOOL EndPaint( HWND hWnd, //已经重新绘制的窗口的句柄 const PAINTSTRUCT* lpPaint //指向包含BeginPaint检索的绘画信息的PAINTSTRUCT结构的指针 );
返回值:返回值始终为非零值。
EndPaint函数释放BeginPaint函数检索道德显示设备上下文
3.颜色的使用
colorref–实际DWORD–using long
例如:
COLORREF nColor = 0;
赋值RGB宏:
例:
`nColor =RGB(0,0,255);
获取RGB的值:
GetRValue/GetGValue/GetBValue
函数例:
BYTE nRed = GetRValue(nColor);
二.基本图形绘制
1.点的绘制
SetPixel
函数
SetPixel函数将指定坐标处的像素设置为指定颜色
函数原型:
COLORREF SetPixel( HDC hdc, //设备上下文句柄 int x, //要设置的x坐标 int y, //要设置的y坐标 COLORREF color //用于绘制点的颜色 );
- 返回值:如果函数成功,则返回值为函数将像素设置为的RGB值。此值可能与crColor指定的颜色不同,当找不到指定颜色的完全匹配项时发生。如果函数失败,返回值为-1.
回调函数中处理绘图消息
这里给出的是所有的回调函数内容,大家可以翻到最后直接看绘制窗口消息处理过程。
LRESULT CALLBACK WindowProc( IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { char output[256] = { 0 }; switch (uMsg) { //常见消息 case WM_DESTROY: { PostQuitMessage(0); break; } case WM_SYSCOMMAND: { sprintf(output, TEXT("检测到WM_COMMAND消息\n")); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_CREATE: { sprintf(output, "检测到WM_CREATE消息,将创建窗口。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_SIZE: { sprintf(output, "lParam:窗口宽变化为:%d,窗口高变化为:%d \n", HIWORD(lParam), LOWORD(lParam)); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } //菜单命令消息 case WM_COMMAND: { switch(LOWORD(wParam)) { case MY_OPEN: { sprintf(output, "打开按钮被点击,请到回调函数中做具体处理。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case MY_QUIT: { sprintf(output, "退出按钮被点击,请到回调函数中做具体处理。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case IDM_ABOUT: { sprintf(output, "帮助按钮被点击,请到回调函数中做具体处理。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); } case MY_NEWFILE: { sprintf(output, "打开新文件按钮被点击,请到回调函数中做具体处理。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case MY_LASTTIMEFILE: { sprintf(output, "上次打开文件按钮被点击,请到回调函数中做具体处理。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } } break; } //键盘消息 case WM_KEYDOWN: { sprintf(output, "检测到WM_KEYDOWN消息,键码值:%d.\n", wParam); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_KEYUP: { sprintf(output, "检测到WM_KEYUP消息,键码值:%d.该按键被放开\n", wParam); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } //鼠标消息 case WM_LBUTTONDOWN: { sprintf(output, "检测到WM_LBUTTONDOWN消息,鼠标左键被按下。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_LBUTTONUP: { sprintf(output, "检测到WM_LBUTTONUP消息,鼠标左键被放开。\n"); WriteConsole(g_hOUTPUT, output, strlen(output),0,0); break; } case WM_RBUTTONDOWN: { sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被按下。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_RBUTTONUP: { sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被放开。\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); } case WM_MOUSEMOVE: { sprintf(output, "检测到WM_MOUSEMOVE消息,鼠标移动中,鼠标位置(%d,%d).\n", LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_MOUSEWHEEL: { sprintf(output, "鼠标滚轮滚动中,偏移量:%d,鼠标当前位置(%d,%d)\n",HIWORD(wParam), LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); break; } case WM_CONTEXTMENU: { TrackPopupMenu( CreatePopupMenu(), TPM_LEFTALIGN, 0, 0, 0, hwnd, 0); break; } //绘制窗口消息 case WM_PAINT: { sprintf(output, "检测到WM_PAINT消息"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hwnd, &ps); SetPixel(hdc, 100, 100, RGB(6, 8, 9)); EndPaint(hwnd, &ps); break; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
处理效果:
大家可以看到程序中出现的点。由于是一个像素,可能看的不是很清楚。
2.线的绘制
像绘制点一样,我们也需要抓到设备上下文的句柄,画线,释放设备上下文。
在绘制线条的时候,我们需要几个函数,我们先来学习一下这几个函数:
MoveToEx
函数
MoveToEx函数将当前位置更新为指定点,并选择性地返回上一个位置。
语法:
BOOL MoveToEx( HDC hdc, //设备上下文句柄 int x, int y, //指定当前点 LPPOINT lppt //指向接收当前位置的POINT指针,如果此参数为NULL指针,则不返回上一个位置 )
- 返回值:
- 如果函数成功,则返回值为非零值
- 如果函数失败,则返回值为0
- 注释:MoveToEx函数将影响所有的绘图函数
LineTo
函数
LineTo函数从当前位置绘制一行,但不包括指定点。
BOOL LineTo( HDC hdc, //设备上下文句柄 int x, int y //指定线条终点坐标 );
- 返回值:
- 如果函数成功,返回值为非零值
- 如果函数失败,返回值为零
- 回调函数处理:
case WM_PAINT: { sprintf(output, "检测到WM_PAINT消息\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hwnd, &ps); MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, 500, 500); EndPaint(hwnd, &ps); break; }
处理效果:
3.封闭图形绘制
封闭图形:能够用画刷填充的图形
这里给出绘制矩形和绘制圆形的函数,其他函数有很多,大家可以自行学习
- 绘制矩形:
Rectangle
函数
Rectangle 函数绘制矩形。 该矩形使用当前笔轮廓,并使用当前画笔填充。
MSDN官方文档解释Rectangle函数
BOOL Rectangle( HDC hdc, //设备上下文句柄 int left, int top, int right, int botton );
回调函数处理:
case WM_PAINT: { sprintf(output, "检测到WM_PAINT消息\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hwnd, &ps); /* MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, 500, 500);*/ Rectangle(hdc,100,100,200,200); EndPaint(hwnd, &ps); break; }
处理效果:
- 绘制圆形:
Ellipse
函数 - 椭圆函数绘制椭圆。 椭圆的中心是指定边界矩形的中心。
BOOL Ellipse( HDC hdc, //设备上下文句柄 int left, int top, int right, int bottom );
回调函数处理:
case WM_PAINT: { sprintf(output, "检测到WM_PAINT消息\n"); WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0); PAINTSTRUCT ps = { 0 }; HDC hdc = BeginPaint(hwnd, &ps); /* MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, 500, 500);*/ //绘制直线 // Rectangle(hdc, 100, 100, 200, 200); //绘制矩形 Ellipse(hdc, 100, 100, 50, 50); //绘制圆形 EndPaint(hwnd, &ps); break; }
处理效果: