MFC GDI绘图基础

一.关于GDI的基本概念程序员

什么是GDI算法

Windows绘图的实质就是利用Windows提供的图形设备接口GDI(Graphics Device Interface)将图形绘制在显示器上。编程

在Windows操做系统中,动态连接库C:/WINDOWS/system32/gdi32.dll(GDI Client DLL)中定义了GDI函数,实现与设备无关的包括屏幕上输出像素、在打印机上输出硬拷贝以及绘制Windows用户界面功能。在Visual C++6.0中的头文件C:/Program Files/Microsoft Visual Studio/VC98/Include/wingdi.h和Visual Studio 2005中的头文件C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include/WinGDI.h是访问gdi32.dll库文件的钥匙。下面咱们大体浏览一下wingdi.h(included in Windows.h)头文件:数组

/* Bitmap Header Definition */定义了BITMAP位图结构数据结构

/* Mapping Modes */定义了DC中的坐标映射方式,包括如下经常使用函数:app

SetMapMode、SetViewportExtEx、SetViewportOrgEx、 SetWindowExtEx 、SetWindowOrgEx。函数

/* Stock Logical Objects */系统预约义的堆(STOCK)对象,包括BRUSH、PEN和FONT对象工具

/* Brush Styles */定义了画刷格式,包括SOLID、HOLLOW、HATCHED等格式字体

/* Hatch Styles */定义了画刷阴影格式,包括:this

HS_VERTICAL    /* ||||| */

HS_FDIAGONAL  /* ///// */

HS_BDIAGONAL  /* ///// */

HS_CROSS       /* +++++ */

HS_DIAGCROSS  /* xxxxx */

/* Pen Styles */定义了画笔格式,包括SOLID、DASH、DOT等格式

什么是DC?

设备环境DC(Device Context),也称为设备描述表或设备上下文。

设备环境保存了绘图操做中一些共同须要设置的信息,如当前的画笔、画刷、字体和位图等图形对象及属性,以及坐标映射、颜色和背景等影响图形输出的绘图模式。形象的说,一个设备环境提供了一张画布和一些绘画的工具,咱们可使用不一样格式、颜色的绘画工具在上面涂鸦。这里,设备环境中的“设备”是指任何类型的显示器或打印机等输出设备,绘图时,咱们没必要关心所使用设备的编程的原理和方法,全部的绘制操做必须经过设备环境进行间接的处理,Windows会自动将设备环境所描述的结构映射到相应的物理设备上。

从根本上来讲,DC它是Windows内部使用的数据结构,它存储着向设备输出时说须要的信息,应用程序利用它定义图形对象及其属性,并实现应用程序、设备驱动程序和输出设备之间绘图命令的转换。要想调用GDI函数向某个区域输出文字或绘制图形,必须先取得或创建设备环境句柄,应用程序每一次绘图操做均按照设备环境中的设置的绘图属性进行。

设备环境不像其余Windows结构,在程序中不能直接存取设备环境结构,只能经过系统提供的一系列函数或使用设备环境的句柄HDC来间接地获取或设置设备环境结构中的各项属性,这些属性包括显示器高度和宽度、支持的颜色数和分辨率等。

MFC中与GDI有关的类

为了支持GDI绘图,MFC提供了两种重要的类:设备环境DC(Device Context)类,用于设置绘图属性和绘制图形;绘图对象类,封装了各类GDI绘图对象,包括画笔、刷子、字体、位图、调色板和区域。

在MFC中,CDC是设备环境类的基类,除了通常的窗口显示外,还用于基于桌面的全屏幕绘制和非屏幕显示的打印机输出。CDC类封装了全部图形输出函数,包括矢量、光栅和文本输出。CDC的派生类包括CClientDC、CPaintDC、WindowDC、CMetaFileDC。

 

 

(1)CPaintDC类是一个来自CDC的设备环境类。它在构造期间执行CWnd::BeginPaint,在析构期间执行CWnd::EndPaint,EndPaint()除了释放设备环境外,还负责从消息队列中清除WM_PAINT消息。一个CPaintDC对象只在响应一个窗口重绘消息(WM_PAINT)的时候被使用,一般是在你的OnPaint消息处理成员函数中。所以,在处理窗口重画时,必须使用CPaintDC,不然WM_PAINT消息没法从消息队列中清除,将引发不断的窗口重画。

CPaintDC类成员:

数据成员

m_ps:包含了用于画客户区的PAINTSTRUCT 

m_hWnd: CPaintDC对象所附着的HWND 

构造函数CPaintDC:构造一个链接到指定的CWnd上的CPaintDC对象

(2)CClientDC(窗口客户区设备环境)类用于管理窗口用户区对应的显示上下文,它在构造时调用了Windows函数GetDC,在析构时调用了ReleaseDC。这意味着和CClientDC对象相关的设备上下文是窗口的客户区。通常在响应非窗口重画消息(如键盘输入时绘制文本、鼠标绘图)绘图时要用到它。

CClientDC类的成员:

构造函数CClientDC:构造一个链接到CWnd上的CClientDC对象数据成员

数据成员m_hWnd:所在的有效窗口的HWND

(3)CWindowDC(窗口设备环境)类用于管理与整个窗口对应的显示上下文,包括它的结构和控件。它在构造的时候调用Windows函数GetWindowDC,在销毁的时候调用ReleaseDC。这意味着CWindowDC对象能够访问CWnd的所有屏幕区域(包括客户区和非客户区)。它用于窗口(包括窗口边框、标题栏、控制按钮等)的绘制,除非要本身绘制窗口边框和按钮(如一些CD播放程序等),不然通常不用它。

 CWindowDC类成员:

 Construction CWindowDC:构造一个CWindowDC对象

 Data Members m_hWnd:与这个CWindowDC相关联的HWND句柄

(4)CMetaFileDC专门用于图元文件的绘制。图元文件记录一组GDI命令,能够经过这一组GDI命令重建图形输出。使用CMetaFileDC时,全部的图形输出命令会自动记录到一个与CMetaFileDC相关的图元文件中。

(5)此外咱们还能够利用Windows内存DC进行绘图,此时涉及到屏幕DC和内存DC。把所要绘制的一切先在内存DC中进行绘制,以后所有搬到屏幕DC中,从而把全部繁琐的绘制过程都在内存DC中完成了,咱们在屏幕上看到的是一幅完整的图画,因此不可能出现闪烁的状况。

 

二.MFC中GDI绘图

    GDI绘图包括如下步骤:获取设备环境,设置坐标映射,建立绘图工具,调用DC绘图函数绘图。

 

1、获取设备环境

(1)在SDK编程中,获取设备环境的方法有两种:

<1>经过API函数BeginPaint。应用程序响应WM_PAINT消息进行图形刷新时主要经过BeginPaint函数获取设备环境,在消息处理函数返回前调用API函数EndPaint释放设备环境。

函数原型为:WINUSERAPI HDC WINAPI BeginPaint( HWND hWnd,LPPAINTSTRUCT lpPaint);

    //如下为Win API示例::BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);

     case WM_PAINT://窗口客户区须要重绘

     {

         char szText[]="Hello World";

         PAINTSTRUCT ps;

         HDC hdc=::BeginPaint(hWnd,&ps);

         ::TextOut(hdc,10,10,szText,strlen(szText));

         ::EndPaint(hWnd,&ps);

         return 0;

}

MFC对BeginPaint进行了封装:

CWnd::BeginPaint,CDC* BeginPaint( LPPAINTSTRUCT lpPaint ); 等价于::BeginPaint(CWnd::m_hWnd, LPPAINTSTRUCT lpPaint);

<2>经过API函数GetDC。在非WM_PAINT消息处理函数中,须要调用GetDC来获取设备环境,调用API函数ReleaseDC来释放设备环境。

函数原型为:WINUSERAPI HDC WINAPI GetDC( HWND hWnd);

(2)在MFC中,MFC提供了不一样类型的DC类,每个类都封装了DC句柄,而且它们的构造函数自动调用获取DC的API函数,析构函数自动调用释放DC的API函数。所以,在程序中经过声明一个MFC设备环境类的对象就自动获取了一个DC,而当该对象被销毁时就自动释放了获取的DC。MFC AppWizard应用程序向导建立的OnDraw()函数自动支持所获取的DC。 

<1> CPaintDC构造函数:CPaintDC(CWnd* pWnd); 构造一个CPaintDC对象(pWnd指向一个CPaintDC对象所属的CWnd对象),准备用于绘画的应用程序窗口。

// BeginPaint

void CView::OnPaint()

{

         CPaintDC dc(this); // device context for painting

         // TODO: Add your message handler code here

         OnPrepareDC(&dc);

         OnDraw(&dc)

}

当咱们改变了窗口尺寸、移动窗口或恢复了先前被覆盖的部分,应用程序窗口就会收到一个Windows系统发送来的WM_PAINT消息,而后调用基类Cview的OnPaint函数或咱们本身添加的消息处理函数OnPaint。咱们能够在OnPaint函数中重绘窗口中从新可见的部分(),但简单的处理办法是重绘整个窗口。上面的代码中,因为基类Cview的OnPaint函数调用了OnDraw函数,所以应用程序常常在OnDraw函数中绘制视图。

<2>CClientDC构造函数:CClientDC(CWnd* pWnd); 构造一个CClientDC对象,它将存取pWnd指向的CWnd的客户区。

// 鼠标左键事件处理

void CExView::OnLButtonDown(UINT nFlags, CPoint point)

{

          // TODO: 在此添加消息处理程序代码和/或调用默认值

          CClientDC dc(this);//定义客户区设备环境

          dc.LineTo(point);//绘制线段

}

CClientDC表明了窗口客户区对应的显示上下文,它在构造时调用了API函数GetDC,并将当前窗口的句柄m_hWnd做为函数参数;在析构时调用了API函数ReleaseDC。当在客户去绘图时,须要利用CClientDC类定义一个客户区设备环境句柄。

有时候须要访问与一个客户设备环境相关联的窗口对象,能够经过Attach函数把这个CClientDC的成员m_hWnd句柄传递给一个窗口对象,该窗口就是与客户区设备环境相关联的窗口。

CWnd::Attach,BOOL Attach( HWND hWndNew );

说明:将一个Windows窗口与CWnd对象相链接。
返回值:若是成功,则返回非零值;不然返回0。
参数:hWndNew指定了Windows窗口的句柄

<3>CWindowDC构造函数:CWindowDC( CWnd* pWnd );构造一个CWindowDC对象,它能够访问pWnd指向的CWnd对象的整个屏幕区域(包括客户区和非客户区)。好比咱们在作屏幕保护程序时,通常以整个屏幕区域做为绘制区域。

 

2、设置坐标映射

   (1)Windows坐标系统

Windows坐标系分为逻辑坐标系和设备坐标系两种,GDI支持这两种坐标系。通常而言,GDI的文本和图形输出函数使用逻辑坐标,而在客户区移动或按下鼠标的鼠标位置是采用设备坐标。

<1>逻辑坐标系是面向DC的坐标系,这种坐标不考虑具体的设备类型,在绘图时,Windows会根据当前设置的映射模式将逻辑坐标转换为设备坐标。

<2>设备坐标系是面向物理设备的坐标系,这种坐标以像素或设备所能表示的最小长度单位为单位,X轴方向向右,Y轴方向向下。设备坐标系的原点位置(0, 0)不限定在设备显示区域的左上角。

设备坐标系分为屏幕坐标系、窗口坐标系和客户区坐标系三种相互独立的坐标系。

屏幕坐标系以屏幕左上角为原点,一些与整个屏幕有关的函数均采用屏幕坐标,如GetCursorPos()、SetCursorPos()、CreateWindow()、MoveWindow()。弹出式菜单使用的也是屏幕坐标。

窗口坐标系以窗口左上角为坐标原点,它包括窗口标题栏、菜单栏和工具栏等范围。

客户区坐标系以窗口客户区左上角为原点,主要用于客户区的绘图输出和窗口消息的处理。鼠标消息的坐标参数使用客户区坐标,CDC类绘图成员函数使用与客户区坐标对应的逻辑坐标。

(2)坐标之间的相互转换

编程时,有时须要根据当前的具体状况进行三种设备坐标之间或与逻辑坐标的相互转换。

MFC提供了两个函数CDC::DPtoLP()和CDC::LPtoDP()用于设备坐标与逻辑坐标之间的相互转换。

MFC提供了两个函数CWnd::ScreenToClient()和CWnd::ClientToScreen()用于屏幕坐标与客户区坐标的相互转换。

(3)映射模式

映射模式肯定了在绘制图形时所依据的坐标系,它定义了逻辑单位的实际大小、坐标增加方向,全部映射模式的坐标原点均在设备输出区域(如客户区或打印区)的左上角。此外,对于某些映射模式,用户还能够自定义窗口的长度和宽度,设置视图区的物理范围。

Windows定义了8种映射模式,见下表。

映射模式使得程序员可没必要考虑输出设备的具体设备坐标系,而在一个统一的逻辑坐标系中进行图形的绘制。

映射方法(Mapping Mode)

逻辑单位

坐标轴方向

MM_TEXT(默认方式)

1 pixel

X轴正方向朝右,Y轴正方向朝下

MM_LOMETRIC

0.1 mm

X轴正方向朝右,Y轴正方向朝上

MM_HIMETRIC

0.01 mm

X轴正方向朝右,Y轴正方向朝上

MM_LOENGLISH

0.01 inch

X轴正方向朝右,Y轴正方向朝上

MM_HIENGLISH

0.001 inch

X轴正方向朝右,Y轴正方向朝上

MM_TWIPS

1/1440 inch

X轴正方向朝右,Y轴正方向朝上

MM_ISOTROPIC

自定义(X=Y)

自定义

MM_ANISOTROPIC

自定义(X!=Y)

自定义

当绘制的图形须要随着窗口的大小改变而自动改变的时候,通常选择MM_ISOTROPIC和MM_ANISOTROPIC映射方式。它们的惟一区别就是前者的X轴和Y轴的逻辑单位的大小是相同的,单词“isotropic”就是各个方向相等的意思,此映射方式适合绘制圆或正方形。而实际应用中,经常给X轴和Y轴取不一样的比例,这时候选择MM_ANISOTROPIC映射方式。单词“anisotropic”就是各个方向相异的意思。

(4)自定义映射模式

“窗口”和“视口”的概念:

窗口(Window):对应逻辑坐标系上程序员设定的区域

视口(Viewport):对应实际输出设备上程序员设定的区域

窗口原点是指逻辑窗口坐标系的原点在视口(设备)坐标系中的位置,视口原点是指设备实际输出区域的原点。

除了映射模式,窗口和视口也是决定一个点的逻辑坐标如何转换为设备坐标的一个因素。一个点的逻辑坐标按照以下式子转换为设备坐标:

设备(视口)坐标 = 逻辑坐标 –窗口原点坐标 + 视口原点坐标

//定义坐标映射方式

WINGDIAPI int   WINAPI SetMapMode(HDC, int);

此API函数在MFC中封装为CDC::virtual int SetMapMode(int nMapMode);

//定义逻辑窗口区域,单位为逻辑单位(Logical)

WINGDIAPI BOOL  WINAPI SetWindowExtEx (HDC, int, int, LPSIZE);

此API函数在MFC中封装为CDC::virtual CSize SetWindowExt(int cx, int cy);

//设置逻辑窗口的原点坐标,缺省原点为(0,0)。

WINGDIAPI BOOL  WINAPI SetWindowOrgEx(HDC, int, int, LPPOINT);

此API函数在MFC中封装为CDC::CPoint SetWindowOrg(int x, int y);

注意:SetWindowOrg(Ex) 只有在映射模式为MM_ANISOTROPIC或MM_ISOTROPIC时才有意义。

//定义视口的坐标轴方向及区域、定义域和值域,单位为像素(Pixel)

WINGDIAPI BOOL  WINAPI SetViewportExtEx(HDC, int, int, LPSIZE);

此API函数在MFC中封装为CDC::virtual CSize SetViewportExt(int cx, int cy);

注意:SetViewportExt(Ex) 只有在映射模式为MM_ANISOTROPIC或MM_ISOTROPIC时才有意义。

//设置视口的原点坐标,缺省原点为(0,0)。

WINGDIAPI BOOL  WINAPI SetViewportOrgEx(HDC, int, int, LPPOINT);

此API函数在MFC中封装为CDC:: virtual CPoint SetViewportOrg(int x, int y);

参考:《GDI中的坐标映射问题》http://dev.csdn.NET/article/12/12013.shtm

 

3、建立绘图工具并选入DC

有了画布,要绘图咱们必须有画笔画刷。在Windows中有HPEN、HBRUSH等GDI对象,MFC对GDI对象进行了很好的封装,提供了封装GDI对象的类,如CPen、CBrush、CFont、CBitmap和CPalette等,这些类都是GDI对象类CGdiObject的派生类。

    通常先建立画笔(刷),而后调用CDC::SelectObject函数将画笔(刷)选入设备环境最为当前绘图工具,绘图完毕恢复设备环境之前的画笔(刷)对象,最后调用CGdiObject::DeleteObject函数删除画笔(刷)对象。

    这里须要注意的是,CGdiObject::DeleteObject函数完全删除底层GDI对象(CPen和CBrush类的基类)。在MFC中,当对象销毁时会调用对象的析构函数自动删除对象,通常没必要调用CGdiObject::DeleteObject删除GDI对象,由于若是设备环境还在使用一个GDI对象时,将引发应用程序崩溃或出现难以理解的运行错误。

   (1)建立画笔

BOOL CPen::CreatePen( int nPenStyle, int nWidth, COLORREF cfColor );

nPenStyle  指定画笔的风格。其可能取值的列表,请参见CPen构造函数中的nPenStyle参数。

nWidth 指定画笔的宽度。若是这个值为0,则不论是什么映射模式,以设备单位表示的宽度老是一个像素。

crColor 包含画笔的一个RGB颜色,为COLORREF结构。

此外,可经过CDC::SelectStockObject函数来调用系统预约义的库存笔对应的CGdiObject对象。

pOldPen = (Cpen*)pDC->SelectStockObject(BLACK_PEN);

(2)建立画刷

BOOL CBrush::CreateSolidBrush ( COLORREF crColor );

BOOL CBrush::CreateHatchBrush( int nIndex, COLORREF crColor );

参数: nIndex 指定画刷的阴影线风格。可取的值以下:

HS_HORIZONTAL   /* ==== */

HS_VERTICAL    /* ||||| */

HS_FDIAGONAL  /* ///// */

HS_BDIAGONAL  /* ///// */

HS_CROSS       /* +++++ */

HS_DIAGCROSS  /* xxxxx */

返回值:调用成功时返回非零值,不然为0。

此外,可经过CDC::SelectStockObject函数来调用系统预约义的库存画刷对应的CGdiObject对象。

pOldBrush = (CBrush*)pDC->SelectStockObject(BLACK_BRUSH);

(3)将画笔(刷)选入设备环境。

如下为MFC中默认映射方式下的GDI绘图的模块:

//先获取设备环境pDC

    CPen *pOldPen,newPen;

    CBrush *pOldBrush,newBrush1,newBrush2;

    //建立宽度为pixel的白色实线画笔

    newPen.CreatePen(PS_SOLID,1,RGB(0,0,0));

    //建立红色实线画刷

    newBrush1.CreateSolidBrush(RGB(255,0,0));

    //建立红色实线度的向下(从右到左)影线的阴影画刷

    newBrush2.CreateHatchBrush(HS_BDIAGONAL,RGB(255,0,0));

    //将newPen画笔和newBrush1画刷对象选入设备环境

    pOldPen = pDC->SelectObject(&newPen);

    pOldBrush = pDC->SelectObject(&newBrush1);

    //调用DC绘图函数绘图

    //……

    //绘图完毕,恢复原来画笔、画刷

    pDC->SelectObject(pOldPen);

pDC->SelectObject(pOldBrush);

//删除建立的画笔、画刷

// newPen.DeleteObject();

// newBrush1.DeleteObject();

// newBrush2.DeleteObject();

   (4)当绘制文本Text时,通常能够经过调用CDC::SetBkColor函数来设置背景颜色,调用CDC::SetTextColor函数来设置文字颜色,调用CDC::SetTextAlign函数设置文本对齐标记。

 

4、调用DC绘图函数绘图

GDI为提供了绘制基本图形的成员函数,在MFC中这些函数封装在CDC类中。

注意:绘图函数使用的坐标都是逻辑坐标。

经常使用CDC绘图函数

函数

功能

线输出函数

GetCurrentPosition

获取笔的当前位置(以逻辑坐标表示)

MoveTo

移动当前位置

LineTo

从当前位置到一点画直线,但不包括那个点

Arc

画一段椭圆弧

ArcTo

画一段椭圆弧。除了更新当前位置之外,这个函数与Arc相似

PolyPolyline

画多组相连线段。这个函数不使用也不更新当前位置

PolylineTo

画一条或多条直线,并把当前位置移到最后一条直线的终点

PolyBezier

画一条或多条Bezier样条。不使用也不更新当前位置

PolyBezierTo

画一条或多条Bezier样条,并把当前位置移到最后一条Bezier样条的终点

 

 

椭圆和多边形函数

Chord

绘制椭圆弧(椭圆和一条线段相交围成的闭合图形)

DrawFocusRect

绘制用于表示焦点的风格的矩形

Ellipse

绘制椭圆

Pie

绘制饼形图

Polygon

绘制多边形,包含由线段链接的一个或多个点(顶点)

PolyPolygon

建立使用当前多边形填充模式的两个或多个多边形,多边形能够相互分开或叠加

Polyline

绘制多边形,包含链接指定点的一组线段

Rectangle

使用当前笔绘制矩形,用当前画刷填充

RoundRect

使用当前笔绘制圆角矩形,用当前画刷填充

位图函数

BitBlt

从指定设备上下文拷贝位图

StretchBlt

把位图由源矩形和设备移动到目标矩形,必要时拉伸或压缩位图以适合目标矩形的维数

GetPixel

获取指定点像素的RGB颜色值

SetPixel

设置指定点像素为最接近指定色的近似值

文本函数

TextOut

用当前选取字体在指定位置写字符串

ExtTextOut

用当前选取字体在矩形区域写字符串

TabbedTextOut

在指定位置写字符串,制表符扩展为制表符中止位置数组中指定值

DrawText

在指定矩形内绘制格式化文本

------------------------------详情参考MSDN、MFC类库详解---------------------------

相关文章
相关标签/搜索