企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣。 首先,我们写一个程序骨架子,以便做实验。 ~~~ #include <Windows.h> 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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/2016-06-14_575fd2d10c0f7.PNG) 完整的代码清单如下: ~~~ #include <Windows.h> 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); } ~~~