Windows程序设计__孙鑫C++Lesson11《图形绘制及保存》

Windows程序设计__孙鑫C++Lesson11《图形绘制及保存》

本节要点:
1.指针数组方法保存图形
2.OnDraw函数的调用实质
3.坐标系的相关概念
4.SCrollView的使用及其中绘图出现问题的解决
5.图元文件(MetaFile)保存图形
6.兼容DC保存图形
1.指针数组方法保存图形
(1)新建CGraph类,添加public数据成员若干,用来保存图形绘制类型、起点和终点。
(2)利用MFC提供的CPtrArray来保存图形绘制对象数组。
(3)局部对象的指针用CPtrArray来保存无效,语句:
//CGraph graph(m_nDrawType,m_ptOrigin,point);
//m_ptrArray.Add(&graph);
执行后局部对象析构后保存的地址无效;解决方法,有new分配内存。
堆中内存分配由用户管理,用new在堆中分配的内存须由delete回收。
利用指针数组保存图形绘制和重绘过程代码如下:
//**************************************************************************
//step1 添加新类CGraph
class CGraph 
{
public:
CGraph();
CGraph(int nDrawType,CPoint ptOrigin,CPoint ptEnd);
virtual ~CGraph();
public:
int m_nDrawType;
CPoint m_ptOrigin;
CPoint m_ptEnd;
};
//**************************************************************************
//step2 添加CPtrArray成员变量到View类
CPtrArray m_ptrArray;
//**************************************************************************
//step3 绘制图形并将图形参数保存到CPtrArray
switch(m_nDrawType)
{
case 0:
     dc.SetPixel(point.x,point.y,RGB(255,0,0));
     break;
    case 1:
     dc.MoveTo(m_ptOrigin.x,m_ptOrigin.y);
     dc.LineTo(point.x,point.y);
     break;
case 2:
    dc.Rectangle(CRect(m_ptOrigin,point));
    break;
case 3:
    dc.Ellipse(CRect(m_ptOrigin,point));
       break;
default:
  break;
}
//设备坐标点与逻辑坐标的转换
OnPrepareDC(&dc);
dc.DPtoLP(&m_ptOrigin);
dc.DPtoLP(&point);
CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(pGraph);
//**************************************************************************
//step4 重绘图形
void CGraphicView::OnDraw(CDC* pDC)
{
int cnt=m_ptrArray.GetSize();
int index=0;
CBrush* pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=pDC->SelectObject(pBrush);
for(index=0;index<cnt;index++)
{  
  CGraph *pGraph=(CGraph *)m_ptrArray.GetAt(index);
  switch(m_nDrawType)
  {
   case 0:
       pDC->SetPixel(pGraph->m_ptEnd.x,pGraph->m_ptEnd.y,RGB(255,0,0));
       break;
   case 1:
       pDC->MoveTo(pGraph->m_ptOrigin.x,pGraph->m_ptOrigin.y);
       pDC->LineTo(pGraph->m_ptEnd.x,pGraph->m_ptEnd.y);
       break;
   case 2:
      pDC->Rectangle(CRect(pGraph->m_ptOrigin,pGraph->m_ptEnd));
      break;
   case 3:
      pDC->Ellipse(CRect(pGraph->m_ptOrigin,pGraph->m_ptEnd));
      break;
   default:
    break;
  }
}
     pDC->SelectObject(pOldBrush);
}
//**************************************************************************
2.OnDraw函数的调用实质
OnDraw是基类的响应WM_PAINT消息的OnPaint函数中调用OnDraw后来实现的,而CView::OnDraw是虚函数,子类有则调用子类,否则调

用基类。
(1)基类的WM_PAINT消息响应在CView::中利用CPaintDC获得DC。
函数实现为:
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);//CPaintDC是对BeginPaint和EndPaint的封装
OnPrepareDC(&dc);//准备DC
OnDraw(&dc);//调用基类的OnDraw,子类有则优先调用
}
(2)如果子类自己捕获的OnPaint 函数中没有调用OnDraw则程序不会进入到OnDraw函数,需要手动添加OnPrepareDC(&dc);OnDraw

(&dc);代码。
3.坐标系的相关概念
坐标系的概念比较多,需要仔细学习,这里仅提出关于逻辑坐标、设备坐标的概念。作图时逻辑坐标  Windows显示需要设备坐标


LPtoDP和DPToLP在设备坐标和逻辑坐标之间转换。
4.SCrollView的使用及其中绘图出现问题的解决
(1)CView 替换为CScrollView 需设定滚动大小
窗口创建之后第一个调用的函数OnInitialUpdate().
(2)认识了设备坐标和逻辑坐标之后,可以解决图形"漂移"情形,在CScrollView 中,保存图形的参数信息时调用如下:
//**************************************************************************
//设备坐标与逻辑坐标的转换
OnPrepareDC(&dc);
dc.DPtoLP(&m_ptOrigin);
dc.DPtoLP(&point);
//保存绘图参数
CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);
m_ptrArray.Add(pGraph);
//**************************************************************************
5.图元文件(MetaFile)保存图形
(1)CMetaFileDC的操作
创建 CMetaFileDC::Create
关闭 CMetaFileDC::Close
播放 CDC::PlayMetaFile 
保存 CopyMetaFile
获取 GetMetaFile或者GetEnhMetaFile
删除 DeleteMetaFile
(2)MetaFile的操作实现保存图形过程,代码如下:
//**************************************************************************
m_mfDC.Create();//step1 创建元文件DC
//**************************************************************************
//step2 利用MetaFileDC绘图操作
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
......
    switch(m_nDrawType)
{
case 0:
     m_mfDC.SetPixel(point.x,point.y,RGB(255,0,0));
     break;
     case 1:
     m_mfDC.MoveTo(m_ptOrigin.x,m_ptOrigin.y);
     m_mfDC.LineTo(point.x,point.y);
     break;
case 2:
    m_mfDC.Rectangle(CRect(m_ptOrigin,point));
    break;
case 3:
    m_mfDC.Ellipse(CRect(m_ptOrigin,point));
           break;
default:
    break;
}
}
//**************************************************************************
//step3  播放(重绘)图形
void CGraphicView::OnDraw(CDC* pDC)
{
HMETAFILE hMetaFile=m_mfDC.Close();
pDC->PlayMetaFile(hMetaFile);
m_mfDC.Create();//再次创建源文件
     m_mfDC.PlayMetaFile(hMetaFile);//继续绘制 相当于保存先前绘制
DeleteMetaFile(hMetaFile);//释放资源
}
//**************************************************************************
//step4 元文件保存
void CGraphicView::OnFileSave()
{
// TODO: Add your command handler code here
//保存绘制命令
HMETAFILE hMetaFile=m_mfDC.Close();
HMETAFILE hMetaFilesave=CopyMetaFile(hMetaFile,"meta.wmf");
m_mfDC.Create();//再次创建源文件
        m_mfDC.PlayMetaFile(hMetaFilesave);//继续播放 相当于保存先前绘制
DeleteMetaFile(hMetaFilesave);
}
//**************************************************************************
//step5 元文件打开
void CGraphicView::OnFileOpen()
{
// TODO: Add your command handler code here
HMETAFILE hMetaFile=GetMetaFile("meta.wmf");
m_mfDC.PlayMetaFile(hMetaFile);
DeleteMetaFile(hMetaFile);
        Invalidate();
}
//**************************************************************************
6.兼容DC保存图形
(1)兼容DC特点.
MSDN关于CreateCompatibleDC注解为:
Creates a memory device context that is compatible with the device specified by pDC. A memory device context is a

block of memory that represents a display surface. It can be used to prepare images in memory before copying them

to the actual device surface of the compatible device.
When a memory device context is created, GDI automatically selects a 1-by-1 monochrome stock bitmap for it. GDI

output functions can be used with a memory device context only if a bitmap has been created and selected into that

context.
意思为(个人译文训练):(该函数)创建一个与pDC所指定的设备相兼容的内存设备环境。一个内存设备环境就是一块代表了现实表面

的内存区。 这个内存区可以在向兼容设备实际拷贝图像之前被用来在内存中准备图像。
当一个内存设备环境被创建后,GDI自动为他选择一个1-1单色位图块。GDI输出函数可以用来操作内存DC仅当一个位图已经被创建

并且被选入这个内存DC。
(2)兼容DC创建
由上述(1)中内容可见创建兼容DC后必须为它准备一副位图,并选入其中。兼容DC在创建后以前是用LoadBitmap函数加载一副位图

到内存然后贴到兼容DC,这里不需要一副实际的位图,就使用兼容位图。但要注意兼容位图并不包含颜色表和像素数据块(这个具

体是什么,有待弄清楚),必须将当前设备的颜色表和象素数据块拷贝到兼容DC当中去。
(3)利用兼容DC保存图形的具体实现过程代码如下:
//**************************************************************************
//step1 添加变量
CDC m_dcCompatible;
//step2 创建兼容DC并利用兼容DC作图
//**************************************************************************
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
        CClientDC dc(this);
CBrush* pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
//CBrush *pOldBrush=dc.SelectObject(pBrush);
if(!m_dcCompatible.m_hDC)
{
     m_dcCompatible.CreateCompatibleDC(&dc);//创建兼容DC
     CBitmap bmp;
  CRect rect;
  GetClientRect(&rect);
  bmp.CreateCompatibleBitmap (&dc,rect.Width(),rect.Height());
        m_dcCompatible.SelectObject(&bmp);//选入兼容位图
        m_dcCompatible.SelectObject(pBrush);
m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);//选入当前DC的颜色表和象素数据块
}
//注意在兼容DC中作图是不会显示在客户区的 在OnDraw函数中实现图形的绘出
        switch(m_nDrawType)
{
case 0:
     m_dcCompatible.SetPixel(point.x,point.y,RGB(255,0,0));
     break;
    case 1:
     m_dcCompatible.MoveTo(m_ptOrigin.x,m_ptOrigin.y);
     m_dcCompatible.LineTo(point.x,point.y);
     break;
case 2:
    m_dcCompatible.Rectangle(CRect(m_ptOrigin,point));
    break;
case 3:
    m_dcCompatible.Ellipse(CRect(m_ptOrigin,point));
       break;
default:
  break;
}
}
//**************************************************************************
//step3 重绘图形
void CGraphicView::OnDraw(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);//贴图操作
}
//**************************************************************************
实验过程中运行效果如下图所示:


保存的wmf格式元文件在mspaint中打开效果如下图:

//**************************************************************************
本节小结:
1.掌握CptrArray数据结构的使用,推广到MFC其他数据结构的使用,善于利用类库提供的数据结构和类
2.掌握三种保存图形的方法,尤其是利用兼容DC的方法。
3.了解坐标系的概念,这部分内容比较难懂,还有待深入学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值