孙鑫: 第十一讲 图形保存和绘画

 

1.创建4个菜单,为其添加消息响应,用成员变量保存绘画类型。添加LButtonDown和Up消息。
2.当窗口重绘时,如果想再显示原先画的数据,则需要保存数据。为此创建一个新类来记录绘画类型和两个点。
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();

};
   然后在void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中加入如下代码
//CGraph graph(m_nDrawType,m_ptOrigin,point);//不能用局部变量
//m_ptrArray.Add(&graph);//加入这种指针数组中
//加入到指针数组中
在GraphicView.h中有如下代码
CPtrArray m_ptrArray;
   在OnDraw中重画时调出数据
for(int i=0;i<m_ptrArray.GetSize();i++)


3.在CView::OnPaint()调用了OnDraw(),但在void CGraphicView::OnPaint()中MFC的Wizard没有调用OnDraw(),要注意这个区别。如果你此时想调用,必须手动添加代码。 OnDraw(&dc);


4.让窗口具有滚动条的功能。
   第1.将CGraphicView的头文件中的CView全部替换成CSrollView
   第2.添加如下的代码
void CGraphicView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();

// TOD Add your specialized code here and/or call the base class
SetScrollSizes(MM_TEXT,CSize(800,600));//设置映射模式,设定窗口大小。OK!
}
5.坐标系的转换,此处不再详细介绍,需要时请查阅相关资料。
6.解决重绘时线跑到上面的问题。为什么会错位?因为逻辑坐标和设备坐标没有对应起来。
解决方法:
  在OnLButtonDown画完图后,保存之前。调用

7.另外两种方法来保存数据。
  一种是用CMetaFileDC
  另一种是利用兼容DC,重绘时利用 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
将兼容DC的图拷贝到屏幕DC上去。
此处不再详细介绍这两种方法,因为介绍多了容易搞晕。呵呵

 

 

如何让CDC上输出的文字、图形具有保持功能,集合类CPtrArray的使用,CPaintDC与CClientDC的区别与应用,OnPaint与OnDraw在CView中的关系及实现内幕,滚动窗口的实现,坐标空间,映射方式,设备坐标与逻辑坐标的转换。元文件设备描述表的使用,如何利用兼容DC实现图形的保存和再现。

 

#先实现一下上节课的绘图功能

#保存所绘制的图像以及图像的重绘00:09

3个要素 绘制的类型,起点,终点

保存这3个要素就可以在OnDraw中对其进行重绘

×新建一个类来保存CGraph

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();

};

×动态存储这些对象

用MFC的集合类CPtrArray,支持void指针数组。CPtrArray与CObArray相似

MSDN:

The CPtrArray class supports arrays of void pointers.

The member functions of CPtrArray are similar to the member functions of class CObArray. Because of this similarity, you can use the CObArray reference documentation for member function specifics. Wherever you see a CObject pointer as a function parameter or return value, substitute a pointer to void.

CObject* CObArray::GetAt( int <nIndex> ) const;

for example, translates to

void* CPtrArray::GetAt( int <nIndex> ) const;

 

#增加Add,获取元素GetAt,获取元素数目GetSize

#代码:

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

//使用new在堆中分配空间,在栈中分配的话CGraph的对象析构以后内存被回收

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

       m_ptrArray.Add(pGraph); //CPtrArray m_ptrArray;

 

//void CGraphicView::OnDraw(CDC* pDC)

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

       pDC->SelectObject(pBrush);

 

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

       {

              //void* to CGraph* 类型转换

              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;

              }

       }

 

#OnDraw函数00:32

OnDraw是个虚函数。

Vc安装目录vc98-》MFC-》SRC-》viewcore.cpp

void CView::OnPaint() //可以在此设置断点看是否能够进入这里

{

       // standard paint routine

       CPaintDC dc(this);

       OnPrepareDC(&dc);

在view类中重写OnPaint消息,则系统会调用自定义的OnPaint函数。

void CGraphicView::OnPaint()

{

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

       // TODO: Add your message handler code here

       OnPrepareDC(&dc);

       OnDraw(&dc);

       // Do not call CScrollView::OnPaint() for painting messages

}

 

 

#让窗口具有滚动的能力00:44

#在头文件和源文件中手动把view的基类改为CScrollView

#对CScrollView的尺寸进行设置

SetScrollSizes

//什么时候调用呢?窗口创建之后调用

//使用OnInitialUpdate(该函数是当第一个视图和文档关联之后被调用)(虚函数)

//而且它在第一次调用OnDraw的调用之前

void CGraphicView::OnInitialUpdate()

{

       CScrollView::OnInitialUpdate();

       // TODO: Add your specialized code here and/or call the base class

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

}

 

//?当移动到窗口下端划线,窗口重绘时所画的线条位置发生了改变

//坐标点并没有发生改变

//但我们作图的时候使用的是逻辑坐标,windows需要将逻辑坐标改变为设备坐标来输出图形

//而OnPrepareDC(&dc)用来调整显示上下文的属性

//可能就是它改变了上下文属性

//在MFC中查找OnPrepareDC函数(viewscrl.cpp)(先搜索CSrollView)

其中

       switch (m_nMapMode)

       {

       case MM_SCALETOFIT:

              pDC->SetMapMode(MM_ANISOTROPIC);

              pDC->SetWindowExt(m_totalLog); // window is in logical coordinates

              pDC->SetViewportExt(m_totalDev);

              if (m_totalDev.cx == 0 || m_totalDev.cy == 0)

                     TRACE0("Warning: CScrollView scaled to nothing.\n");

              break;

       default://设置了MM_TEXT就到这里

              ASSERT(m_nMapMode > 0);

              pDC->SetMapMode(m_nMapMode);

              break;

       }

       CPoint ptVpOrg(0, 0);       // assume no shift for printing

       if (!pDC->IsPrinting())//是否在打印中?不在打印中则执行

       {

              ASSERT(pDC->GetWindowOrg() == CPoint(0,0));

 

              // by default shift viewport origin in negative direction of scroll

              ptVpOrg = -GetDeviceScrollPosition();//返回值有符号

 

              if (m_bCenter)

              {

                     CRect rect;

                     GetClientRect(&rect);

 

                     // if client area is larger than total device size,

                     // override scroll positions to place origin such that

                     // output is centered in the window

                     if (m_totalDev.cx < rect.Width())

                            ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2;

                     if (m_totalDev.cy < rect.Height())

                            ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2;

              }

       }

       pDC->SetViewportOrg(ptVpOrg);//设置视口的原点

 

#视口和窗口原点的改变01:25?????????????????????

CDC中提供了两个成员函数函数SetViewportOrg和SetWindowOrg,用来改变视口和窗口的原点。(获取则为Get……) 

1#CMetaFileDC m_dcMetaFile;

2#m_dcMetaFile.Create();//参数为NULL则一个内存源文件被创建

3#把绘图时的dc都换成m_dcMetaFile

4#ONDRAW中使用close来返回一个源文件的句柄

 HMETAFILE hmetaFile;

 hmetaFile=m_dcMetaFile.Close();

 pDC->PlayMetaFile(hmetaFile);//播放

//再次创建一个源文件

 m_dcMetaFile.Create();

//在源文件dc中播放先前的源文件,从而保存了上次的操作

 m_dcMetaFile.PlayMetaFile(hmetaFile);

//删除源文件资源

 DeleteMetaFile(hmetaFile);

 

#把源文件保存到文件中

添加菜单上打开和保存的命令响应

×CopyMetaFile

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);

}

#打开文件

GetMetaFile××××××××××

//GetEnhMetaFile

void CGraphicView::OnFileOpen()

{

 // TODO: Add your command handler code here

 HMETAFILE hmetaFile;

 hmetaFile=GetMetaFile("meta.wmf");

 m_dcMetaFile.PlayMetaFile(hmetaFile);

 DeleteMetaFile(hmetaFile);

 Invalidate();

}

 

(2)使用兼容DC

#CDC m_dcCompatible;

#先判断兼容dc是否已经创建

 if(!m_dcCompatible.m_hDC)

 {

         m_dcCompatible.CreateCompatibleDC(&dc);

         CRect rect;

         GetClientRect(&rect); //客户区的大小

         CBitmap bitmap;

         //通过指定的宽和高来创建一个兼容位图

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

//如果要在兼容dc上绘图必须要选入一幅位图,导入的也行

         m_dcCompatible.SelectObject(&bitmap);

//m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);CreateCompatibleBitmap返回的位图对象只包含相应设备描述表中的位图的位图信息头,不包含颜色表和象素数据块。因此,选入该位图对象的设备描述表不能像选入普通位图对象的设备描述表一样应用,必须在SelectObject函数之后,调用BitBlt将原始设备描述表的颜色表及象素数据块拷贝到兼容设备描述表。

         m_dcCompatible.SelectObject(pBrush);

 }

同样把dc换成m_dcCompatible,由于是内存中dc,同样操作时是看不见的

OnDraw中贴图:

 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、付费专栏及课程。

余额充值