第 5章 图形操作内容摘要:
1,CD和 CDC类
2,GDI和 CGdiObject类
3,绘图程序实例一
4,绘图程序实例二返回目录
5.1 CD和 CDC类学习目标
1.了解设备环境 ( Device Context,简称 DC) 和设备环境类 (CDC)
2,利用设备环境类进行简单绘图
5.1
返回第 5章使用设备环境类绘图的一个简单例子
1,利用 AppWizard生成基于对话框程序框架 (BmpTest )
2,添加画线和画椭圆的代码 (OnPaint()函数 ) 见下页
3,代码解释,
( 1) CPaintDC dc( this) ;这句代码是生成一个 CPaintDC类的实例 dc,生成后就可以调用他的函数来进行绘图了 。
( 2) dc.MoveTo( 30,30) ;调用 dc的 MoveTo函数把画线的起点定在对话框的
( 30,30) 坐标点 。 MoveTo函数的一般调用格式为,MoveTo( int x,int
y),其中参数 x,y是要画直线的起始点坐标 。
( 3) dc.LineTo( 30,30) ;调用 dc的 LineTo函数画线到 ( 100,100) 坐标点 。
LineTo函数的一般调用格式为,LineTo( int x,int y),其中参数 x,y是所画直线的终止点坐标 。
( 4) dc.Ellipse( 120,120,160,160);本句代码的作用是调用设备环境对象 dc
的画椭圆函数 Ellipse画椭圆。 Ellipse函数的一般调用格式为,Ellipse
( LPCRECT lpRect),其中参数 lpRect是所画椭圆的外接矩形 。
5.1
5.1
void CBmpTestDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND,(WPARAM) dc.GetSafeHdc(),0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x,y,m_hIcon);
}
else
{
CPaintDC dc(this); // device context for painting
dc.MoveTo(10,10);
dc.LineTo(100,100);
dc.Ellipse(120,120,160,160);
CDialog::OnPaint();
}
}
2.1
void CBmpTestDlg::OnPaint()
{
if(IsIconic())
{
//与前一页代码相同;
else
{
CPaintDC dc(this); // device context for painting
CPen newpen(PS_SOLID,1,RGB(0,255,0));
CBrush newbrush(RGB(0,0,255));
CPen *pOldpen=dc.SelectObject(&newpen);
CBrush *pOldbrush=dc.SelectObject(&newbrush);
dc.MoveTo(10,10);
dc.LineTo(100,100);
dc.Ellipse(80,80,120,120);
dc.SelectObject(pOldpen);
dc.SelectObject(pOldbrush);
CDialog::OnPaint();
}
}
4.改变画图颜色
5.1
代码解释:
1.CPen newpen( PS_SOLID,1,RGB( 0,255,0));生成一支颜色为绿色、宽度为 1个像素的实心画笔。
2.CBrush newbrush( RGB( 0,0,255));生成一只颜色为蓝色的画刷。
3.CPen *pOldpen=dc.SelectObject( &newpen);把刚生成的画笔选进设备环境对象 dc,
这样再画线就用新的笔了。当 SelectObject函数的参数为画笔类型时,返回值为原来所用画笔的指针。
4.CBrush *pOldbrush=dc.SelectObject( &newbrush);把刚生成的画刷选进设备环境对象 dc。当 SelectObject函数的参数为画刷类型时,返回值为原来所用画刷的指针。
5.dc.SelectObject( pOldpen);绘图完毕后把原来的画笔再选进设备环境对象。
6.dc.SelectObject( pOldbrush);绘图完毕后把原来的画刷再选进设备环境对象。
Cpen类的构造函数声明是:
CPen(int nPenStyle,int nWidth,COLORREF crColor);
参数 nPenStyle指明画笔的风格。 nWidth指明以象素为单位的画笔宽度。 crColor包含了画笔的 RGB值。
nPenStyle的取值有以下几种:
PS_SOLD,创建一支实线画笔。
PS_DASH,创建一支虚线画笔。
PS_DOT,创建一支点线画笔。
PS_DASHDOT,创建一支虚线和点交替的画笔。
PS_DASHDOTDOT,创建一支虚线和两点交替的画笔。
5.2 GDI和 CGdiObject类学习目标
1.了解图形设备接口
2,了解类 CGdiObject极其派生类
5.2
返回第 5章绘图程序实例一
1,用应用向导产生单文档程序框架 (BmpTest )
2,修改、添加资源
1,添加菜单
1,ID_GRAPH_TRACK 轨迹
2,ID_GRAPH_MOVE 移动
2,添加位图
1,IDB_BIT1
2,IDB_BIT2
3,添加数据类型及变量
5.2
classCMoveTestView:publicCView
{
protected://createfrom serializationonly
CMoveTestView();
DECLARE_DYNCREATE(CMoveTestView)
private:
CStringflag;
BOOLdrawtrack;
structpointstruct{
CPointpoint;
structpointstruct*next;
};
structpointstruct*pointlisthead,*pointlisttrail;
……
}
5.2
flag:判断选择的是“轨道”菜单项还是“车轮”菜单项按钮;
drawtrack:当用户选择轨道菜单项时,判断是否画轨道;
struct pointstruct:用来记录轨道中各个点的结构体;
pointlisthead:用户所绘制轨道的点链表的头结点;
pointlisttrail:用户所绘制轨道的点链表的尾结点。
4,添加函数
1,添加菜单响应函数
5.2
void CMoveTestView::OnGraphMove()
{
flag="move";
MoveObject();
}
void CMoveTestView::OnGraphTrack()
{
flag="track";
}
代码解释:
1.当选择菜单“轨迹”时,把 flag赋值为,track”;
2.当选择菜单“移动”时,把 flag赋值为,move”;
3.MoveObject();调用此函数可产生沿着画好轨道运动的车轮。该函数将在后面给出。
2,添加鼠标响应函数 5.2
void CMoveTestView::OnLButtonDown(UINT nFlags,CPoint point)
{
if(flag= ="track")
{
struct pointstruct *temppoint;
while(pointlisthead)
{
temppoint = pointlisthead;
pointlisthead = pointlisthead->next;
delete temppoint;
}
drawtrack=true;
}
CView::OnLButtonDown(nFlags,point);
}
5.2
void CMoveTestView::OnMouseMove(UINT nFlags,CPoint point)
{
if(drawtrack)
{
CClientDC dc(this);
dc.SetPixel(point.x,point.y,RGB(0,0,0));
struct pointstruct *newpoint=new struct pointstruct;
newpoint->point.x=point.x;
newpoint->point.y=point.y;
newpoint->next=NULL;
if(pointlisthead= =NULL)
{
pointlisthead=pointlisttrail=newpoint;
}
else
{
pointlisttrail->next=newpoint;
pointlisttrail=newpoint;
}
}
CView::OnMouseMove(nFlags,point);
}
5.2void CMoveTestView::OnLButtonUp(UINTnFlags,CPoint point)
{
if(drawtrack)
drawtrack=false;
CView::OnLButtonUp(nFlags,point);
}
1,当按下鼠标左键时调用函数,如果当前用户选择的是“轨迹”
菜单,说明用户要绘制轨道,则把变量 drawtrack赋值为真,此时鼠标移动则绘制轨道。
2,当在视图窗口中移动鼠标时,根据变量 drawtrack判断是否要绘制轨道,在绘制轨道时首先创建设备环境对象 dc。调用函数
SetPixel( point.x,point.y,RGB( 0,0,0))在鼠标当前点画点。
并把鼠标当前点加入轨迹链表。
3,当鼠标左键抬起时,如果变量 drawtrack为真,把他设置为假以结束轨迹绘制。此后,鼠标再移动,将不再绘制轨迹,除非在 flag==“track”时再按下鼠标左键。
5.构造函数中将 drawtrack 初始化为 FALSE 5.2
CMoveTestView::CMoveTestView()
{
// TODO,add construction code here
drawtrack=false;
pointlisthead =NULL;
}
6,MoveObject()函数
voidCMoveTestView::MoveObject()
{
struct pointstruct *pointtemp=pointlisthead;
int i=1;
CPointpointold;
BOOL first=true;
while(pointtemp)
{
CClientDCdc(this);
CDC*memdc=newCDC;
memdc->CreateCompatibleDC(&dc);
if(!first)
{
CBitmapbitmap1;
if(i%2==0)
bitmap1.LoadBitmap(IDB_BIT1);
else
bitmap1.LoadBitmap(IDB_BIT2);//DSTINVERT
memdc->SelectObject(&bitmap1);
dc.BitBlt(pointold.x-24,pointold.y-24,48,48,memdc,0,0,PATPAINT);
}
else
first=false;
CBitmapbitmap;
if(i%2)
代码解释:
( 1) i变量用来标志该绘制哪一个位图;
( 2) first变量用来判断是否是第一个点;
( 3) dc.BitBlt( pointold.x-24,pointold.y-24,48,48,memdc,0,0,PATPAINT) ;
调用绘制位图函数把 memdc设备环境对象中的位图拷贝到 dc所标识的设备环境对象中。
( 4)
pointtemp=pointlisthead;
while(pointtemp)
{
CClientDC dc(this);
dc.SetPixel(pointtemp->point.x,pointtemp->point.y,RGB(0,0,0));
pointtemp=pointtemp->next;
}
车轮运动完毕后,重新绘制轨迹各点。
绘图程序实例二
1,用应用向导产生单文档程序框架 (Draw )
2,编辑各种资源
1,定义 ID
1,ID_DRAW_LINE
2,ID_DRAW_RECTANGLE
3,ID_DRAW_ELLIPSE
4,ID_DRAW_FILL
5,ID_DRAW_LINEWIDTH_ONE
6,ID_DRAW_LINEWIDTH_TWO
7,ID_DRAW_LINEWIDTH_THREEE
8,ID_DRAW_LINEWIDTH_FOUR
9,ID_DRAW_LINEWIDTH_FIVE
2,编辑菜单资源
3,编辑工具条资源
4,编辑光标资源
5.2
5.2
菜单项 ID Caption 属性直线 ID_DRAW_LINE 直线 ( &L)
矩形 ID_DRAW_RECTANGLE 矩形 ( &R)
椭圆 ID_DRAW_ELLIPS 椭圆 ( &E)
填充 ID_DRAW_FILL 填充 ( &F)
分隔线 Separator
线宽 线宽 Pop-up
线色 ID_DRAW_LINECOLOR 线色 ( &C),..
填充色 ID_DRAW_FILLCOLOR 填充色 ( &O),..
1 D_DRAW_LINEWIDTH_ONE 1
2 ID_DRAW_LINEWIDTH_TWO 2
3 ID_DRAW_LINEWIDTH_THREE 3
4 ID_DRAW_LINEWIDTH_FOUR 4
5 ID_DRAW_LINEWIDTH_FIVE 5
5.2
各按扭的 ID号分别是“直线”、“矩形”、“椭圆”
和“填充”菜单项的 ID号,
填充光标,ID_CURSOR_FILL
3.在视类中添加成员变量并初始化 -----( 1)添加成员变量
5.2
classCDrawView,public CView
{// Implementation
public:
int m_nMaxX; // 用于保存屏幕尺寸
int m_nMaxY;
CDC * m_pMemDC; //内存 DC
CBitmap * m_pBitmap; // 与显示 DC的位图兼容的位图
int m_nDrawType; //画图类型
int m_nLineWidth; //线宽
COLORREF m_cLineColor; //线的颜色
COLORREF m_cFillColor; //填充颜色
bool m_bDrawing; //是否正在画图
CPoint m_ptStart; //画图的起始点
CPoint m_ptOld; //保存鼠标临时点
virtual ~CDrawView();
};
3.在视类中添加成员变量并初始化
( 2) 在构造函数中初始化部分成员变量
5.2
CDrawView::CDrawView()
{
// TODO,add construction code here
m_pMemDC = new CDC; //构造内存 DC对象
m_pBitmap = new CBitmap; // 构造位图对象
m_nDrawType = -1; //程序启动时没有选中绘图类型,不处于绘图状态
m_nLineWidth = 1; //线宽初始为 1个像素
m_cLineColor = RGB(0,0,0); //线的颜色初始为黑色
m_cFillColor = RGB(0,0,255); //填充色初始为蓝色
m_bDrawing = false; //程序启动时不处于画图状态
}
3.在视类中添加成员变量并初始化
( 3) 在析构造函数中释放指针变量占用的内存
5.2
CDrawView::~CDrawView()
{
delete m_pMemDC; //删除内存 DC对象
delete m_pBitmap; //删除位图对象
}
3.在视类中添加成员变量并初始化
( 4) 为视类添加 WM_CREATE消息处理函数
5.2
int CDrawView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO,Add your specialized creation code here
m_nMaxX= GetSystemMetrics(SM_CXSCREEN); //得到屏幕宽度
m_nMaxY= GetSystemMetrics(SM_CYSCREEN); //得到屏幕高度
CDC * pDC = GetDC(); //获得显示 DC的指针
m_pMemDC->CreateCompatibleDC(pDC); //创建显示 DC的兼容内存 DC
//创建与显示 DC的,位图,兼容的位图,位图和屏幕一样大
m_pBitmap->CreateCompatibleBitmap(pDC,m_nMaxX,m_nMaxY);
//将兼容位图选进内存 DC,pOldBitmap用来保存内存 DC中的原位图
CBitmap * pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
// 创建一个白色画刷
CBrush brush;
brush.CreateStockObject(WHITE_BRUSH);
//将选进内存 DC的位图整个填上白色 ( 与窗口底色一致 )
4.在视类中添加菜单消息响应函数
(1) 在视图类中添加,直线”、“矩形”、“椭圆”、“填充” 4个菜单项的消息响应函数,和“线宽”菜单中 5个菜单项的消息响应函数
5.2
DrawView.h文件
afx_msg void OnSelectDrawType(UINT nID);
afx_msg void OnUpdateSelectDrawType(CCmdUI * pCmdUO);
afx_msg void OnDrawLineWidth(UINT nID);
afx_msg void OnUpdateDrawLineWidth(CCmdUI * pCmdUO);
DrawView.cpp文件
ON_COMMAND_RANGE(ID_DRAW_LINE,ID_DRAW_FILL,OnSelectDrawType)
ON_UPDATE_COMMAND_UI_RANGE(ID_DRAW_LINE,ID_DRAW_FILL,OnUpdateSelectDrawType)
ON_COMMAND_RANGE(ID_DRAW_LINEWIDTH_ONE,ID_DRAW_LINEWIDTH_FIVE,OnDrawLineWidth)
ON_UPDATE_COMMAND_UI_RANGE(ID_DRAW_LINEWIDTH_ONE,ID_DRAW_LINEWIDTH_FIVE,OnUpdat
eDrawLineWidth)
void CDrawView::OnSelectDrawType(UINT nID)
{
m_nDrawType = nID - ID_DRAW_LINE;
}
void CDrawView::OnUpdateSelectDrawType(CCmdUI * pCmdUI)
{
int nID = pCmdUI->m_nID - ID_DRAW_LINE;
if(nID == m_nDrawType)
pCmdUI->SetCheck(true);
else
pCmdUI->SetCheck(false);
}
void CDrawView::OnDrawLineWidth(UINT nID)
{
m_nLineWidth = nID - ID_DRAW_LINEWIDTH_ONE + 1;
}
void CDrawView::OnUpdateDrawLineWidth(CCmdUI * pCmdUI)
{
int nID = pCmdUI->m_nID - ID_DRAW_LINEWIDTH_ONE + 1;
if(nID == m_nLineWidth)
pCmdUI->SetCheck(true);
else
pCmdUI->SetCheck(false);
}
5.2
( 2)在视图类中添加,线色 …” 和“填充色 …”2 个菜单项的消息处理函数 5.2
void CDrawView::OnDrawLinecolor()
{
CColorDialog dlg;
dlg.m_cc.Flags |= CC_PREVENTFULLOPEN | CC_RGBINIT;
dlg.m_cc.rgbResult = m_cLineColor;
if(dlg.DoModal() == IDOK)
{
m_cLineColor = dlg.GetColor();
}
}
void CDrawView::OnDrawFillcolor()
{
CColorDialog dlg;
dlg.m_cc.Flags |= CC_PREVENTFULLOPEN | CC_RGBINIT;
dlg.m_cc.rgbResult = m_cFillColor;
if(dlg.DoModal() == IDOK)
{
m_cFillColor = dlg.GetColor();
}
}
5.在视类中添加鼠标消息响应函数
void CDrawView::OnLButtonDown(UINT nFlags,CPoint point)
{
if(m_bDrawing) return; //如果正在画图,则返回
SetCapture(); //捕获鼠标信息
m_bDrawing = true; //表示正在画图
m_ptStart = point; //将当前鼠标点赋给画图的起点 m_ptStart
m_ptOld = point; //将当前鼠标点赋给临时点 m_ ptOld
if(m_nDrawType == 3) //如果是填充,处理如下
{
CBrush * pOldBrush; //定义保存内存 DC中原画刷的临时指针
CBitmap * pOldBitmap; //定义保存内存 DC中原位图的临时指针
CBrush brFill; //以下两行根据当前填充颜色创建实心画刷
brFill.CreateSolidBrush(m_cFillColor);
// 将创建的画刷选入内存 DC,并将原来内存 DC中的画刷保存在 pOldBrush中
pOldBrush = m_pMemDC->SelectObject(&brFill);
// 将兼容位图选入内存 DC,并将原来内存 DC中的位图保存在 pOldBitmap中
pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
// 调用 CDC类的函数 ExtFloodFill()填充内存 DC的一个封闭区域
m_pMemDC->ExtFloodFill(point.x,point.y,m_pMemDC->GetPixel(point),FLOODFILLSURFACE);
Invalidate(false); //使视窗口无效,重画视窗
m_pMemDC->SelectObject(pOldBrush); //恢复内存 DC原来的画刷
m_pMemDC->SelectObject(pOldBitmap); //恢复内存 DC原来的位图
m_bDrawing = false; //画图结束
}
// CView::OnLButtonDown(nFlags,point);
5.2
5.在视类中添加鼠标消息响应函数
void CDrawView::OnMouseMove(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(m_bDrawing) //如果正在画图,执行以下代码
{
CDC * pDC = GetDC(); //获得显示 DC的指针
CBitmap * pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
CPen pen; //以下两行根据线宽和线色创建实线画笔
pen.CreatePen(PS_SOLID,m_nLineWidth,m_cLineColor);
CPen * pOldPen = pDC->SelectObject(&pen);
// 将系统提供的空刷子选入 DC,并将原来 DC中的画刷保存在 pOldBrush中
CBrush * pOldBrush = (CBrush *) pDC->SelectStockObject(NULL_BRUSH);
// 以画图起始点和上次鼠标点为对角点构造矩形
CRect rectOld(m_ptStart,m_ptOld);
rectOld.NormalizeRect();
rectOld.InflateRect(m_nLineWidth,m_nLineWidth); //根据线宽扩大矩形
// 将内存 DC中对应上面构造的矩形区域拷贝到显示 DC,擦掉原来画的矩形
pDC->BitBlt(rectOld.left,rectOld.top,rectOld.Width(),rectOld.Height(),m_pMemDC,
rectOld.left,rectOld.top,SRCCOPY);
// 以画图起始点和鼠标当前点为对角点构造矩形
CRect rectNew(m_ptStart,point);
switch(m_nDrawType)
{
case 0,//画直线
pDC->MoveTo(m_ptStart);
5.2
5.在视类中添加鼠标消息响应函数
void CDrawView::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(m_bDrawing)
{
m_bDrawing = false; //画图过程结束
CBitmap * pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
CPen pen;
pen.CreatePen(PS_SOLID,m_nLineWidth,m_cLineColor);
CPen * pOldPen = m_pMemDC->SelectObject(&pen);
CBrush * pOldBrush = (CBrush *) m_pMemDC->SelectStockObject(NULL_BRUSH);
// 以画图起始点和鼠标当前点 ( 画图结束点 ) 为对角点构造矩形
CRect rectNew(m_ptStart,point);
switch(m_nDrawType)
{
case 0,// 画直线
m_pMemDC->MoveTo(m_ptStart);
m_pMemDC->LineTo(point);
break;
case 1,//画矩形
m_pMemDC->Rectangle(rectNew);
break;
case 2,//画椭圆
m_pMemDC->Ellipse(rectNew);
break;
5.2
6.修改 OnDraw()函数
void CDrawView::OnDraw(CDC* pDC)
{
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO,add draw code for native data here
CBitmap * pOldBitmap = m_pMemDC->SelectObject(m_pBitmap);
pDC->BitBlt(0,0,m_nMaxX,m_nMaxY,m_pMemDC,0,0,SRCCOPY);
m_pMemDC->SelectObject(pOldBitmap);
}
5.2
7.添加 WM_SETCURSOR消息的处理函数
BOOL CDrawView::OnSetCursor(CWnd* pWnd,UINT nHitTest,UINT message)
{
// TODO,Add your message handler code here and/or call default
switch(m_nDrawType)
{
case 0:
case 1:
case 2:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));
break;
case 3:
::SetCursor(AfxGetApp()->LoadCursor(ID_CURSOR_FILL));
break;
default:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
break;
}
return true;
// return CView::OnSetCursor(pWnd,nHitTest,message);
}
5.2
返回第 5章