轻松消除贴图闪烁

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

posted @ 2006-12-25 22:43  我的一天  阅读(230)  评论(0编辑  收藏  举报