跟我一起玩Win32开发(8):绘图(A)
从本篇开始,我就不吹牛皮,那就吹吹兔皮吧。说说与绘图有关的东东。
要进行绘制,首先要得到一个DC,啥是DC呢?按字面翻译叫设备上下文,也可以翻译为设备描述表,它主要指API为我们封装了一些与显示设备相关的交互操作,我们这里说的是图形的绘制,自然指的是显卡。当然,对于同一客观事物,世界上并不存在唯一的理解方案,技术上的东西最终拿来用的,不应该有硬性的去统一。我们之中的很多人,最大的失败在于,人家说要这样理解他就毫不怀疑地这样理解,权威人士说要这样这样,他就不经过大脑思考地跟着那样那样。
虽然我的母校是名不见经传的三流大学,但回忆我的大学,很幸运,我曾经遇到几位好老师,真正的好老师,不是那些所谓的叫兽砖家。记得某位老师说过:这本书,如果读完了你一无所获,那你太失败了;如果你把书中的内容都掌握了,勉强及格;如果你能把书中的所有观点全部推翻,你才是优秀的。
在许多情况下,我们绘图都是遵循先GetDC-----〉绘图------〉ReleaseDC,DC是一种资源,用完了要释放,我们到图书馆借书,看完了要还书。不过,在处理WM_PAINT消息时,调用BeginPaint函数后,开始绘图,画完了调用EndPaint。当然这个并不违背我们前面所说的使用完HDC要释放的道理,只是BeginPaint函数会自动调用GetDC,EndPaint会自动调用ReleaseDC。
好的,首先我们来写几个字吧。绘制文本可以使用DrawText函数,他的最后一个参数是文本的对齐格式,如左对齐、居中、右对齐等。
- PAINTSTRUCT ps;
- switch(msg)
- {
- case WM_PAINT:
- BeginPaint(hwnd, &ps);
声明一个PAINTSTRUCT结构体的变量,然后传给BeginPaint函数,之后就可以画东西了。
- DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);
但是,如果我希望文本的颜色不是默认的黑色,我们可以考虑调用SetTextColor函数来设置颜色,之后我们绘制的所有文本都是这个颜色了,如果之后希望改变文本的颜色,就再次调用SetTextColor函数。
- SetTextColor(ps.hdc, RGB(0,150,255));
RGB宏可以通过三个值来确定颜色值,这个估计不用我介绍了,如果不懂RGB,可以去请教芙蓉姐姐。
我希望新绘制的文本在前一个文本的下一行,当然,你可能会说,用DrawText的时候把传给它的RECT改一下坐标就行了。这方法虽然可以,但我们不好调坐标。其实,我们如果知道文本字符的高度,那不就好办了吗,对的,要获得文本高度,可以调用GetTextMetrics函数。现在我们要用的工具都齐全了。
- case WM_PAINT:
- BeginPaint(hwnd, &ps);
- TEXTMETRIC tm;
- // 取得与文本相关的数据
- GetTextMetrics(ps.hdc, &tm);
- RECT rect;
- rect.top = 0L;
- rect.left = ps.rcPaint.left;
- rect.right = ps.rcPaint.right;
- rect.bottom = rect.top + tm.tmHeight;
- // 第一行文本
- SetTextColor(ps.hdc, RGB(0,150,255));
- DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);
- // 第二行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(220, 12, 50));
- DrawText(ps.hdc, L"疑是地上霜", -1, &rect, DT_LEFT);
- // 第三行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(30,255,7));
- DrawText(ps.hdc, L"举头望明月", -1, &rect, DT_RIGHT);
- // 第四行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(0,40,210));
- DrawText(ps.hdc, L"低头思故乡", -1, &rect, DT_RIGHT);
- EndPaint(hwnd, &ps);
- return 0;
这个不难理解吧,就是每一行文本的矩形区域得顶部和底部坐标分别加上文本的高度。
现在可以看看效果了。
接下来,我们画几条弧线。绘制弧线使用Arc函数,第一个参数是目标HDC,随后的4个参数用于确定弧线所在的位置的矩形,最后4个参数是确定弧线的开始点和结束点的坐标。
- BOOL WINAPI Arc(
- HDC hdc, //DC的句柄
- int x1, // 矩形的左坐标
- int y1, //矩形上坐标
- int x2,//矩形的右坐标
- int y2, //矩形的下坐标
- int x3, //起点x坐标
- int y3, //起点y坐标
- int x4, //终点x坐标
- int y4 //终点y坐标
- );
在默认情况下,弧线是逆时针方向的。
- // 绘制弧线
- HPEN pen = CreatePen(PS_SOLID, 2, RGB(200, 100, 20));//创建笔
- // 将笔选到DC中
- auto oldObj = SelectObject(ps.hdc, pen);
- // 画弧线
- Arc(ps.hdc, 20, 100, 300, 300, 39, 110, 280, 285);
- Arc(ps.hdc, 200, 160, 390, 400, 300,350, 380,165);
- // 画完之后,把原先的笔选回去
- SelectObject(ps.hdc, oldObj);
- // 清理
- DeleteObject(pen);
上面代码将画出如下图所示的弧线。
默认是逆时针方向,现在我想画顺时针方向的弧线。看下面例子,通过SetArcDirection函数改变弧线的方向。
- /*
- AD_COUNTERCLOCKWISE表示逆时针方向
- AD_CLOCKWISE表示顺时针方向
- */
- SetArcDirection(ps.hdc, AD_CLOCKWISE);
- Arc(ps.hdc, 20,150, 460,450, 90,162, 85,300);
下面介绍一下LineDDA函数,这个家伙不简单,为啥?因为I它可以通过回调函数来对一条线段中不同的点进行分别处理。其回调函数如下:
- VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);
最后一个参数是长指针,我们可以将一个HDC的地址传给它。
因为需要回调函数,我们得先写好回调函数,但是,在文件的前面要先声明一下,C语言的函数如果在调用之后定义,就必须先声明,不然编译的时候找不到。
- VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData)
- {
- // 从参数中取得HDC
- HDC hdc = *((HDC*)lpData);
- // 不同位置的线段设置不同的颜色
- int type=0;
- if(x <= 510 || y <= 200)
- {
- type = 0;
- }
- else if((x > 510 && x <= 700) ||
- (y > 720 && y <= 360))
- {
- type = 1;
- }
- else
- {
- type = 2;
- }
- // 根据不同情况着色
- switch(type)
- {
- case 0:
- SetPixel(hdc,x,y,RGB(0,255,0));
- break;
- case 1:
- SetPixel(hdc,x,y,RGB(0,0,255));
- break;
- case 2:
- SetPixel(hdc,x,y,RGB(255,0,0));
- break;
- default:
- SetPixel(hdc,x,y,RGB(255,0,0));
- }
- }
接着在相应WM_PAINT消息的时候调用LineDDA函数。
- LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);
结果你会看到,画出来的线段是有三种颜色的。
完整的代码如下:
- #include <Windows.h>
- LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
- VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);
- int WINAPI WinMain(HINSTANCE hTheApp,
- HINSTANCE hPrevApp,
- LPSTR cmdline,
- int nShow)
- {
- WNDCLASS wc = { };
- wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wc.hInstance = hTheApp;
- wc.lpfnWndProc = WindowProc;
- wc.lpszClassName = L"MyApp";
- wc.style = CS_HREDRAW | CS_VREDRAW;
- RegisterClass(&wc);
- HWND hwnd = CreateWindow(L"MyApp",
- L"我的应用程序",
- WS_OVERLAPPEDWINDOW | WS_VISIBLE,
- 35,
- 28,
- 600,
- 500,
- NULL,
- NULL,
- hTheApp,
- NULL);
- if(hwnd == NULL)
- return -1;
- // 消息循环
- MSG msg;
- while(GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- PAINTSTRUCT ps;
- switch(msg)
- {
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- case WM_PAINT:
- BeginPaint(hwnd, &ps);
- TEXTMETRIC tm;
- // 取得与文本相关的数据
- GetTextMetrics(ps.hdc, &tm);
- RECT rect;
- rect.top = 0L;
- rect.left = ps.rcPaint.left;
- rect.right = ps.rcPaint.right;
- rect.bottom = rect.top + tm.tmHeight;
- // 第一行文本
- SetTextColor(ps.hdc, RGB(0,150,255));
- DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);
- // 第二行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(220, 12, 50));
- DrawText(ps.hdc, L"疑是地上霜", -1, &rect, DT_LEFT);
- // 第三行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(30,255,7));
- DrawText(ps.hdc, L"举头望明月", -1, &rect, DT_RIGHT);
- // 第四行文本
- rect.top += tm.tmHeight;
- rect.bottom += tm.tmHeight;
- SetTextColor(ps.hdc, RGB(0,40,210));
- DrawText(ps.hdc, L"低头思故乡", -1, &rect, DT_RIGHT);
- // 绘制弧线
- HPEN pen = CreatePen(PS_SOLID, 3, RGB(200, 100, 20));//创建笔
- // 将笔选到DC中
- auto oldObj = SelectObject(ps.hdc, pen);
- // 画弧线
- /*Arc(ps.hdc, 20, 100, 300, 300, 39, 110, 280, 285);
- Arc(ps.hdc, 200, 160, 390, 400, 300,350, 380,165);*/
- /*
- AD_COUNTERCLOCKWISE表示逆时针方向
- AD_CLOCKWISE表示顺时针方向
- */
- SetArcDirection(ps.hdc, AD_CLOCKWISE);
- Arc(ps.hdc, 20,150, 300,450, 90,162, 85,300);
- // 画完之后,把原先的笔选回去
- SelectObject(ps.hdc, oldObj);
- // 清理
- DeleteObject(pen);
- // 分段线条
- LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);
- EndPaint(hwnd, &ps);
- return 0;
- }
- return DefWindowProc(hwnd, msg, wParam, lParam);
- }
- VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData)
- {
- // 从参数中取得HDC
- HDC hdc = *((HDC*)lpData);
- // 不同位置的线段设置不同的颜色
- int type=0;
- if(x <= 510 || y <= 200)
- {
- type = 0;
- }
- else if((x > 510 && x <= 700) ||
- (y > 720 && y <= 360))
- {
- type = 1;
- }
- else
- {
- type = 2;
- }
- // 根据不同情况着色
- switch(type)
- {
- case 0:
- SetPixel(hdc,x,y,RGB(0,255,0));
- break;
- case 1:
- SetPixel(hdc,x,y,RGB(0,0,255));
- break;
- case 2:
- SetPixel(hdc,x,y,RGB(255,0,0));
- break;
- default:
- SetPixel(hdc,x,y,RGB(255,0,0));
- }
- }
跟我一起玩Win32开发(8):绘图(A)的更多相关文章
- 跟我一起玩Win32开发(转自CSDN-东邪独孤)
跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩W ...
- 跟我一起玩Win32开发(17):启动和结束进程
这里我再次说明一下,我不知道为什么,现在的人那么喜欢走极端,估计是价值观都“升级”了的缘故吧. 我撰写这一系列Win32相关的文章,并不是叫大家一定要用Win32去开发项目,仅仅是给大家了解一下,Wi ...
- 跟我一起玩Win32开发(18):使用对话框的两个技巧
相信大家知道对话框怎么用了,就是先用“资源编辑器”设计一个对话框,然后在代码中加载处理.今天,我向大家分享两个使用对话框的技巧,还是比较实用的.不用担心,先喝杯茶,很简单的,一点也不复杂,总之,看俺写 ...
- 跟我一起玩Win32开发(9):绘图(B)
我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣. 首先,我们写一个程序骨架子,以便做实验. #include <Windows.h> LRESULT CALLBACK MainWinPro ...
- 跟我一起玩Win32开发(10):绘图(C)
今天我们来欣赏一下用于填充图形的函数,当然我不会逐个去介绍,因为我们参考MSDN直接套参数就可以了. SetDCBrushColor函数有必要扯一下,它的声明如下: COLORREF SetDCBru ...
- 跟我一起玩Win32开发(19):浏览和打开文件
在应用程序中,我们很经常要实现的功能,是Open文件或保存文件对话框,让用户来选择一个或N个文件.本文我将介绍两种思路,第一种方法较为复杂,第二种方法较为简单. 方法一:老规矩 这是一种传统方法,使用 ...
- 跟我一起玩Win32开发(12):使用控件——单选按钮
今天,咱们还是接着玩“控件斗地主”,这是我原创的超级游戏,有益身心健康,玩一朝,十年少. 哦,对,脑细胞极速运动了一下,想起了一个问题,这个破问题虽然网上有很多种解决方案,但是,并没有让所有人都解决问 ...
- 跟我一起玩Win32开发(23):渐变颜色填充
GradientFill函数可以对特定的矩形区域或者三角形区域进行渐变颜色的填充.我们先来看看GradientFill函数到底长得什么样子,帅不帅. BOOL GradientFill( _In_ ...
- 跟我一起玩Win32开发(21):复制&粘贴&剪贴板操作
我要提醒一下大家,看了我的博文学到的知识,千万不要用于实际开发,不然你会被你的上司骂:“妈的,这些东西哪来的,从来没有人这样做过.”不信你试试,脑细胞被冻结的经理或者技术总监们肯定会这样说的. 如果是 ...
随机推荐
- XenServer网卡Bonding
在给XenServer配置网卡bonding时,需要在所有节点都添加到集群之后再进行,这也是来自Citrix的建议:"Citrix recommends never joining a ho ...
- python的pexpect模块
Pexpect 是 Don Libes 的 Expect 语言的一个 Python 实现,是一个用来启动子程序,并使用正则表达式对程序输出做出特定响应,以此实现与其自动交互的 Python 模块. P ...
- 模式识别之聚类算法k-均值---k-均值聚类算法c实现
//写个简单的先练习一下,测试通过 //k-均值聚类算法C语言版 #include <stdlib.h> #include <stdio.h> #inc ...
- Ctrl+Enter 选中文本提交
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <bod ...
- Xamarin.Android 记事本(一)
导读 1.视图及数据库的创建 2.listview数据绑定 3.listview的点击事件 正文 如何创建一个listview,大家可以看这里,基本流程操作是一模一样的,我就不多说了,然后就是新建一个 ...
- 如何查看Java进程并获取进程ID?
1. 在 LINUX 命令平台输入 1-2 个字符后按 Tab 键会自动补全后面的部分(前提是要有这个东西,例如在装了 tomcat 的前提下, 输入 tomcat 的 to 按 tab).2. ps ...
- Cache 简介
一.什么是缓存1.Cache是高速缓冲存储器 一种特殊的存储器子系统,其中复制了频繁使用的数据以利于快速访问2.凡是位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之 ...
- hdoj 1875 畅通project再续【最小生成树 kruskal && prim】
畅通project再续 Problem Description 相信大家都听说一个"百岛湖"的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其它的小岛时都要通过划小船来实现. ...
- HDFS集群安装部署
准备环境: 三台centos7虚拟机(Node-1,Node-2,Node-3) 配置虚拟机网络,保证三台机器可以互相ping通,并且和宿主机可以互相ping通.如果仅仅是作为虚拟机学习,可以关闭防火 ...
- java 监听文件或者文件夹变化的几种方式
1.log4j的实现的文件内容变化监听 package com.jp.filemonitor; import org.apache.log4j.helpers.FileWatchdog; public ...