注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

逍遥子 曰:

得失失得 何必患得患失 舍得得舍 不妨不舍不得

 
 
 

日志

 
 

MFC中的GDI绘图二[转]  

2012-12-11 13:57:17|  分类: MFC |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

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

   有了画布,要绘图我们必须有画笔画刷。在Windows中有HPENHBRUSHGDI对象,MFCGDI对象进行了很好的封装,提供了封装GDI对象的类,如CPenCBrushCFontCBitmapCPalette等,这些类都是GDI对象类CGdiObject的派生类。

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

   这里需要注意的是,CGdiObject::DeleteObject函数彻底删除底层GDI对象(CPenCBrush类的基类)。在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

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

-------------------详情参考MSDNMFC类库详解-----------------

 

坐标映射实例

 

1)建立单文档MFC项目DrawNew>Projects>MFC AppWizard(EXE)>Single Document

2)找到CMainFrame::PreCreateWindow函数,在其中设置默认窗口大小为400 pixel*300 pixel

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

    if( !CFrameWnd::PreCreateWindow(cs) )

        return FALSE;

    // TODO: Modify the Window class or styles here by modifying

    // the CREATESTRUCT cs

    cs.cx=400;

    cs.cy=300;

    return TRUE;

}

3)添加OnPaint事件

资源管理器—>ClassView>右击CDrawView 选择Add Windows Message Handler

>WM_PAINT> Add Handler

1.     void CDrawView::OnPaint()

2.     {

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

4.          // TODO: Add your message handler code here

5.          CRect cr;//矩形结构

6.          GetClientRect(&cr);//获得客户区窗口

7.          int cx=cr.right;//

8.            int cy=cr.bottom;//

9.          dc.SetMapMode(MM_ISOTROPIC);//X=Y

10.       dc.SetWindowExt(1000,1000);//设置逻辑窗口,默认窗口原点为(00

11.       dc.SetViewportExt(cx,-cy);//定义输出视口,XY上为正

12.       dc.SetViewportOrg(cx/2,cy/2);//定义视口原点为客户区中心

13.       dc.Ellipse(-200,200,200,-200);//绘制椭圆与客户区外切的椭圆

14.       //绘制水平垂直的四条半径

15.       dc.MoveTo(0,0);     dc.LineTo(200,0);

16.       dc.MoveTo(0,0);     dc.LineTo(-200,0);

17.       dc.MoveTo(0,0);     dc.LineTo(0,200);

18.       dc.MoveTo(0,0);     dc.LineTo(0,-200);

19.       //执行F5进行Debug,在底端Output窗口中可以观察ClientRect

20.      TRACE( "ClientRect.x = %d, ClientRect.y = %d/n", cx, cy );

21.  }

运行结果如图1左。当改变窗口大小时,图中圆形状始终不变。

<1>将上面代码的第9行改为:dc.SetMapMode(MM_ANISOTROPIC);//X!=Y运行结果如图1右。

                       1

我们发现,尽管上面代码的第13dc.Ellipse(-200,200,200,-200);中定义的椭圆外接矩形逻辑上为正方形,但是显示的并不是圆,而是椭圆。

当我们改变窗口大小时,图中椭圆变形,甚至可能变为圆形。具体为:

保持窗口宽度不变时,减小高度,椭圆变得更扁;保持窗口高度不变时,减小宽度,椭圆变得更圆,当拉伸到客户区为正方形时,我们发现椭圆变成了圆!

<2>将上面代码的第9行改回dc.SetMapMode(MM_ISOTROPIC);//X=Y,第15行改为dc.LineTo(500,0); 18行改为dc.LineTo(0,-500); 运行结果如图2左。

保持窗口高度不变,减小窗口宽度,使窗口宽度<窗口高度,运行结果如图2右。

 

          2

<3>在将<2>中代码的第9行改回dc.SetMapMode(MM_ANISOTROPIC);//X!=Y

运行结果如图3

                            3

当我们改变窗口大小时,dc.LineTo(500,0); dc.LineTo(0,-500);都是由原点(客户区中心)到客户区右端中心、底端中心的直线。

<4>将原代码中第10dc.SetWindowExt(1000,1000);//设置逻辑窗口后添加dc.SetWindowOrg(100,100);设置逻辑窗口的原点为(100100)。观察运行结果可知,图1中的图形整体向左向下分别移动了100个逻辑单位:

(-200,200,200,-200)——>(-200-100,200-100,200-100,-200-100)

若需要保持图1中的图形,则需要将涉及到的每个点加上(100,100),即:

13.       dc.Ellipse(-200+100,200+100,200+100,-200+100);

14.       //绘制水平垂直的四条半径

15.       dc.MoveTo(0+100,0+100);     dc.LineTo(200+100,0+100);

16.       dc.MoveTo(0+100,0+100);     dc.LineTo(-200+100,0+100);

17.       dc.MoveTo(0+100,0+100);     dc.LineTo(0+100,200+100);

18.       dc.MoveTo(0+100,0+100);     dc.LineTo(0+100,-200+100)

 

<1>逻辑窗口原点映射为视口原点

<2>逻辑窗口宽度和高度映射为视口宽度和高度

<3>当映射方式为MM_ISOTROPIC时,WindowExt.Width=WindowExt.Height,有效绘图区域为以视口宽高中的最小边为边长的正方形区域。比例因子为:

scaleX=scaleY=min{ViewportExt.Width, ViewportExt.Height }/WindowExt.Width

当映射方式为MM_ANISOTROPIC时,有效绘图区域为整个视口(这里为客户区)。比例因子为:

scaleX=ViewportExt.Width/WindowExt.Width

scaleY=ViewportExt.Height /WindowExt.Height。见图4.

<4>设备(视口)坐标 = (逻辑坐标逻辑窗口原点坐标)×比例因子+视口原点坐标

             4

以下分析中客户区大小为ClientRect=(388200),逻辑窗口原点为WindowOrg=(100,100),基于(3<4>中修改后的代码。

在上图4左中,nMapMode=MM_ISOTROPIC,椭圆外接矩形左上角逻辑坐标(-100,300)映射为客户区的以Pixel为单位的坐标为:

left_top_X= (-100-100)×(200/1000+388/2=154 pixel

left_top_Y= (300-100)×(200/1000+200/2=140 pixel

依此次方法可计算出右下角逻辑坐标(300,-100)映射为客户区的以Pixel为单位的坐标为:

right_bottom_X=234  pixel;right_bottom_Y=60 pixel

我们若在第9dc.SetMapMode(MM_ISOTROPIC);//X=Y前添加CreatePen(PS_SOLID,2,RGB(255,0,0));dc.Ellipse(154,140,234,60);则可以发现,这个以2个像素宽的红色画笔绘制的()圆刚好和设置映射模式后绘制的()圆重合。但是我们改变窗口大小时,发现设置映射模式后绘制的()圆按比例拉伸,但红色圆始终在原地且大小保持不变,这也说明了默认映射方式MM_TEXT是以X轴正方向朝右,Y轴正方向朝下的坐标系和1 pixel为单位进行绘制的。

同理,我们可以分析上图4右中,nMapMode=MM_ANISOTROPIC的情况下,CRect(116,140,272,60);为等效椭圆外接矩形。

(4)总结逻辑窗口坐标到设备视口坐标的映射方法

  评论这张
 
阅读(978)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018