2007-04-03 17:16
内存DC是一种提高绘图效率,避免屏幕闪烁的好办法,几乎所有的绘图都必须使用到内存DC,可是在MSDN里一切都不是那么明显.
为了为以后的游戏制作更加顺利,我研究了一下内存DC的标准用法,然后贴在这里也是一个备忘.
其实所有的内存DC的原理都像下图所示:
要装载的图片 背景 蒙板 人物
| | |
| /
| /
| /
| /
+------------+
| 暂存DC1 |
+------|-----+
暂存DC2
|
操作窗口
简单解释一下这个图片,这里的两个暂存DC都是内存DC,他们的作用不同,第一个暂存DC是用来载入图片,而第二个则是当前屏幕在内存中的一个镜像(所有 的显示处理都先在这里处理),这就是所谓内存DC的原理了.
我们来看代码,首先我们在类的头文件中声明所有要使用的类,比如内存DC和位图.
Class CMyView
{
public:
//内存DC(主DC,临时DC(用来装载位图))
CDC *m_PrimaryMemDC;
CDC *m_TempMemDC;
//位图对象
CBitmap *m_bgMap; //背景
CBitmap *m_bitMap; //人物
CBitmap *m_tempMap; //临时
CRect m_rect;
}
然后便是在类的构造函数中建立对象,代码一目了然,我就不多说了.
CMemDCTestView::CMemDCTestView()
{
m_TempMemDC = new CDC;
m_PrimaryMemDC = new CDC;
m_bitMap = new CBitmap;
m_tempMap = new CBitmap;
m_bgMap = new CBitmap;
}
之后在OnCreate函数中初始化这些对象(注意不是构造函数,因为构造的时候还没有DC),OnCrate函数默认在View类中是没有的,所以你要 手动加入.
CMyView::OnCrate(……)
{
CClientDC dc(this);
//创建与内存兼容的DC
m_PrimaryMemDC->CreateCompatibleDC(&dc);
m_TempMemDC->CreateCompatibleDC(&dc);
//创建兼容位图
m_tempMap->CreateCompatibleBitmap(&dc,1024,768);
//装载位图
HBITMAP bitmap = (HBITMAP)::LoadImage(NULL,"pic/bgmap.bmp",
IMAGE_BITMAP,1024,768,
LR_LOADFROMFILE);
m_bgMap->Attach(bitmap);
bitmap = (HBITMAP)::LoadImage(NULL,"pic/AppExit_enu.bmp",IMAGE_BITMAP,61,67,
LR_LOADFROMFILE);
m_bitMap->Attach(bitmap);
//载入位图
m_PrimaryMemDC->SelectObject(m_tempMap);
return 0;
}
我们看到这里主要做的工作是创建和窗口DC相兼容的内存DC,然后是载入位图.
提醒一点就是创建兼容位图,然后把他载入内存DC中这个过程的理解,我是这么理解的只有你的DC中已经有了一张图片,然后才能够往上面贴图片.就像我们画 画的时候先要有一张白纸,然后才能画画.
还是拿画画做例子,再解释另外一个东西,使用SelectObject函数可以看成是换画纸,而BitBlt函数我们就可以看作是把其他画纸上的东西复印 到现在的这个画纸上,所以你的DC必须先有画纸才能用BitBlt.这也就是为什么我刚才试了很多次都不能BitBlt成功的原因.
再往下看,当然到了最关键的OnDraw函数了。
void CMemDCTestView::OnDraw(CDC* pDC)
{
CMemDCTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
GetClientRect(&m_rect);
// TODO: add draw code for native data here
BITMAP map;
m_bgMap->GetBitmap(&map);
m_TempMemDC->SelectObject(m_bgMap);
m_PrimaryMemDC->BitBlt(0,0,map.bmWidth,map.bmHeight,m_TempMemDC,0,0,SRCCOPY);
//m_PrimaryMemDC->TextOut(0,0,"Hello");
m_bitMap->GetBitmap(&map);
m_TempMemDC->SelectObject(m_bitMap);
m_PrimaryMemDC->BitBlt(0,0,map.bmWidth,map.bmHeight,m_TempMemDC,0,0,SRCCOPY);
pDC->BitBlt(0,0,m_rect.Width(),m_rect.Height),m_PrimaryMemDC,0,0,SRCCOPY);
}
在这个函数里我们首先定义了一个数据结构BITMAP通过CBitmap::GetBitmap来得到图像的相关信息(主要是长和宽),之后我们把要载入 的第一幅位图载入暂存DC1(m_TempMemDC)中,之后再把它贴到暂存DC2(m_PrimaryMemDC)上。(注意这时的暂存DC2已经有 一个默认的位图选入了,否则贴图操作不能成功)下面的步骤的重复的贴第二张图片,以此类推,最后把暂存DC2的东西贴到窗口DC上,我猜这个窗口DC一定 在程序创建的时候已经选入一张位图了(不然无法BitBlt)。
好了,这就是内存DC绘图的标准用法,只是繁琐,并不是很难,大家用的时候一定要细心再细心,特别是对于选择默认位图的部分,很容易遗漏。