从本篇开始,我就不吹牛皮,那就吹吹兔皮吧。说说与绘图有关的东东。

要进行绘制,首先要得到一个DC,啥是DC呢?按字面翻译叫设备上下文,也可以翻译为设备描述表,它主要指API为我们封装了一些与显示设备相关的交互操作,我们这里说的是图形的绘制,自然指的是显卡。当然,对于同一客观事物,世界上并不存在唯一的理解方案,技术上的东西最终拿来用的,不应该有硬性的去统一。我们之中的很多人,最大的失败在于,人家说要这样理解他就毫不怀疑地这样理解,权威人士说要这样这样,他就不经过大脑思考地跟着那样那样。

虽然我的母校是名不见经传的三流大学,但回忆我的大学,很幸运,我曾经遇到几位好老师,真正的好老师,不是那些所谓的叫兽砖家。记得某位老师说过:这本书,如果读完了你一无所获,那你太失败了;如果你把书中的内容都掌握了,勉强及格;如果你能把书中的所有观点全部推翻,你才是优秀的。

在许多情况下,我们绘图都是遵循先GetDC-----〉绘图------〉ReleaseDC,DC是一种资源,用完了要释放,我们到图书馆借书,看完了要还书。不过,在处理WM_PAINT消息时,调用BeginPaint函数后,开始绘图,画完了调用EndPaint。当然这个并不违背我们前面所说的使用完HDC要释放的道理,只是BeginPaint函数会自动调用GetDC,EndPaint会自动调用ReleaseDC。

好的,首先我们来写几个字吧。绘制文本可以使用DrawText函数,他的最后一个参数是文本的对齐格式,如左对齐、居中、右对齐等。

  1. PAINTSTRUCT ps;
  2. switch(msg)
  3. {
  4. case WM_PAINT:
  5. BeginPaint(hwnd, &ps);

声明一个PAINTSTRUCT结构体的变量,然后传给BeginPaint函数,之后就可以画东西了。

  1. DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);

但是,如果我希望文本的颜色不是默认的黑色,我们可以考虑调用SetTextColor函数来设置颜色,之后我们绘制的所有文本都是这个颜色了,如果之后希望改变文本的颜色,就再次调用SetTextColor函数。

  1. SetTextColor(ps.hdc, RGB(0,150,255));

RGB宏可以通过三个值来确定颜色值,这个估计不用我介绍了,如果不懂RGB,可以去请教芙蓉姐姐。

我希望新绘制的文本在前一个文本的下一行,当然,你可能会说,用DrawText的时候把传给它的RECT改一下坐标就行了。这方法虽然可以,但我们不好调坐标。其实,我们如果知道文本字符的高度,那不就好办了吗,对的,要获得文本高度,可以调用GetTextMetrics函数。现在我们要用的工具都齐全了。

  1. case WM_PAINT:
  2. BeginPaint(hwnd, &ps);
  3. TEXTMETRIC tm;
  4. // 取得与文本相关的数据
  5. GetTextMetrics(ps.hdc, &tm);
  6. RECT rect;
  7. rect.top = 0L;
  8. rect.left = ps.rcPaint.left;
  9. rect.right = ps.rcPaint.right;
  10. rect.bottom = rect.top + tm.tmHeight;
  11. // 第一行文本
  12. SetTextColor(ps.hdc, RGB(0,150,255));
  13. DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);
  14. // 第二行文本
  15. rect.top += tm.tmHeight;
  16. rect.bottom += tm.tmHeight;
  17. SetTextColor(ps.hdc, RGB(220, 12, 50));
  18. DrawText(ps.hdc, L"疑是地上霜", -1, &rect, DT_LEFT);
  19. // 第三行文本
  20. rect.top += tm.tmHeight;
  21. rect.bottom += tm.tmHeight;
  22. SetTextColor(ps.hdc, RGB(30,255,7));
  23. DrawText(ps.hdc, L"举头望明月", -1, &rect, DT_RIGHT);
  24. // 第四行文本
  25. rect.top += tm.tmHeight;
  26. rect.bottom += tm.tmHeight;
  27. SetTextColor(ps.hdc, RGB(0,40,210));
  28. DrawText(ps.hdc, L"低头思故乡", -1, &rect, DT_RIGHT);
  29. EndPaint(hwnd, &ps);
  30. return 0;

这个不难理解吧,就是每一行文本的矩形区域得顶部和底部坐标分别加上文本的高度。

现在可以看看效果了。

接下来,我们画几条弧线。绘制弧线使用Arc函数,第一个参数是目标HDC,随后的4个参数用于确定弧线所在的位置的矩形,最后4个参数是确定弧线的开始点和结束点的坐标。

  1. BOOL  WINAPI Arc(
  2. HDC hdc, //DC的句柄
  3. int x1, // 矩形的左坐标
  4. int y1, //矩形上坐标
  5. int x2,//矩形的右坐标
  6. int y2, //矩形的下坐标
  7. int x3, //起点x坐标
  8. int y3, //起点y坐标
  9. int x4, //终点x坐标
  10. int y4 //终点y坐标
  11. );

在默认情况下,弧线是逆时针方向的。

  1. // 绘制弧线
  2. HPEN pen = CreatePen(PS_SOLID, 2, RGB(200, 100, 20));//创建笔
  3. // 将笔选到DC中
  4. auto oldObj = SelectObject(ps.hdc, pen);
  5. // 画弧线
  6. Arc(ps.hdc, 20, 100,  300, 300, 39, 110, 280, 285);
  7. Arc(ps.hdc, 200, 160, 390, 400, 300,350, 380,165);
  8. // 画完之后,把原先的笔选回去
  9. SelectObject(ps.hdc, oldObj);
  10. // 清理
  11. DeleteObject(pen);

上面代码将画出如下图所示的弧线。

默认是逆时针方向,现在我想画顺时针方向的弧线。看下面例子,通过SetArcDirection函数改变弧线的方向。

  1. /*
  2. AD_COUNTERCLOCKWISE表示逆时针方向
  3. AD_CLOCKWISE表示顺时针方向
  4. */
  5. SetArcDirection(ps.hdc, AD_CLOCKWISE);
  6. Arc(ps.hdc, 20,150, 460,450, 90,162, 85,300);

下面介绍一下LineDDA函数,这个家伙不简单,为啥?因为I它可以通过回调函数来对一条线段中不同的点进行分别处理。其回调函数如下:

  1. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);

最后一个参数是长指针,我们可以将一个HDC的地址传给它。

因为需要回调函数,我们得先写好回调函数,但是,在文件的前面要先声明一下,C语言的函数如果在调用之后定义,就必须先声明,不然编译的时候找不到。

  1. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData)
  2. {
  3. // 从参数中取得HDC
  4. HDC hdc = *((HDC*)lpData);
  5. // 不同位置的线段设置不同的颜色
  6. int type=0;
  7. if(x <= 510 || y <= 200)
  8. {
  9. type = 0;
  10. }
  11. else if((x > 510 && x <= 700) ||
  12. (y > 720 && y <= 360))
  13. {
  14. type = 1;
  15. }
  16. else
  17. {
  18. type = 2;
  19. }
  20. // 根据不同情况着色
  21. switch(type)
  22. {
  23. case 0:
  24. SetPixel(hdc,x,y,RGB(0,255,0));
  25. break;
  26. case 1:
  27. SetPixel(hdc,x,y,RGB(0,0,255));
  28. break;
  29. case 2:
  30. SetPixel(hdc,x,y,RGB(255,0,0));
  31. break;
  32. default:
  33. SetPixel(hdc,x,y,RGB(255,0,0));
  34. }
  35. }

接着在相应WM_PAINT消息的时候调用LineDDA函数。

  1. LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);

结果你会看到,画出来的线段是有三种颜色的。

完整的代码如下:

  1. #include <Windows.h>
  2. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  3. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);
  4. int WINAPI WinMain(HINSTANCE hTheApp,
  5. HINSTANCE hPrevApp,
  6. LPSTR cmdline,
  7. int nShow)
  8. {
  9. WNDCLASS wc = {   };
  10. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  11. wc.hInstance = hTheApp;
  12. wc.lpfnWndProc = WindowProc;
  13. wc.lpszClassName = L"MyApp";
  14. wc.style = CS_HREDRAW | CS_VREDRAW;
  15. RegisterClass(&wc);
  16. HWND hwnd = CreateWindow(L"MyApp",
  17. L"我的应用程序",
  18. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  19. 35,
  20. 28,
  21. 600,
  22. 500,
  23. NULL,
  24. NULL,
  25. hTheApp,
  26. NULL);
  27. if(hwnd == NULL)
  28. return -1;
  29. // 消息循环
  30. MSG msg;
  31. while(GetMessage(&msg, NULL, 0, 0))
  32. {
  33. TranslateMessage(&msg);
  34. DispatchMessage(&msg);
  35. }
  36. }
  37. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  38. {
  39. PAINTSTRUCT ps;
  40. switch(msg)
  41. {
  42. case WM_DESTROY:
  43. PostQuitMessage(0);
  44. return 0;
  45. case WM_PAINT:
  46. BeginPaint(hwnd, &ps);
  47. TEXTMETRIC tm;
  48. // 取得与文本相关的数据
  49. GetTextMetrics(ps.hdc, &tm);
  50. RECT rect;
  51. rect.top = 0L;
  52. rect.left = ps.rcPaint.left;
  53. rect.right = ps.rcPaint.right;
  54. rect.bottom = rect.top + tm.tmHeight;
  55. // 第一行文本
  56. SetTextColor(ps.hdc, RGB(0,150,255));
  57. DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);
  58. // 第二行文本
  59. rect.top += tm.tmHeight;
  60. rect.bottom += tm.tmHeight;
  61. SetTextColor(ps.hdc, RGB(220, 12, 50));
  62. DrawText(ps.hdc, L"疑是地上霜", -1, &rect, DT_LEFT);
  63. // 第三行文本
  64. rect.top += tm.tmHeight;
  65. rect.bottom += tm.tmHeight;
  66. SetTextColor(ps.hdc, RGB(30,255,7));
  67. DrawText(ps.hdc, L"举头望明月", -1, &rect, DT_RIGHT);
  68. // 第四行文本
  69. rect.top += tm.tmHeight;
  70. rect.bottom += tm.tmHeight;
  71. SetTextColor(ps.hdc, RGB(0,40,210));
  72. DrawText(ps.hdc, L"低头思故乡", -1, &rect, DT_RIGHT);
  73. // 绘制弧线
  74. HPEN pen = CreatePen(PS_SOLID, 3, RGB(200, 100, 20));//创建笔
  75. // 将笔选到DC中
  76. auto oldObj = SelectObject(ps.hdc, pen);
  77. // 画弧线
  78. /*Arc(ps.hdc, 20, 100,  300, 300, 39, 110, 280, 285);
  79. Arc(ps.hdc, 200, 160, 390, 400, 300,350, 380,165);*/
  80. /*
  81. AD_COUNTERCLOCKWISE表示逆时针方向
  82. AD_CLOCKWISE表示顺时针方向
  83. */
  84. SetArcDirection(ps.hdc, AD_CLOCKWISE);
  85. Arc(ps.hdc, 20,150, 300,450, 90,162, 85,300);
  86. // 画完之后,把原先的笔选回去
  87. SelectObject(ps.hdc, oldObj);
  88. // 清理
  89. DeleteObject(pen);
  90. // 分段线条
  91. LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);
  92. EndPaint(hwnd, &ps);
  93. return 0;
  94. }
  95. return DefWindowProc(hwnd, msg, wParam, lParam);
  96. }
  97. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData)
  98. {
  99. // 从参数中取得HDC
  100. HDC hdc = *((HDC*)lpData);
  101. // 不同位置的线段设置不同的颜色
  102. int type=0;
  103. if(x <= 510 || y <= 200)
  104. {
  105. type = 0;
  106. }
  107. else if((x > 510 && x <= 700) ||
  108. (y > 720 && y <= 360))
  109. {
  110. type = 1;
  111. }
  112. else
  113. {
  114. type = 2;
  115. }
  116. // 根据不同情况着色
  117. switch(type)
  118. {
  119. case 0:
  120. SetPixel(hdc,x,y,RGB(0,255,0));
  121. break;
  122. case 1:
  123. SetPixel(hdc,x,y,RGB(0,0,255));
  124. break;
  125. case 2:
  126. SetPixel(hdc,x,y,RGB(255,0,0));
  127. break;
  128. default:
  129. SetPixel(hdc,x,y,RGB(255,0,0));
  130. }
  131. }

跟我一起玩Win32开发(8):绘图(A)的更多相关文章

  1. 跟我一起玩Win32开发(转自CSDN-东邪独孤)

    跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩W ...

  2. 跟我一起玩Win32开发(17):启动和结束进程

    这里我再次说明一下,我不知道为什么,现在的人那么喜欢走极端,估计是价值观都“升级”了的缘故吧. 我撰写这一系列Win32相关的文章,并不是叫大家一定要用Win32去开发项目,仅仅是给大家了解一下,Wi ...

  3. 跟我一起玩Win32开发(18):使用对话框的两个技巧

    相信大家知道对话框怎么用了,就是先用“资源编辑器”设计一个对话框,然后在代码中加载处理.今天,我向大家分享两个使用对话框的技巧,还是比较实用的.不用担心,先喝杯茶,很简单的,一点也不复杂,总之,看俺写 ...

  4. 跟我一起玩Win32开发(9):绘图(B)

    我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣. 首先,我们写一个程序骨架子,以便做实验. #include <Windows.h> LRESULT CALLBACK MainWinPro ...

  5. 跟我一起玩Win32开发(10):绘图(C)

    今天我们来欣赏一下用于填充图形的函数,当然我不会逐个去介绍,因为我们参考MSDN直接套参数就可以了. SetDCBrushColor函数有必要扯一下,它的声明如下: COLORREF SetDCBru ...

  6. 跟我一起玩Win32开发(19):浏览和打开文件

    在应用程序中,我们很经常要实现的功能,是Open文件或保存文件对话框,让用户来选择一个或N个文件.本文我将介绍两种思路,第一种方法较为复杂,第二种方法较为简单. 方法一:老规矩 这是一种传统方法,使用 ...

  7. 跟我一起玩Win32开发(12):使用控件——单选按钮

    今天,咱们还是接着玩“控件斗地主”,这是我原创的超级游戏,有益身心健康,玩一朝,十年少. 哦,对,脑细胞极速运动了一下,想起了一个问题,这个破问题虽然网上有很多种解决方案,但是,并没有让所有人都解决问 ...

  8. 跟我一起玩Win32开发(23):渐变颜色填充

    GradientFill函数可以对特定的矩形区域或者三角形区域进行渐变颜色的填充.我们先来看看GradientFill函数到底长得什么样子,帅不帅. BOOL GradientFill( _In_   ...

  9. 跟我一起玩Win32开发(21):复制&粘贴&剪贴板操作

    我要提醒一下大家,看了我的博文学到的知识,千万不要用于实际开发,不然你会被你的上司骂:“妈的,这些东西哪来的,从来没有人这样做过.”不信你试试,脑细胞被冻结的经理或者技术总监们肯定会这样说的. 如果是 ...

随机推荐

  1. 解决:Android4.3锁屏界面Emergency calls only - China Unicom与EMERGENCY CALL语义反复

    从图片中我们能够看到,这里在语义上有一定的反复,当然这是谷歌的原始设计.这个问题在博客上进行共享从表面上来看着实没有什么太大的意义,只是因为Android4.3在锁屏功能上比起老版本号做了非常大的修改 ...

  2. 从.Net版本演变看String和StringBuilder性能之争

    在C#中string关键字的映射实际上指向.NET基类System.String.System.String是一个功能非常强大且用途非常广泛的基类,所以我们在用C#string的时候实际就是在用.NE ...

  3. [网页游戏开发]容器的使用及自定义Tab,RadioGroup,List,ViewStack

    Morn里面,容器和其他普通组件不同,无需皮肤,所以也不能从组件树种拖动创建(Tab,RadioGroup例外),只能转换而来 Morn的容器组件主要有Box,Container,Panel,Tab, ...

  4. WebGIS中地图恢复初始位置及状态

    我想实现这么一个效果:地图任意缩放后,点击一个按钮,将立刻回到地图初始加载时的位置,并且是没有缩放的状态. 怎么办呢?最好的办法就是用Home按钮. <!DOCTYPE HTML> < ...

  5. iOS 各种编译错误汇总

    1.error: macro names must be identifiers YourProject_prefix.pch 原因: 因为你弄脏了预处理器宏,在它处于<Multiple Val ...

  6. Intellij IDEA debug jar包

    打成jar包 mvn clean install -Dmaven.test.skip=true jar包保存在target目录下 启动jar Terminal控制台执行下面的命令,启动jar java ...

  7. 如何设计一个优秀的API

    如何设计一个优秀的API - 文章 - 伯乐在线 http://blog.jobbole.com/42317/ 如何设计一个优秀的API - 标点符 https://www.biaodianfu.co ...

  8. (linux)MMC 卡驱动分析

    最近花时间研究了一下 MMC 卡驱动程序,开始在网上找了很多关于 MMC 卡驱动的分析文章,但大都是在描述各个层,这对于初学者来讲帮助并不大,所以我就打算把自己的理解写下来,希望对大家有用.个人觉得理 ...

  9. Ubuntu下如何安装并使用Objective-C

    Objective-C是本人用过的最佳类C.面向对象的编程语言.Objective-C与标准C完美兼容,而在此基础上又加上了将面向对象的基础概念诠释得最好的SmallTalk元素,使得它既简洁.又灵活 ...

  10. Spring Boot2.0之 整合Redis集群

    项目目录结构: pom: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http:// ...