lee-romantic 's Blog
Everything is OK!
Toggle navigation
lee-romantic 's Blog
主页
About Me
归档
标签
C++GDI绘图学习
2019-02-21 22:13:50
330
0
0
lee-romantic
##Windows GDI 教程(一) 一个简单的绘图程序 常见的图形编程库,除了 GDI 外还有 GDI+、OpenGL、DirectX等等,GDI 是其中最基础的一个库。所以 GDI 注定了不会有高级应用,有兴趣的就当刷低级怪吧。 在教程的最开始,需要简单的说明一些前置条件。 开发环境与前言 首先是标明开发环境: 操作系统:win7 (xp应该可以,win8未测试),win10可以 使用工具:visual studio 2010(或更高) 窗口创建 以前代码的前置问题,首先本教程内的 GDI 画图,在最开始部分主要是在窗口内部绘制(为避免混乱窗口外部,也就是整个桌面的绘制会在很后面的地方讨论)。因此,这里需要对于创建窗口一定的了解。为了让大家可以直接复制完代码就可以在一个文件里面运行,博主准备的代码是手动动态创建窗口的代码,所以这里创建窗口的代码有点长,不过大家不要怕,我们要关注的只是中间的一小部分。这里博主先把代码贴上: ``` #include <windows.h> // 用于注册的窗口类名 const char g_szClassName[] = "myWindowClass"; /* * 第四步,窗口过程 */ LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // 窗口绘制消息 case WM_PAINT: /* * 我们只需要在这里调用我们的 GDI 绘制函数就可以,其他地方可以先无视 */ break; // 窗口关闭消息 case WM_CLOSE: DestroyWindow(hwnd); break; // 窗口销毁消息 case WM_DESTROY: PostQuitMessage(0); // 发送离开消息给系统 break; // 其他消息 default: // pass 给系统,咱不管 return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } /* * 第一步,注册窗口类 */ void RegisterMyWindow(HINSTANCE hInstance) { WNDCLASSEX wc; // 1)配置窗口属性 wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = MyWindowProc; // 设置第四步的窗口过程回调函数 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // 2)注册 if(!RegisterClassEx(&wc)) { MessageBox(NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK); exit(0); // 进程退出 } } /* * 第二步,创建窗口 */ HWND CreateMyWindow(HINSTANCE hInstance, int nCmdShow) { HWND hwnd; hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName, TEXT("我的窗口名称"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, // 出现坐标 x,y 默认分配 窗口宽 400 高 300 NULL, NULL, hInstance, NULL); if(hwnd == NULL) { MessageBox(NULL, TEXT("窗口创建失败!"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK); exit(0); // 进程退出 } // 显示窗口 ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); return hwnd; } /* * 主函数 */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG Msg; // 第一步,注册窗口类 RegisterMyWindow(hInstance); // 第二步:创建窗口 hwnd = CreateMyWindow(hInstance, nCmdShow); // 第三步:消息循环 while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return Msg.wParam; } ``` 运行效果图:  这个创建窗口的代码很长,看起来有点吓人,但是学习 GDI 的过程中,这其中几乎是完全不需要记忆的,只要有一定的了解,然后会 copy 就可以了。当然如果你能懂也是更好,以上代码的出处为 《Windows SDK 教程(三) 一些细节以及动态创建控件》,有兴趣的可以去看看。 那么博主也说过了,最开始的时候,这段长长的代码其实注意一个地方就可以了,就是其中的第四步窗口过程中的一个小 case。 ``` /* * 第四步,窗口过程 */ LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // 窗口绘制消息 case WM_PAINT: /* * 只有这一个 case 是我们 GDI 入门中需要注意的 * * 当程序执行到这个地方的时候,意味着系统像我们的程序发送了 WM_PAINT 消息 * 也就是告诉我们的程序,可以开始绘制窗口的内容了。 * */ break; // 其余略... } return 0; } ``` 这样看来,貌似我们要注意的地方确实很小吧。那么我接着往下走。 PS:默认情况下,系统只会向我们的程序发送一次 WM_PAINT 消息。如果想要再来一次,需要使用 SendMessage 函数,来自己向自己手动发送该消息。 坐标系 GDI 的绘图坐标系与普通的数学坐标系不同,原点 (0,0) 位于左上角。如图: 坐标系  设备上下文(DC) DC (设备上下文, Device Contexts)是 GDI 编程中一个很基础同时也很重要的概念。博主以前看过不少网上的资料以及书上的描述,总感觉他们说的都很奇怪。这里博主为了方便大家理解就说说自己的看法: 大家只要`把 DC 当成一个保存图像的内存对象即可`。当我们使用 GDI 提供的函数去操作 DC 的时候,也就意味着在使用函数去修改保存在这块内存上的图像。 `BeginPaint 与 EndPaint` 用于从目标窗口获取可画图的 DC,以及关闭这个 DC。 函数原型 ``` HDC BeginPaint( _In_ HWND hwnd, // 传入想要获取 DC 的窗口句柄 _Out_ LPPAINTSTRUCT lpPaint // 保存目标窗口的绘图信息 ); BOOL EndPaint( _In_ HWND hWnd, // 目标窗口的句柄 _In_ const PAINTSTRUCT *lpPaint // 目标窗口的绘图信息 ); ``` `SelectObject` 设置目标 DC 选中指定的对象(如画笔、画刷、图片等等)。 函数原型 ``` HGDIOBJ SelectObject( _In_ HDC hdc, // 目标 DC 的句柄 _In_ HGDIOBJ hgdiobj // 被选中的对象 ); ``` `CreatePen` 创建一个画笔(pen)对象。 函数原型 ``` HPEN CreatePen( _In_ int fnPenStyle, // 样式 _In_ int nWidth, // 宽度 _In_ COLORREF crColor // 颜色 ); ``` `MoveToEx` 移动绘制的初始位置。未移动则默认是 (0,0)。(C语言基础好的可以联想 fseek 函数) 函数原型 ``` BOOL MoveToEx( _In_ HDC hdc, // 操作目标DC的句柄 _In_ int X, // x 坐标 _In_ int Y, // y 坐标 _Out_ LPPOINT lpPoint // 保存移动后的当前坐标 ); ``` `LineTo` 使用当前选中的对象(selected object、通常是画笔)从当前位置绘制一条直线到目标位置。 函数原型 ``` BOOL LineTo( _In_ HDC hdc, // 目标DC句柄 _In_ int nXEnd, // 目标位置 x 坐标 _In_ int nYEnd // 目标位置 y 坐标 ); ``` 绘制直线实例 ``` #include <windows.h> // 用于注册的窗口类名 const char g_szClassName[] = "myWindowClass"; void Paint(HWND hwnd) { // paint struct 绘图结构体,存储目标窗口可以绘图的客户端区域(client area) PAINTSTRUCT ps; HDC hdc; // DC(可画图的内存对象) 的句柄 HPEN hpen; // 画笔 // 通过窗口句柄获取该窗口的 DC hdc = BeginPaint(hwnd, &ps); // 创建画笔 hpen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); // DC 选择画笔 SelectObject(hdc,hpen); // (画笔)从初始点移动到 50,50 MoveToEx(hdc, 50, 50, NULL); // (画笔)从初始点画线到 100,100 LineTo(hdc, 150, 100); EndPaint(hwnd, &ps); } /* * 第四步,窗口过程 */ LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { // 窗口绘制消息 case WM_PAINT: Paint(hwnd); // 调用我们的 GDI 绘制函数 break; // 其余略... } return 0; } // 其余略 ``` 运行效果图:  参考: https://lellansin.wordpress.com/2014/07/28/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89-%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E7%BB%98%E5%9B%BE%E7%A8%8B%E5%BA%8F/ ##Windows GDI 教程(二) 画笔的颜色与样式: 参考: https://lellansin.wordpress.com/2014/07/29/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E4%BA%8C%EF%BC%89-%E7%94%BB%E7%AC%94%E7%9A%84%E9%A2%9C%E8%89%B2%E4%B8%8E%E6%A0%B7%E5%BC%8F/ ##Windows GDI 教程(三) 绘制函数图像以及多边形 https://lellansin.wordpress.com/2014/07/29/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E4%B8%89%EF%BC%89-%E7%BB%98%E5%88%B6%E5%87%BD%E6%95%B0%E5%9B%BE%E5%83%8F%E4%BB%A5%E5%8F%8A%E5%A4%9A%E8%BE%B9%E5%BD%A2/ ##Windows GDI 教程(四) 文字与字体 https://lellansin.wordpress.com/2014/07/29/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E5%9B%9B%EF%BC%89-%E6%96%87%E5%AD%97%E4%B8%8E%E5%AD%97%E4%BD%93/ ##Windows GDI 教程(五) 填充图形和画刷 https://lellansin.wordpress.com/2014/07/30/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E4%BA%94%EF%BC%89-%E5%A1%AB%E5%85%85%E5%9B%BE%E5%BD%A2%E5%92%8C%E7%94%BB%E5%88%B7/ ##Windows GDI 教程(六) DC与加载位图 https://lellansin.wordpress.com/2014/07/31/windows-gdi-%E6%95%99%E7%A8%8B%EF%BC%88%E5%85%AD%EF%BC%89-dc%E4%B8%8E%E5%8A%A0%E8%BD%BD%E4%BD%8D%E5%9B%BE/ ##
上一篇:
DOS命令学习(从入门到精通)
下一篇:
DLL中调用约定和名称修饰
0
赞
330 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册