zoukankan      html  css  js  c++  java
  • 跟我一起玩Win32开发(8):绘图(A)

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

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

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

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

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

    [cpp] view plain copy
     
    1. PAINTSTRUCT ps;  
    2. switch(msg)  
    3. {  
    4. case WM_PAINT:  
    5.         BeginPaint(hwnd, &ps);  


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

    [cpp] view plain copy
     
    1. DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);  

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

    [cpp] view plain copy
     
    1. SetTextColor(ps.hdc, RGB(0,150,255));  

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

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

    [cpp] view plain copy
     
    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个参数是确定弧线的开始点和结束点的坐标。

    [cpp] view plain copy
     
    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. );  

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

    [cpp] view plain copy
     
    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函数改变弧线的方向。

    [cpp] view plain copy
     
    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它可以通过回调函数来对一条线段中不同的点进行分别处理。其回调函数如下:

    [cpp] view plain copy
     
    1. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);  

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

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

    [cpp] view plain copy
     
    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.     // 根据不同情况着色  
    22.     switch(type)  
    23.     {  
    24.     case 0:  
    25.         SetPixel(hdc,x,y,RGB(0,255,0));  
    26.         break;  
    27.     case 1:  
    28.         SetPixel(hdc,x,y,RGB(0,0,255));  
    29.         break;  
    30.     case 2:  
    31.         SetPixel(hdc,x,y,RGB(255,0,0));  
    32.         break;  
    33.     default:  
    34.         SetPixel(hdc,x,y,RGB(255,0,0));  
    35.     }  
    36. }  


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

    [cpp] view plain copy
     
    1. LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);  

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

    完整的代码如下:

    [cpp] view plain copy
     
    1. #include <Windows.h>  
    2. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);  
    3.   
    4. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData);  
    5.   
    6. int WINAPI WinMain(HINSTANCE hTheApp,  
    7.                     HINSTANCE hPrevApp,  
    8.                     LPSTR cmdline,  
    9.                     int nShow)  
    10. {  
    11.     WNDCLASS wc = {   };  
    12.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);  
    13.     wc.hInstance = hTheApp;  
    14.     wc.lpfnWndProc = WindowProc;  
    15.     wc.lpszClassName = L"MyApp";  
    16.     wc.style = CS_HREDRAW | CS_VREDRAW;  
    17.       
    18.     RegisterClass(&wc);  
    19.     HWND hwnd = CreateWindow(L"MyApp",  
    20.         L"我的应用程序",  
    21.         WS_OVERLAPPEDWINDOW | WS_VISIBLE,  
    22.         35,  
    23.         28,  
    24.         600,  
    25.         500,  
    26.         NULL,  
    27.         NULL,  
    28.         hTheApp,  
    29.         NULL);  
    30.     if(hwnd == NULL)  
    31.         return -1;  
    32.     // 消息循环  
    33.     MSG msg;  
    34.     while(GetMessage(&msg, NULL, 0, 0))  
    35.     {  
    36.         TranslateMessage(&msg);  
    37.         DispatchMessage(&msg);  
    38.     }  
    39. }  
    40.   
    41. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)  
    42. {  
    43.     PAINTSTRUCT ps;  
    44.     switch(msg)  
    45.     {  
    46.     case WM_DESTROY:  
    47.         PostQuitMessage(0);  
    48.         return 0;  
    49.     case WM_PAINT:  
    50.         BeginPaint(hwnd, &ps);  
    51.         TEXTMETRIC tm;  
    52.         // 取得与文本相关的数据  
    53.         GetTextMetrics(ps.hdc, &tm);  
    54.         RECT rect;  
    55.         rect.top = 0L;  
    56.         rect.left = ps.rcPaint.left;  
    57.         rect.right = ps.rcPaint.right;  
    58.         rect.bottom = rect.top + tm.tmHeight;  
    59.         // 第一行文本  
    60.         SetTextColor(ps.hdc, RGB(0,150,255));  
    61.         DrawText(ps.hdc,L"床前明月光", -1, &rect, DT_CENTER);  
    62.         // 第二行文本  
    63.         rect.top += tm.tmHeight;  
    64.         rect.bottom += tm.tmHeight;  
    65.         SetTextColor(ps.hdc, RGB(220, 12, 50));  
    66.         DrawText(ps.hdc, L"疑是地上霜", -1, &rect, DT_LEFT);  
    67.         // 第三行文本  
    68.         rect.top += tm.tmHeight;  
    69.         rect.bottom += tm.tmHeight;  
    70.         SetTextColor(ps.hdc, RGB(30,255,7));  
    71.         DrawText(ps.hdc, L"举头望明月", -1, &rect, DT_RIGHT);  
    72.         // 第四行文本  
    73.         rect.top += tm.tmHeight;  
    74.         rect.bottom += tm.tmHeight;  
    75.         SetTextColor(ps.hdc, RGB(0,40,210));  
    76.         DrawText(ps.hdc, L"低头思故乡", -1, &rect, DT_RIGHT);  
    77.         // 绘制弧线  
    78.         HPEN pen = CreatePen(PS_SOLID, 3, RGB(200, 100, 20));//创建笔  
    79.         // 将笔选到DC中  
    80.         auto oldObj = SelectObject(ps.hdc, pen);  
    81.         // 画弧线  
    82.         /*Arc(ps.hdc, 20, 100,  300, 300, 39, 110, 280, 285); 
    83.         Arc(ps.hdc, 200, 160, 390, 400, 300,350, 380,165);*/  
    84.   
    85.         /* 
    86.         AD_COUNTERCLOCKWISE表示逆时针方向 
    87.         AD_CLOCKWISE表示顺时针方向 
    88.         */  
    89.         SetArcDirection(ps.hdc, AD_CLOCKWISE);  
    90.         Arc(ps.hdc, 20,150, 300,450, 90,162, 85,300);  
    91.         // 画完之后,把原先的笔选回去  
    92.         SelectObject(ps.hdc, oldObj);  
    93.         // 清理  
    94.         DeleteObject(pen);  
    95.         // 分段线条  
    96.         LineDDA(420,130,800,470,LineDDAProc, (LPARAM)&ps.hdc);  
    97.         EndPaint(hwnd, &ps);  
    98.         return 0;  
    99.     }  
    100.     return DefWindowProc(hwnd, msg, wParam, lParam);  
    101. }  
    102.   
    103. VOID CALLBACK LineDDAProc(int x, int y, LPARAM lpData)  
    104. {  
    105.     // 从参数中取得HDC  
    106.     HDC hdc = *((HDC*)lpData);  
    107.     // 不同位置的线段设置不同的颜色  
    108.     int type=0;  
    109.     if(x <= 510 || y <= 200)  
    110.     {  
    111.         type = 0;  
    112.     }  
    113.     else if((x > 510 && x <= 700) ||  
    114.         (y > 720 && y <= 360))  
    115.     {  
    116.         type = 1;  
    117.     }  
    118.     else  
    119.     {  
    120.         type = 2;  
    121.     }  
    122.   
    123.     // 根据不同情况着色  
    124.     switch(type)  
    125.     {  
    126.     case 0:  
    127.         SetPixel(hdc,x,y,RGB(0,255,0));  
    128.         break;  
    129.     case 1:  
    130.         SetPixel(hdc,x,y,RGB(0,0,255));  
    131.         break;  
    132.     case 2:  
    133.         SetPixel(hdc,x,y,RGB(255,0,0));  
    134.         break;  
    135.     default:  
    136.         SetPixel(hdc,x,y,RGB(255,0,0));  
    137.     }  
    138. }  
  • 相关阅读:
    驰骋工作流引擎-系统变量的引用
    驰骋工作流引擎-表单样本展示
    驰骋工作流引擎CCFLOW下载代码
    初识CSS
    初识HTML标签
    初识JDBC
    通过锁对象解决哲学家就餐问题
    MySQL基本用法
    LRU算法实现,HashMap与LinkedHashMap源码的部分总结
    Java简易实现记事本的打开与保存
  • 原文地址:https://www.cnblogs.com/weekbo/p/8681904.html
Copyright © 2011-2022 走看看