轻松消除贴图闪烁
//========================================================================
//TITLE:
// 轻松消除贴图闪烁
//AUTHOR:
// norains
//DATE:
// Thursday 25-December -2006
//Environment:
// EVC4.0 + Standard SDK
//========================================================================
有没有在程序中尝试过贴图?我们都有这么一个经验,如果直接往屏幕贴图,一张图我们觉得显示正常流畅;两张图,如果图片不大,并且速度也够快的话,我们也觉得没问题;但如果,有一张很大的位图,并且我们需要在位图上面输出文本,甚至在这张位图上面还要再贴另外的位图----可能是两张,也可能是十张,甚至是百张----那展现在我们面前的将是什么呢?我们将看到,屏幕上位图缓慢地一张一张出现,造成一种让人烦躁的闪烁效果.
那有没有办法实现上面所需的功能,并且又不显得闪烁呢?方法当然有,并且也极为简单.一句话,就是把所有的数据往内存里写,然后再把内存的数据显示到屏幕上.
好了,那现在让我们开始吧!
首先,让我们获取一个屏幕的DC.这个DC嘛,就是显示给我们看的屏幕的设备.
HDC hdc = GetDC(m_hWnd);
如果代码是用来响应WM_PAINT,那么这个DC句柄的获得应该使用另外一个函数:
HDC hdc = BeginPaint(m_hWnd,&ps);
嗯,接下来我们要做的就是,创建一个和设备DC相符合的内存DC.换句话说,就是开辟一块内存空间,并且这空间的大小足以用来表示设备DC的数据.
HDC hdcMem = CreateCompatibleDC(hdc);
内存分配完毕之后,我们需要告诉程序这内存是用来干什么的,也就是说,这内存的格式是什么.这和变量声明比较像,不过却更为复杂.
最初,我们需要创建一个符合目前设备DC的位图格式:
HBITMAP hBitmap = CreateCompatibleBitmap(hdc,IMG_MAINWND_WIDTH,IMG_MAINWND_HEIGHT);
最后就是把创建的这个位图和内存DC关联.
HGDIOBJ hOldSel = SelectObject(hdcMem,hBitmap);
好了,供贴图的内存已经准备好了,现在我们想干嘛就可以干嘛了.嗯,那我们还是赶快尝试一把吧.好,就先从位图绘制开始吧.
由于位图的绘制比较复杂,并且其数据必须要和内存关联,然后才能读取位图的数据.所以我们第一步还是需要先建立一个适合设备DC的位图DC:
HDC hdcBmp = CreateCompatibleDC(hdc);
读取位图数据:
HANDLE hBmp= LoadImage(m_hInst,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0);
将位图数据和位图DC关联起来:
HGDIOBJ hOldBmpSel = SelectObject(hdcBmp,hBmp);
将位图数据复制到内存DC中:
BitBlt(hdcMem,0,0,IMG_MAINWND_WIDTH,IMG_MAINWND_HEIGHT,hdcBmp,0,0,SRCCOPY);
好了,现在位图已经绘制到内存去了,看起来也不是很复杂嘛!
如果我们想在内存中输出文本,该怎么做呢?呵呵,这比绘制位图要简单多了:
DrawText(hdcMem,TEXT("输出文本"),-1,&rc,DT_LEFT);
嗯,就是这样,仅仅一条语句.如果还想让输出的文本背景透明,并且也想改变字体的颜色,在绘图前我们之需要添加以下语句即可:
COLORREF oldColor;
SetBkMode(hdcMem,TRANSPARENT);
oldColor = SetTextColor(hdcMem,RGB(40,96,170));
当然,绘制完毕之后,把原来的颜色还原是一个好习惯:
SetTextColor(hdcMem,oldColor);
做到现在,是不是已经迫不及待地想将成果显示到屏幕上了呢?好,那就让我们看看成果吧:
BitBlt(hdc,rc.left,rc.top,(rc.right - rc.left),(rc.bottom - rc.top),hdcMem,rc.left,rc.top,SRCCOPY);
怎么样?是不是显示得非常流畅,完全没有闪烁的现象?
做到这里,是不是非常高兴,就想着拿这段代码去应用了?等等,先别急,我们还需要清除一下我们狂欢后留下的垃圾,否则,系统这家伙就要生气,说不定就会来个蓝屏当机哦!
SelectObject(hdcBmp,hOldBmpSel);
SelectObject(hdcMem,hOldSel);
DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(m_hWnd,hdc); //如果是响应WM_PAINT消息,这里应改成:EndPaint(m_hWnd,&ps);
如果我们想做得更完美一些,甚至可以不用响应WM_ERASEBKGND消息.因为WM_ERASEBKGND会占用一帧来绘制.所以我们需要做的是,只要检测到WM_ERASEBKGND消息,直接返回.
附录1.
无闪烁的完整代码(其中的一些未知变量,窗口句柄需根据实际情况更改):
HDC hdc = GetDC(m_hWnd);
//Create a DC that matches the device
HBITMAP hBitmap = CreateCompatibleBitmap(hdc,IMG_MAINWND_WIDTH,IMG_MAINWND_HEIGHT);
HDC hdcMem = CreateCompatibleDC(hdc);
//Select the bitmap into to the compatible device context
HGDIOBJ hOldSel = SelectObject(hdcMem,hBitmap);
//Create a DC that matches the device
HDC hdcBmp = CreateCompatibleDC(hdc);
//Load the bitmap
HANDLE hBmp= LoadImage(m_hInst,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0);
//Select the bitmap into to the compatible device context
HGDIOBJ hOldBmpSel = SelectObject(hdcBmp,hBmp);
//Copy the bitmap image to the memory DC
BitBlt(hdcMem,0,0,IMG_MAINWND_WIDTH,IMG_MAINWND_HEIGHT,hdcBmp,0,0,SRCCOPY);
COLORREF oldColor;
SetBkMode(hdcMem,TRANSPARENT);
oldColor = SetTextColor(hdcMem,RGB(40,96,170));
DrawText(hdcMem,lpcszText,-1,&m_rcInfoArea,DT_LEFT);
SetTextColor(hdcMem,oldColor);
BitBlt(hdc,m_rcInfoArea.left,m_rcInfoArea.top,(m_rcInfoArea.right - m_rcInfoArea.left),(m_rcInfoArea.bottom - m_rcInfoArea.top),hdcMem,m_rcInfoArea.left,m_rcInfoArea.top,SRCCOPY);
//Restore original bitmap selection and destroy the memory DC
SelectObject(hdcBmp,hOldBmpSel);
SelectObject(hdcMem,hOldSel);
DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(m_hWnd,hdc);
附录2
会闪烁的代码
InvalidateRect(m_hWnd,&m_rcInfoArea,TRUE);
HDC hdc = GetDC(m_hWnd);
//Create a DC that matches the device
HDC hdcBmp = CreateCompatibleDC(hdc);
//Load the bitmap
HANDLE hBmp= LoadImage(m_hInst,MAKEINTRESOURCE(IDB_MAINWND),IMAGE_BITMAP,0,0,0);
//Select the bitmap into to the compatible device context
HGDIOBJ hOldSel = SelectObject(hdcBmp,hBmp);
//Get the bitmap dimensions from the bitmap
BITMAP bmp;
GetObject(hBmp,sizeof(BITMAP),&bmp);
//Copy the bitmap image from the memory DC to the screen DC
BitBlt(hdc,m_rcInfoArea.left,m_rcInfoArea.top,(m_rcInfoArea.right - m_rcInfoArea.left),(m_rcInfoArea.bottom - m_rcInfoArea.top),hdcBmp,m_rcInfoArea.left,m_rcInfoArea.top,SRCCOPY);
//Restore original bitmap selection and destroy the memory DC
SelectObject(hdcBmp,hOldSel);
COLORREF oldColor;
SetBkMode(hdc,TRANSPARENT);
oldColor = SetTextColor(hdc,RGB(40,96,170));
DrawText(hdc,lpcszText,-1,&m_rcInfoArea,DT_LEFT);
SetTextColor(hdc,oldColor);
DeleteDC(hdcBmp);
ReleaseDC(m_hWnd,hdc);