第4章 Windows的图形设备接口及Windows绘图.ppt
第4章 Windows的图形设备接口及Windows绘图,Windows图形设备接口(GDI)是为与设备无关的图形设计的。所谓设备的无关性,就是操作系统屏蔽了硬件设备的差异,因而设备无关性能使用户编程时无需考虑特殊的硬件设置。,一、图形设备接口(GDI),GDI负责系统与用户或绘图程序之间的信息交换,并控制在输出设备上显示图形或文字,是Windows系统的重要组成部分。,设备描述表即为设备环境的属性的集合。,应用程序与输出设备之间的桥梁,Win系统,外设,直接访问,统一的设备环境(DC),使应用程序与设备相连,1.GDI的一些基本概念,应用程序,设备描述表及其属性,通过设备描述表的句柄来间接地存取,应用程序每一次图形操作均参照设备描述表中的属性执行,图形刷新是绘图过程中必须考虑的重要问题,刷新请求对刷新请求的响应刷新方法,包括,(1)刷新请求,2.图形刷新,(2)系统对刷新请求的响应 当用户区的内容需要刷新时,系统向应用程序消息队列发送WM_PAINT消息,系统在应用程序的消息队列中加入该消息,以通知窗口函数执行刷新处理,三种刷新,窗口移动后的刷新被覆盖区域的刷新对象穿越后的刷新(系统自动完成),用户区移动或显示用户窗口大小改变程序通过滚动条滚动窗口,窗口被另一个窗口覆盖的恢复如下拉式菜单关闭等,光标穿过用户区图标拖过用户区,窗口被另一个窗口覆盖的区域称为无效区域。Windows系统为每个窗口建立了一个PAINTSTRUCT结构,该结构中包含了包围无效区域的一个最小矩形的结构RECT,应用程序可以根据这个无效矩形执行刷新操作。,Typedef struct tagPAINTSTRUCT HDC hdc;/设备环境句柄 BOOL fErase;/一般取真值,表示擦除无效矩形的背景 RECT rcPaint;/无效矩形标识 BOOL fRestore;/系统保留 BOOL fIncUpdate;/系统保留 BYTE rgbReserved16;/系统保留 PAINTSTRUCT;,常用的Windows应用程序刷新窗口的方法,保存副本。刷新时将副本拷贝到相应的窗口中,记录事件。刷新时重新执行这个曾经发生的事件,重新绘制。将图形绘制处理程序放在消息WM_PAINT响应模块中,刷新时重绘图形,(3)有效的刷新方法,获取设备环境是应用程序输出图形的先决条件,常用的两种方法是调用函数BeginPaint或GetDC,由BeginPaint函数获取的设备环境要用EndPaint函数释放void EndPaint(HWND hwnd,PAINTSTRUCT&ps),3.获取设备环境的方法,(2)调用GetDC函数 如果绘图工作并非由WM_PAINT消息驱动,则调用GetDC函数获取设备环境。hdc=GetDC(hwnd);,由GetDC函数获取的设备环境必须用ReleaseDC函数释放 void ReleaseDC(HWND hwnd);,映像模式定义了将逻辑单位转化为设备的度量单位以及设备的x方向和y方向,程序员可在一个统一的逻辑坐标系中操作而不必考虑输出设备的坐标系情况。,窗口:对应逻辑坐标系上程序员设定的区域视口:对应实际输出设备上程序员设定的区域,窗口和视口如何映射?,4.映像模式,坐标系统,逻辑坐标系统,设备坐标系统,应用程序可获取设备环境的当前映像模式,并根据需要设置映像模式。,相关函数,设置设备环境的映像模式SetMapMode(hdc,nMapMode);nMapMode为映像模式的整型标识符,获取当前设备环境的映像模式nMapMode=GetMapMode(hdc);,窗口区域的定义由SetWindowExtEx函数完成,其函数原型为:BOOL SetWindowExtEx(HDC hdc,int nHeight,nWidth,/以逻辑单位表示的窗口区域高宽度 LPSIZE lpSize,/函数调用前窗口区域尺寸的SIZE结构地址);,视口区域的定义由SetViewportExtEx函数完成,函数原型为:BOOL Set ViewportExtEx(HDC hdc,int nHeight,nWidth,/以物理设备单位表示的新视口区域高宽度LPSIZE lpSize);,只有在映射模式为MM_ANISOTROPIC和MM_ISOTROPIC时才有意义,视口的默认原点和窗口的默认原点均为(0,0)。可通过调用函数SetViewportOrgEx和SetWindowOrgEx设定窗口与视口的原点。SetWindowOrgEx函数的原型为:BOOL SetWindowOrgEx(HDC hdc,int X,Y,/以逻辑单位表示的窗口原点坐标LPPOINT lpPoint/函数调用前原点坐标的POINT结构的地址);,只有在映射模式为MM_ANISOTROPIC和MM_ISOTROPIC时才有意义,(1)画笔的创建 使用画笔之前必须事先定义一个画笔句柄。形式如下:HPEN hP;然后调用函数GetStockObject获取Windows系统定义的四种画笔。例如获取画笔BLACK_PEN的形式如下:hP=GetStockObject(BLACK_PEN);,WHITE_PENBLACK_PENDC_PENNULL_PEN,二、绘图工具与颜色1.画笔,(2)创建新画笔,形式如下:hP=CreatePen(int nPenStyle,/确定画笔样式int nWidth,/画笔宽度COLORREF rgbColor/画笔颜色);,创建画笔后,必须调用SelectObject函数将其选入设备环境。SelectObject(hdc,hP);/hP为所创建或获取的画笔句柄,不再使用当前画笔时,需删除画笔,以免占内存 DeleteObject(hP);,PS_DASH:虚线PS_DASHDOT:点划线PS_DASHDOTDOT:双点划线PS_DOT:点线PS_INSIDEFRAME:实线PS_NULL:无PS_SOLID:实线,画刷的创建与应用与画笔很相似,操作画刷也包括创建、选入设备环境和删除。(1)画刷的创建 使用画刷需事先定义一个画刷句柄。形式如下:HBRUSH hBr;/hBr为画刷句柄 然后调用函数GetStockObject获取Windows系统提供的7种画刷 hBr=(HBRUSH)GetStockObject(nBrushStyle)画刷样式,BLACK_BRUSH黑色画刷DKGRAY_BRUSH深灰色画刷GRAY_BRUSH灰色画刷HOLLOW_BRUSH虚画刷LTGRAY_BRUSH亮灰色画刷NULL_BRUSH空画刷WHITE_BRUSH白色画刷,指定颜色画刷如何获得?,2.画刷,可调用函数CreateSolidBrush和CreateHatchBrush创建画刷,hBr=CreateSolidBrush(rgbColor);,创建具有指定颜色的单色画刷,创建指定阴影图案和颜色的画刷,(2)选入设备环境 创建画刷后,通过SelectObject(hdc,hBr);将其选入设备环境(3)删除画刷 不使用画刷时,可用DeleteObject(hBr);删除画刷,释放内存,Windows使用宏RGB定义绘图的颜色,其形式为:RGB(nRed,nGreen,nBlue),红色值,蓝色值,绿色值,RGB(255,0,0),RGB(0,255,0),RGB(0,0,255),3.颜色,(1)设置画笔当前位置的函数MoveToEx,BOOL MoveToEx(HDC hdc,int X,Y,/X、Y分别为新位置的逻辑坐标 LPPOINT lpPoint/存放原画笔位置的POINT结构地址),(2)从当前位置向指定坐标点画直线的函数LineToEx,BOOL LineToEx(HDC hdc,int X,int Y)/X和Y为线段的终点坐标,(3)从当前位置开始,依次用线段连接lpPoints中指定的各点BOOL Polyline(HDC hdc,LPPOINT lpPoints,/指向包含各点坐标的POINT结构数组的指针 int nCount/nCount为POINT数组中点的个数),三、常用绘图函数,(4)绘制椭圆弧线的函数ArcBOOL Arc(HDC hdc,int X1,intY1,/边框矩形左上角的逻辑坐标int X2,int Y2,/边框矩形右下角的逻辑坐标int X3,int Y3,/椭圆弧起始点坐标int X4,int Y4/椭圆弧终止点坐标),(5)绘制饼图,并用当前画刷进行填充BOOL Pie(HDC hdc,int X1,intY1,/边框矩形左上角的逻辑坐标int X2,int Y2,/边框矩形右下角的逻辑坐标int X3,int Y3,/椭圆弧起始经线的确定点坐标int X4,int Y4/椭圆弧终止经线的确定点坐标),(6)绘制矩形,并用当前画刷进行填充BOOL Rectangle(HDC hdc,int X1,int Y1,int X2,int Y2),(X1,Y1)和(X2,Y2)分别为矩形的左上角和右下角的逻辑坐标,(7)绘制圆角矩形,并用当前画刷填充BOOL RoundRect(HDC hdc,int X1,int Y1,int X2,int Y2,int nHeight,int nWidth),圆角的高度和宽度,(8)绘制椭圆,并用当前画刷填充 BOOL Ellipse(HDC hdc,intX1,intY1,intX2,intY2),(9)绘制多边形,并用当前画刷填充BOOL Polygon(HDC hdc,LPPOINT lpPoints,int nCount),包含各点坐标的POINT数组的地址,多边形点的个数,【例4-1】利用绘图函数创建填充区。共有三个填充图形,第一个是用深灰色画刷填充带圆角的矩形,第二个是采用亮灰色画刷填充一个椭圆形图,第三个是用虚画刷填充饼形图。,#include#include#includelong WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam);,使用虚画刷填充时,看不出填充效果!,四、应用实例,BOOL InitWindowsClass(HINSTANCE hInstance);BOOL InitWindows(HINSTANCE hInstance,int nCmdShow);HWND hWndMain,int WINAPI WinMain/主函数(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)MSG Message;if(!InitWindowsClass(hInstance)return FALSE;if(!InitWindows(hInstance,nCmdShow)return FALSE;while(GetMessage(,long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)HDC hDC;/定义指向设备的句柄 HBRUSH hBrush;/定义指向画刷的句柄 HPEN hPen;/定义指向画笔的句柄 PAINTSTRUCT PtStr;/定义指向包含绘图信息的结构体变量,消息处理函数,switch(iMessage)/处理消息case WM_PAINT:/处理绘图消息 hDC=BeginPaint(hWnd,/采用亮灰色画刷,下面只改变画刷,不改变笔,SelectObject(hDC,hBrush);/选择画刷 Ellipse(hDC,150,50,200,150);/绘制椭圆 hBrush=(HBRUSH)GetStockObject(HOLLOW_BRUSH);/虚画刷 SelectObject(hDC,hBrush);/选择画刷 Pie(hDC,250,50,300,100,250,50,300,50);/绘制饼形 EndPaint(hWnd,BOOL InitWindows(HINSTANCE hInstance,int nCmdShow)/初始化窗口HWND hWnd;hWnd=CreateWindow(WinFill,/生成窗口 填充示例程序,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hInstance,NULL);if(!hWnd)return FALSE;hWndMain=hWnd;ShowWindow(hWnd,nCmdShow);/显示窗口 UpdateWindow(hWnd);return TRUE;,BOOL InitWindowsClass(HINSTANCE hInstance)/定义窗口类 WNDCLASS WndClass;WndClass.cbClsExtra=0;WndClass.cbWndExtra=0;WndClass.hbrBackground=(HBRUSH)(GetStockObject(WHITE_BRUSH);WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);WndClass.hIcon=LoadIcon(NULL,END);WndClass.hInstance=hInstance;WndClass.lpfnWndProc=WndProc;WndClass.lpszClassName=WinFill;WndClass.lpszMenuName=NULL;WndClass.style=CS_HREDRAW|CS_VREDRAW;return RegisterClass(,例:应用画笔和画刷等工具绘制如图所示的图形。,1#include2#include3#include4 long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam);5 BOOL InitWindowsClass(HINSTANCE hInstance);6 BOOL InitWindows(HINSTANCE hInstance,int nCmdShow);7 HWND hWndMain;/主函数8 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)9 MSG Message;10if(!InitWindowsClass(hInstance)return FALSE;11if(!InitWindows(hInstance,nCmdShow)return FALSE;12while(GetMessage(,long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)1 HDC hDC;/定义设备环境句柄2 HBRUSH hBrush;/定义画刷的句柄3 HPEN hPen;/定义画笔的句柄4 PAINTSTRUCT PtStr;/定义指向包含绘图信息的结构体变量/定义一个POINT数组,包括6个点 5 POINT points6=100,212,70,227,70,250,130,250,130,227,100,212;6 switch(iMessage)/处理消息 7 case WM_PAINT:/处理绘图消息 8 hDC=BeginPaint(hWnd,1 Polyline(hDC,points,6);/画一个五边形2Arc(hDC,63,137,138,212,100,137,100,137);/画一个圆3Pie(hDC,213,137,288,212,240,137,260,137);/画一个圆饼4Rectangle(hDC,213,212,287,250);/画一个长方形5RoundRect(hDC,213,100,287,137,20,20);/画一个圆角长方形6DeleteObject(hPen);/删除画笔7DeleteObject(hBrush);/删除画刷8EndPaint(hWnd,BOOL InitWindows(HINSTANCE hInstance,int nCmdShow)/初始化窗口HWND hWnd;hWnd=CreateWindow(WinFill,/生成窗口填充示例程序,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL,NULL,hInstance,NULL);if(!hWnd)return FALSE;hWndMain=hWnd;ShowWindow(hWnd,nCmdShow);/显示窗口UpdateWindow(hWnd);return TRUE;,BOOL InitWindowsClass(HINSTANCE hInstance)/定义窗口类WNDCLASS WndClass;WndClass.cbClsExtra=0;WndClass.cbWndExtra=0;WndClass.hbrBackground=(HBRUSH)(GetStockObject(WHITE_BRUSH);WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);WndClass.hIcon=LoadIcon(NULL,END);WndClass.hInstance=hInstance;WndClass.lpfnWndProc=WndProc;WndClass.lpszClassName=WinFill;WndClass.lpszMenuName=NULL;WndClass.style=CS_HREDRAW|CS_VREDRAW;return RegisterClass(,【例4-2】编写一个程序,在屏幕上出现一个圆心沿正弦曲线轨迹移动的实心圆,而且,每隔四分之一周期,圆的填充色和圆的周边颜色都发生变化,同时,圆的半径在四分之一周期之内由正弦曲线幅值的0.2倍至0.6倍线性增长。,(1)正弦曲线是此题的基础。在WndMain()函数消息循环前,生成正弦曲线各点的坐标。把正弦曲线一个周期的横坐标分成100个等分点,存储在数组lpSin100中,100个点的坐标计算如下:,for(int j=0;j100;j+)/生成正弦曲线的点坐标 lpSinj.x=(long)(j*2*Pi/100*60);lpSinj.y=(long)(dfRange*sin(j*2*Pi/100);,双击此处运行程序,(2)动态显示圆在正弦曲线上移动,数组lpSin100的长度为100,设定圆在正弦曲线移动时共有100个位置,数组中每一个值是圆移动时圆心的坐标,每四分之一周期有25个位置,i=25处于第1个1/4周期,创建红色画笔和画刷;25i50处于第2个1/4周期,创建绿色画笔和画刷;50i75处于第3个1/4周期,创建蓝色画笔和画刷;75i100处于第4个1/4周期,创建黄色画笔和画刷;,if(i=25)/第一个1/4周期 hPen=CreatePen(PS_DASH,1,RGB(255,0,0);hBrush=CreateHatchBrush(HS_BDIAGONAL,RGB(255,0,0);lRadious=(long)(dfRange*0.2+i%25*dfRange*0.4/25);/计算半径,在消息WM_PAINT处理程序中,调用函数BeginPaint()获得设备环境句柄。由此经过线性差分计算圆半径的大小lRadious,第1个1/4周期的程序代码如下:,创建的画笔和画刷选入设备环境后,调用函数Ellipse()绘制圆形。下面这段代码是动态显示的关键:Sleep(100);/停0.1秒if(i100)InvalidateRect(hWnd,NULL,1);/刷新用户区 调用Sleep(100)函数使程序暂停0.1秒。所含参数100代表暂停的时间,使用毫秒作单位。,i100时调用函数刷新用户区发送WM_PAINT消息,消息发到的窗口的句柄,代表刷新整个用户区,代表清除用户区中所有的显示内容,#include#include#include#include#define Pi 3.1415926long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam);double dfTheta=0,dfRange=100.0;/正弦曲线的角度变量long i=0,j=0;long lCentreX=0,lCentreY=0,lRadious=(long)(0.2*dfRange);/定义圆心坐标和圆半径POINT lpSin100;/定义正弦曲线的点坐标int WINAPI WinMain(,.)/填写窗口类属性if(!RegisterClass(,hWnd=CreateWindow(SIN,/窗口类名4_6,/标题名WS_OVERLAPPEDWINDOW,/带标题栏,最大/小按钮的窗口CW_USEDEFAULT,/窗口左上角坐标0,CW_USEDEFAULT,/采用默认的宽度和高度0,NULL,/无父窗口NULL,/无主菜单hInstance,/当前实例句柄NULL);.ShowWindow(hWnd,nCmdShow);/显示窗口UpdateWindow(hWnd);/更新并绘制用户区,1.for(int j=0;j100;j+)/生成正弦曲线的点坐标2.3.lpSinj.x=(long)(j*2*Pi/100*60);4.lpSinj.y=(long)(dfRange*sin(j*2*Pi/100);5.6.while(GetMessage(,long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)1.HDC hDC;/定义设备环境句柄2.HBRUSH hBrush;/定义画刷句柄3.HPEN hPen;/定义画笔句柄4.PAINTSTRUCT PtStr;/定义包含绘图信息的结构体变量5.switch(iMessage)6.case WM_PAINT:/处理绘图消息7.hDC=BeginPaint(hWnd,/绘制正弦曲线,if(i=25)/第一个1/4周期hPen=CreatePen(PS_DASH,1,RGB(255,0,0);hBrush=CreateHatchBrush(HS_BDIAGONAL,RGB(255,0,0);lRadious=(long)(dfRange*0.2+i%25*dfRange*0.4/25);/计算半径,else if(i=50)/第二个1/4周期hPen=CreatePen(PS_DASH,1,RGB(0,255,0);hBrush=CreateHatchBrush(HS_DIAGCROSS,RGB(0,255,0);lRadious=(long)(dfRange*0.2+i%25*dfRange*0.4/25);,else if(i=75)/第三个周期hPen=CreatePen(PS_DASH,1,RGB(0,0,255);hBrush=CreateHatchBrush(HS_CROSS,RGB(0,0,255);lRadious=(long)(dfRange*0.2+i%25*dfRange*0.4/25);,else/第四个周期hPen=CreatePen(PS_DASH,1,RGB(255,255,0);hBrush=CreateHatchBrush(HS_VERTICAL,RGB(255,255,0);lRadious=(long)(dfRange*0.2+i%25*dfRange*0.4/25);,1.SelectObject(hDC,hBrush);/选入画刷2.SelectObject(hDC,hPen);/选入画笔3.lCentreX=lpSini.x;/圆心x坐标4.lCentreY=lpSini.y;/圆心y坐标5.Ellipse(hDC,lCentreX-lRadious,lCentreY-lRadious,lCentreX+lRadious,lCentreY+lRadious);/画圆6.i+;7.DeleteObject(hPen);/删除画笔8.DeleteObject(hBrush);/删除画刷9.EndPaint(hWnd,【例4-3】绘图与刷新。制订一种重新绘制图形的刷新方式,将图形绘制模块放在消息WM_PAINT的处理过程中,当窗口需要刷新时,通知窗口函数重新绘制图形以完成刷新工作。本例要求先使用画笔和画刷绘制一个矩形,然后使用红色网格绘制一个椭圆,再使用绿色点划线绘制椭圆的轴线。,#include#include#include#include LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow)HWND hwnd;MSG Msg;WNDCLASS wndclass;char lpszClassName=基本绘图;char lpszTitle=My_Drawing;wndclass.style=0;/填写属性 wndclass.lpszClassName=lpszClassName;,if(!RegisterClass(,hwnd=CreateWindow(,);ShowWindow(hwnd,nCmdShow);UpdateWindow(hwnd);while(GetMessage(,LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)HDC hdc;PAINTSTRUCT ps;HPEN hP;/定义画笔句柄 HBRUSH hB;/定义画刷句柄 switch(message)case WM_PAINT:/通过响应WM_PAINT消息完成绘图工作 hP=CreatePen(PS_DASHDOT,1,RGB(0,255,0);/自定义绿笔/所画线条为点划线,宽度为1 hB=CreateHatchBrush(HS_CROSS,RGB(255,0,0);/红色网状 hdc=BeginPaint(hwnd,/绘制椭圆,并填充,SelectObject(hdc,hP);/更新画笔,选“自定义绿笔”MoveToEx(hdc,100,130,NULL);/使用当前画笔绘制轴线 LineTo(hdc,300,130);MoveToEx(hdc,200,30,NULL);LineTo(hdc,200,230);EndPaint(hwnd,【例4-4】设置映像与使用映像模式实例。本例中的程序运行时,初始阶段按模式MM_TEXT绘图,图形为一个坐标系,以逻辑坐标系的原点为原点,X、Y轴分别是逻辑坐标系的X、Y轴。当用户按下A键、B键或C键时,产生WM_CHAR消息,将映像模式分别设置为ISOTROPIC、ANISOTROPIC或LOMETRIC,同时调用InvalidateRect函数刷新用户区。,按A键,按B键,按C键,1.#include 2.#include 3.#include 4.#include 5.LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);6.int nMode=MM_TEXT;/设置映像模式的初始值7.8.LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)9.HDC hdc;10.PAINTSTRUCT ps;11.HPEN hPen;12.switch(message)case WM_CHAR:/按下不同的键时,设置不同的映像模式if(wParam=a|wParam=A)nMode=MM_ISOTROPIC;else if(wParam=b|wParam=B)nMode=MM_ANISOTROPIC;else if(wParam=c|wParam=C)nMode=MM_LOMETRIC;else;InvalidateRect(hwnd,NULL,1);/刷新用户区break;,1.case WM_PAINT:2.hdc=BeginPaint(hwnd,