内存DC


在使用vc开发图形相关的应用程序时,常常需要使用MFC的CDC类直接把图形画在窗口上。这通常是通过响应windows的WM_PAINT消息实现的。如果要画的图形比较复杂,或者比较大,那么画图过程可能会造成窗口的闪烁。当窗口调整大小时,这种闪烁由为明显。
 
解决窗口闪烁问题的有效办法就是使用内存DC,也称为缓冲DC。在内存中准备一个和窗口DC相同属性的DC,在这个内存DC上执行画图操作。完成画图以后,把画图输出的内容整体复制到目标窗口DC上。因为画图操作不在窗口DC上进行,所以在画图的过程中窗口可以保持原来的内容。当画好的内容被复制到窗口DC时,因为复制操作执行的非常快,所以用户感觉窗口仿佛被立刻被画好,从而消除了从旧画面到白板再到新画面的闪烁现象。
 
生成内存DC主要用到以下四个函数:
 
CreateCompatibleDC(CDC* pDC )。CDC类的成员函数,用于创建一个和pDC指向的DC兼容的内存DC。
 
CreateDiscardableBITmap( CDC* pDC, int nWidth, int nHeight)。CBitmap类的成员函数,用于按指定尺寸创建一个和pDC指向的DC兼容的位图。    
 
SelectObject(CBitmap * pBitmap)。CDC类的成员函数,执行以后,所以在该DC上的图像输出都将被画到pBitmap指向的位图上。
 
BOOL BitBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )。CDC类的成员函数,用于从源DC(pSrcDC)复制一个矩形的图象到当前DC中。
   
对于一个窗口,我们可以用下面的代码来创建内存DC,在内存DC上输出,并最终复制到窗口DC上。

 
void  PaintWnd(CWnd  *  pWnd)
... {
     CDC 
*  pWndDC  =  pWnd -> GetWindowDC();
     CRect WndRect 
=  pWnd -> GetWindowRect();
     
     CDC MemDC;
     CBitMap MemBitmap;
     
     MemDC.CreateCompatibleDC(pWndDC);         
//  利用屏幕DC创建内存DC
     MemBitmap.CreateCompatibleBitmap(         //  利用屏幕DC创建兼容的位图
         pWndDC,
         WndRect.Width(),
         WndRect.Height());
 
     MemDC.SelectObject(&MemBitmap);            
//  让内存DC输出到位图
 
          //  使用MemDC画图
          //  。。。。。。
 
    pWndDC
-> BitBlt(                                //  从内存DC复制到窗口DC
          0 , 0 ,
         WndRect.Width(),
         WndRect.Height(),
         
& MemDC, 
         
0 , 0 ,
         SRCCOPY);
}


 
当然,实际的情况下,我们需要考虑的更多,因为内存DC、位图的创建都可能会失败。为了简化代码,笔者定义了一个类CMemoryDC,包装了内存DC创建过程中的出错处理,内存DC的事后清理等操作,并自动复制内存DC的内容到目标DC上。
声明CMemoryDC类的头文件MemoryDC.h如下:
 
#pragma  once
#include 
" Afxwin.h "
 
class  CMemoryDC
... {
public :
     CMemoryDC(CDC 
* dc, RECT  *  rect,bool  autoRender  =  false );
     
~ CMemoryDC(void );
 
     
bool  IsOK();
     
void  Render(CDC  *  p_objectDC  =  NULL);
     CDC
*  GetMemoryDC();
     
operator  CDC  *  ();
private :
     
bool  m_bAutoRender;
     CRect m_DCRect;
     CDC
*  m_pOriginalDC;
     CDC m_MemoryDC;
     CBitmap m_MemoryBmp;
}
;
 
类的实现文件CMemoryDC.cpp如下:
#include  " .MemoryDC.h "
 
CMemoryDC::CMemoryDC(CDC 
* dc, RECT  *  rect, bool  autoRender)
...    
     m_bAutoRender 
=  autoRender;
     m_pOriginalDC 
=  dc;
     
if  (dc == NULL  ||  rect == NULL)
         
return ;
     
if  ( ! m_MemoryDC.CreateCompatibleDC(dc))
         
return ;
     m_DCRect.SetRect(rect
-> left, rect -> top, rect -> right, rect -> bottom);   
     
if  ( ! m_MemoryBmp.CreateCompatibleBitmap(dc, m_DCRect.Width(), m_DCRect.Height()))
         
return ;
     m_MemoryDC.SelectObject(m_MemoryBmp);
}

 
CMemoryDC::
~ CMemoryDC(void )
... {
     
if  (m_bAutoRender)
         Render();
     
if  (m_MemoryDC.m_hDC != NULL)
         m_MemoryDC.DeleteDC();
     
if  (m_MemoryBmp.m_hObject != NULL)
         m_MemoryBmp.DeleteObject();
}

 
bool  CMemoryDC::IsOK()
... {
     
return  m_MemoryDC.m_hDC != NULL  &&  m_MemoryBmp.m_hObject  !=  NULL;
 
}

void  CMemoryDC::Render(CDC  *  p_objectDC)
... {
     
if  ( ! IsOK())
         
return ;
 
     CDC 
*  pDC  =  (p_objectDC == NULL  ?  m_pOriginalDC p_objectDC);
     CSize Size 
=  m_MemoryDC.GetViewportExt() ;
     pDC
-> BitBlt(
         m_DCRect.left, 
         m_DCRect.top,
         m_DCRect.Width(),
         m_DCRect.Height(),
         
& m_MemoryDC, 
         
0 , 0 ,
         SRCCOPY);
}

CDC
*  CMemoryDC::GetMemoryDC()
... {
     
return   &  m_MemoryDC;
}

CMemoryDC::
operator  CDC  *  ()
... {
     
return   &  m_MemoryDC;
}

使用这个类可以大大简化内存DC的创建操作。如果我们在窗口消息WM_PAINT的响应函数中使用内存DC,只要用如下这样简便的代码便可实现:
CRect Rect;
GetClientRect(Rect);
CPaintDC dc(
this); // device context for painting

     CMemoryDC MemDC(&dc, Rect, true);
     
if
 (MemDC.IsOK())
     
...
{
         
// 使用MemDC画窗口

}    
//
 MemDC析构时会自动把图像复制到dc,无需其它操作
使用CMemoryDC创建内存DC防止窗口闪烁,编程的代码和不使用内存DC时相比,数量和复杂性几乎没有增加。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值