(9):绘图(B)
最后更新于:2022-04-01 20:07:24
我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣。
首先,我们写一个程序骨架子,以便做实验。
~~~
#include
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hThisApp,
HINSTANCE hPrevApp,
LPSTR lpsCmdln,
int iShow)
{
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
// 默认光标类型为箭头
wc.hCursor = LoadCursor(hThisApp, IDC_ARROW);
// 默认应用程序图标
wc.hIcon = LoadIcon(hThisApp, IDI_APPLICATION);
wc.hInstance = hThisApp;
wc.lpfnWndProc = MainWinProc;
wc.lpszClassName = L"MyAppTest";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
// 注册窗口类
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow(
L"MyAppTest",
L"绘画课",
/* 使用 WS_VISIBLE 就不用调用ShowWindow了 */
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
100,
45,
500,
380,
NULL,
NULL,
hThisApp,
NULL);
// 消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
/*
待实现
*/
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
~~~
### CreatePen函数
我们要进行素描画创作,所以我们必须想清楚要使用什么样的钢笔,画出什么样的线条。故,画图之前得创建一支钢笔,不然,巧妇难为无米之炊。
~~~
HPEN CreatePen(
int iStyle, //钢笔的样式,如虚线、实线
int cWidth, //线条宽度
COLORREF color //线条是啥颜色的
);
~~~
第一个参数指定线条的样式,如
~~~
/* Pen Styles */
#define PS_SOLID 0
#define PS_DASH 1 /* ------- */
#define PS_DOT 2 /* ....... */
#define PS_DASHDOT 3 /* _._._._ */
#define PS_DASHDOTDOT 4 /* _.._.._ */
#define PS_NULL 5
#define PS_INSIDEFRAME 6
#define PS_USERSTYLE 7
#define PS_ALTERNATE 8
~~~
函数成功创建钢笔后就会返回HPEN,H开头的你就要知道它表示句柄。Pen也是系统资源,所以创建笔后系统要为它一个标识。
### SelectObject函数
上过美术课,你会知道,有了用于进行素描创作的钢笔还不能动手干活,我们还需要有纸。接下来,调用SelectObject函数,把刚才创建的钢笔与绘图纸关联,就等于把我们创建的绘图资源放进DC工具箱中,
~~~
HGDIOBJ WINAPI SelectObject(
HDC hdc, //设备描述表的句柄
HGDIOBJ h //要放到DC中的资源的句柄
);
~~~
调用成功后,返回原先的资源句柄。
### MoveToEx和LineTo
MoveToEx是设置绘制的起点,下次绘图将从这个点开始。它的最后一个参数将被设置为当前点,即Move后的坐标。LineTo从当前坐标开始,到指定坐标之间绘制一条线段。
~~~
// 创建钢笔
HPEN pen = CreatePen(PS_DASH, 1, RGB(0,255,50));
// 把笔选到DC中
SelectObject(ps.hdc, pen);
// 设定线段的起点
MoveToEx(ps.hdc, 15, 25, NULL);
// 绘制线条
LineTo(ps.hdc, 65, 49);
LineTo(ps.hdc, 12, 120);
LineTo(ps.hdc, 250, 78);
LineTo(ps.hdc, 312, 185);
DeleteObject(pen);
~~~
记住,只要是我们创建的句柄,用完后调用DeleteObject函数将其销毁。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d0d35f8.PNG)
### PolyBezier函数和PolyBezierTo函数
两个函数都是用来绘制贝塞尔曲线的,不同的是,PolyBezier函数包含指定的起点和终点,PolyBezierTo是从当前点开始绘制贝塞尔曲线。
~~~
// 绘制贝塞尔曲线
pen = CreatePen(PS_DOT, 1, RGB(0,3,255));
SelectObject(ps.hdc, pen);
POINT* pts = new POINT[4];
pts[0].x = 421;
pts[0].y = 16;
pts[1].x = 7;
pts[1].y = 197;
pts[2].x = 480;
pts[2].y = 320;
pts[3].x = 30;
pts[3].y = 350;
PolyBezier(ps.hdc, pts, 4);
delete [] pts;
// 第二段贝塞尔曲线
POINT* pts2 = new POINT[3];
pts2[0].x = 176;
pts2[0].y = 84;
pts2[1].x = 17;
pts2[1].y = 247;
pts2[2].x = 400;
pts2[2].y = 490;
// 移动当前点
MoveToEx(ps.hdc, 395, 270, NULL);
PolyBezierTo(ps.hdc, pts2, 3);
delete [] pts2;
~~~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d0e68ff.PNG)
###
### PolyPolyline绘制复合线条
PolyPolyline函数可以绘制多段复合线条。它的声明如下:
~~~
BOOL PolyPolyline(
HDC hdc,
const POINT *lppt,
const DWORD *lpdwPolyPoints,
DWORD cCount
);
~~~
lppt参数指向一个POINT的数组,它包含绘制复合线条所需的所有点;lpdwPolyPoints指向一个数组,这个数字数组指明如何分配点数组。
例如,lppt有7个点,我计划,前2个点绘制第一条线,接着3个点绘制第二条线,最后2个点绘制第三条线,这样一来,lpdwPolyPolyPoints得值应为:
{ 2, 3, 2 }
nCount里包含lpdwPolyPolyPoints中的数量,我们上面的例子是3.
由于两点决定一条线段,因此,lpdwPolyPolyPoints里面的值记得要>=2。
~~~
// 复杂图形
pen = CreatePen(PS_DASHDOTDOT, 1, RGB(80,20,160));
SelectObject(ps.hdc, pen);
POINT plpts[10] =
{
{47,3}, {11,46}, {28,199}, {203,305}, {94,22},
{402,377}, {21,45}, {237,7}, {300,398}, {175,25}
};
DWORD arr[4] = { 2, 3, 3, 2};
PolyPolyline(ps.hdc, &plpts[0], &arr[0], 4);
~~~
上面的代码将画出以下图形。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-14_575fd2d10c0f7.PNG)
完整的代码清单如下:
~~~
#include
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hThisApp,
HINSTANCE hPrevApp,
LPSTR lpsCmdln,
int iShow)
{
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
// 默认光标类型为箭头
wc.hCursor = LoadCursor(hThisApp, IDC_ARROW);
// 默认应用程序图标
wc.hIcon = LoadIcon(hThisApp, IDI_APPLICATION);
wc.hInstance = hThisApp;
wc.lpfnWndProc = MainWinProc;
wc.lpszClassName = L"MyAppTest";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW | CS_VREDRAW;
// 注册窗口类
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow(
L"MyAppTest",
L"绘画课",
/* 使用 WS_VISIBLE 就不用调用ShowWindow了 */
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
100,
45,
500,
380,
NULL,
NULL,
hThisApp,
NULL);
// 消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK MainWinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
// 创建钢笔
HPEN pen = CreatePen(PS_DASH, 1, RGB(0,255,50));
// 把笔选到DC中
SelectObject(ps.hdc, pen);
// 设定线段的起点
MoveToEx(ps.hdc, 15, 25, NULL);
// 绘制线条
LineTo(ps.hdc, 65, 49);
LineTo(ps.hdc, 12, 120);
LineTo(ps.hdc, 250, 78);
LineTo(ps.hdc, 312, 185);
// 绘制贝塞尔曲线
pen = CreatePen(PS_DOT, 1, RGB(0,3,255));
SelectObject(ps.hdc, pen);
POINT* pts = new POINT[4];
pts[0].x = 421;
pts[0].y = 16;
pts[1].x = 7;
pts[1].y = 197;
pts[2].x = 480;
pts[2].y = 320;
pts[3].x = 30;
pts[3].y = 350;
PolyBezier(ps.hdc, pts, 4);
delete [] pts;
// 第二段贝塞尔曲线
POINT* pts2 = new POINT[3];
pts2[0].x = 176;
pts2[0].y = 84;
pts2[1].x = 17;
pts2[1].y = 247;
pts2[2].x = 400;
pts2[2].y = 490;
// 移动当前点
MoveToEx(ps.hdc, 395, 270, NULL);
PolyBezierTo(ps.hdc, pts2, 3);
delete [] pts2;
// 复杂图形
pen = CreatePen(PS_DASHDOTDOT, 1, RGB(80,20,160));
SelectObject(ps.hdc, pen);
POINT plpts[10] =
{
{47,3}, {11,46}, {28,199}, {203,305}, {94,22},
{402,377}, {21,45}, {237,7}, {300,398}, {175,25}
};
DWORD arr[4] = { 2, 3, 3, 2};
PolyPolyline(ps.hdc, &plpts[0], &arr[0], 4);
DeleteObject(pen);
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
~~~
';