图形的保存和重绘

转自:http://blog.163.com/wufanjx@126/blog/static/1706558832011484416631/

图形的保存和重绘

********************

********************

创建一个保存绘图类型m_nDrawType,鼠标按下点坐标m_ptOrigin,鼠标松开点坐标m_ptEnd的类CGraph

Graph.h

class CGraph 

{

public:

         CPoint m_ptOrigin;

         CPoint m_ptEnd;

         UINT m_nDrawType;

         CGraph();

         CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);

         virtual ~CGraph();

 

};

Grap.cpp

CGraph::CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd)

{

         this->m_nDrawType=m_nDrawType;

         this->m_ptOrigin=m_ptOrigin;

         this->m_ptEnd=m_ptEnd;

}

GraphicView.cpp

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

{

         m_ptOrigin=point;

         CScrollView::OnLButtonDown(nFlags, point);

}

 

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)

{

         CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

         dc.SelectObject(pBrush);

 

         switch(m_nDrawType)

         {

         case 1:

                   dc.SetPixel(point,RGB(0,0,0));

                   break;

         case 2:

                   dc.MoveTo(m_ptOrigin);

                   dc.LineTo(point);

                   break;

         case 3:

                   dc.Rectangle(CRect(m_ptOrigin,point));

                   break;

         case 4:

                   dc.Ellipse(CRect(m_ptOrigin,point));

                   break;

         }

         //CGraph graph(m_nDrawType,m_ptOrigin,point);

         //m_ptrArray.Add(&graph);

         CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);

         m_ptrArray.Add(pGraph);

         CScrollView::OnLButtonUp(nFlags, point);

}

 

void CGraphicView::OnDraw(CDC* pDC)

{

         CGraphicDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: add draw code for native data here

    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

         pDC->SelectObject(pBrush);

 

         for(int i=0;i<m_ptrArray.GetSize();i++)

         {

                   switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)

                   {

                   case 1:

                            pDC->SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0));

                            break;

                   case 2:

                            pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);

                            pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);

                            break;

                   case 3:

                            pDC->Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,

                                     ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));

                            break;

                   case 4:

                            pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,

                                     ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));

                            break;

                   }

         }

 

********************
********************

MFCVIEWCORE.cpp文件中:

void CView::OnPaint()

{

         // standard paint routine

         CPaintDC dc(this);

         OnPrepareDC(&dc);

         OnDraw(&dc);

}

OnDraw是虚函数,根据多态原理,调用时会程序会寻找子类中是否有OnDraw函数,如果子类中没有,再执行父类的OnDraw函数。如果在子类中添加WM_PAINT响应函数OnPaint将不会再执行OnDraw函数。

窗口滚动:

映射方式:坐标空间

逻辑坐标点会设备坐标点的转换。

void CGraphicView::OnPaint()

{

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

        

         OnPrepareDC(&dc);

         OnDraw(&dc);

}

 

void CGraphicView::OnInitialUpdate()

{

         CScrollView::OnInitialUpdate();

        

         SetScrollSizes(MM_TEXT,CSize(800,600));

}

 

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)

{

         CClientDC dc(this);

         CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

         dc.SelectObject(pBrush);

 

         switch(m_nDrawType)

         {

         case 1:

                   dc.SetPixel(point,RGB(0,0,0));

                   break;

         case 2:

                   dc.MoveTo(m_ptOrigin);

                   dc.LineTo(point);

                   break;

         case 3:

                   dc.Rectangle(CRect(m_ptOrigin,point));

                   break;

         case 4:

                   dc.Ellipse(CRect(m_ptOrigin,point));

                   break;

         }

OnPrepareDC(&dc);

         dc.DPtoLP(&m_ptOrigin);//

         dc.DPtoLP(&point);

         CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);

         m_ptrArray.Add(pGraph);

         CScrollView::OnLButtonUp(nFlags, point);

}

补充1参考OnPrepareDC函数,在VIEWSCRL.cpp文件的OnPrepareDC函数中设置了视口原点pDC->SetViewportOrg(ptVpOrg);导致图形错位。解决方法是在保存坐标点之前调用OnPrepare,然后再进行设备坐标向逻辑坐标点转换。OnPrepareDC在窗口重绘时始终调整视口的原点,设备点的原点始终为左上角(00)。在MM_TEXT映射模式下,视口和窗口的单位相同。

补充2逻辑坐标和设备坐标的相互转换

窗口(逻辑)坐标转换为视口(设备)坐标的两个公式:

xViewport=(xWindow-xWinOrg)*xViewExt/xWinExt +xViewOrg

yViewport=(yWindow-yWinOrg)*yViewExt/yWinExt +yViewOrg

视口(设备)坐标转换为窗口(逻辑)坐标的两个公式:

xWindow=(xViewPort-xViewOrg)* xWinExt / xViewExt+xWinOrg

yWindow=(yViewPort-yViewOrg)* xWinExt / xViewExt +yWinOrg

MM_TEXT映射方式下逻辑坐标和设备坐标的相互转换:

窗口(逻辑)坐标转换为视口(设备)坐标的两个公式:

xViewport = xWindow-xWinOrg+xViewOrg

yViewport = yWindow-yWinOrg+yViewOrg

视口(设备)坐标转换为窗口(逻辑)坐标的两个公式:

xWindow = xViewport-xViewOrg+xWinOrg

yWindow = yViewport-yViewOrg+yWinOrg

补充3

关于图形错位的说明:当我们在窗口中点击鼠标左键的时候,得到的是设备坐标(680,390),在MM_TEXT的映射模式下,逻辑坐标和设备坐标是相等的,所以我们利用集合类保存的这个点的坐标是以象素为单位,坐标值为(680,390)。在调用OnDraw函数前,在OnPaint函数中调用了OnPrepareDC函数,调整了显示上下文的属性,将视口的原点设置为了(0,-150),这样的话,窗口的原点,也就是逻辑坐标(0,0)将被映射为设备坐标(0,-150),在画线的时候,因为GDI的函数使用的是逻辑坐标,而图形在显示的时候,Windows需要将逻辑坐标转化为设备坐标,因此,原先保存的坐标点(680,390)(在GDI函数中,作为逻辑坐标使用),根据转换公式

xViewport=xWindow-xWinOrg+xViewOrg

yViewport = yWindow-yWinOrg+yViewOrg,得到设备点的x坐标为680-0+0=680,设备点的y坐标为390-0+(-150)=240,于是我们看到图形在原先显示地方的上方出现了。

解决方法:首先我们在绘制图形之后,在保存坐标点之前,调用OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为(0,-150),这样的话,窗口的原点,也就是逻辑坐标(0,0)将被映射为设备坐标(0,-150),然后我们调用DPtoLP函数将设备坐标(680,390)转换为逻辑坐标,根据设备坐标转换为逻辑坐标的公式:

     xWindow = xViewport-xViewOrg+xWinOrg

     yWindow = yViewport-yViewOrg+yWinOrg,得到逻辑点的x坐标为680-0+0=680y坐标为390-(-150)+0=540,将逻辑坐标(680,540)保存起来,在窗口重绘时,会先调用OnPrepareDC函数,调整显示上下文的属性,将视口的原点设置为了(0,-150),然后GDI函数用逻辑坐标点(680,540)绘制图形,被Windows转换为设备坐标点(680,390),和原先显示图形时的设备点是一样的,当然图形就还在原先的地方显示出来。

补充4

设备坐标(Device Coordinate)又称为物理坐标(Physical Coordinate),是指输出设备上的坐标。通常将屏幕上的设备坐标称为屏幕坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。  

逻辑坐标(Logical Coordinate)是系统用作记录的坐标。在缺省的模式(MM_TEXT)下,       逻辑坐标的方向和单位与设备坐标的方向和单位相同,也是以像素为单位来表示的,X轴向右为正,Y轴向下为正,坐标原点位于窗口的左上角。逻辑坐标和设备坐标即使在缺省模式下其数值也未必一致,除了在以下两种情况下:1.窗口为非滚动窗口;2.窗口为滚动窗口,但垂直滚动条位于滚动边框的最上端,水平滚动条位于最左端,但如果移动了滚动条这两种坐标就不一致了。 

VC中鼠标坐标的坐标位置用设备坐标表示,但所有GDI绘图都用逻辑坐标表示,所以用鼠标绘图时,那么必须将设备坐标转换为逻辑坐标,可以使用CDC 函数DptoLP()将设备坐标转化为逻辑坐标,同样可以用LptoDP()将逻辑坐标转化为设备坐标。

补充5 OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点。

2种保存图形和重绘图形的方式

CMetaFileDC

CGraphicView类中增加成员变量CMetaFileDC m_dcMetafile,并在构造函数中初始化:

M_dcMetafile.Create();

 

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)

{

         // TODO: Add your message handler code here and/or call default

         CClientDC dc(this);

         CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

         m_dcMetaFile.SelectObject(pBrush);

 

         switch(m_nDrawType)

         {

         case 1:

                   m_dcMetaFile.SetPixel(point,RGB(0,0,0));

                   break;

         case 2:

                   m_dcMetaFile.MoveTo(m_ptOrigin);

                   m_dcMetaFile.LineTo(point);

                   break;

         case 3:

                   m_dcMetaFile.Rectangle(CRect(m_ptOrigin,point));

                   break;

         case 4:

             m_dcMetaFile.Ellipse(CRect(m_ptOrigin,point));

                   break;

         }

         CScrollView::OnLButtonUp(nFlags, point);

}

 

void CGraphicView::OnDraw(CDC* pDC)

{

         CGraphicDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: add draw code for native data here

         HMETAFILE hmetaFile;

         hmetaFile=m_dcMetaFile.Close();

         pDC->PlayMetaFile(hmetaFile);

         m_dcMetaFile.Create();

         m_dcMetaFile.PlayMetaFile(hmetaFile);

         DeleteMetaFile(hmetaFile);

}

 

void CGraphicView::OnFileSave()

{

         // TODO: Add your command handler code here

         HMETAFILE hmetaFile;

         hmetaFile=m_dcMetaFile.Close();

         CopyMetaFile(hmetaFile,"meta.wmf");

         m_dcMetaFile.Create();

         DeleteMetaFile(hmetaFile);

}

 

void CGraphicView::OnFileOpen()

{

         // TODO: Add your command handler code here

         HMETAFILE hmetaFile;

         hmetaFile=GetMetaFile("meta.wmf");

         m_dcMetaFile.PlayMetaFile(hmetaFile);

         DeleteMetaFile(hmetaFile);

         Invalidate();

}

②在CGraphicView类中增加成员变量CDC m_dcCompatible

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)

{

         // TODO: Add your message handler code here and/or call default

         CClientDC dc(this);

         CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

 

         if(!m_dcCompatible.m_hDC)

         {

                   m_dcCompatible.CreateCompatibleDC(&dc);

                   CRect rect;

                   GetClientRect(&rect);

                   CBitmap bitmap;

                   bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());

                   m_dcCompatible.SelectObject(&bitmap);

                   m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);

                   m_dcCompatible.SelectObject(pBrush);

         }

         switch(m_nDrawType)

         {

         case 1:

                   //dc.SetPixel(point,RGB(0,0,0));

                   m_dcCompatible.SetPixel(point,RGB(0,0,0));

                   break;

         case 2:

                   //dc.MoveTo(m_ptOrigin);

                   //dc.LineTo(point);

                   m_dcCompatible.MoveTo(m_ptOrigin);

                   m_dcCompatible.LineTo(point);

                   break;

         case 3:

                   //dc.Rectangle(CRect(m_ptOrigin,point));

                   m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));

                   break;

         case 4:

                   //dc.Ellipse(CRect(m_ptOrigin,point));

                   m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));

                   break;

         }

         CScrollView::OnLButtonUp(nFlags, point);

}

 

void CGraphicView::OnDraw(CDC* pDC)

{

         CGraphicDoc* pDoc = GetDocument();

         ASSERT_VALID(pDoc);

         // TODO: add draw code for native data here

         CRect rect;

         GetClientRect(&rect);

         pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值