zoukankan      html  css  js  c++  java
  • VC++绘图时,利用双缓冲解决屏幕闪烁 转载

    最近做中国象棋,绘制界面时遇到些问题,绘图过程中屏幕闪烁,估计都会想到利用双缓冲来解决问题,但查了下网上双缓冲的资料,发现基本是MFC的,转化为VC++后,大概代码如下:

    void DrawBmp(HDC hDC, HBITMAP hBitmap)
    {
         HDC hdcMEM; //用于缓冲作图的内存DC
         HBITMAP bmp; //内存中承载临时图象的位图
         HANDLE hOld;
         hdcMEM = CreateCompatibleDC(hDC);//依附窗口DC创建兼容内存DC
         bmp = CreateCompatibleBitmap(hDC, 100, 100); //创建与hDC环境相关的设备兼容的位图
    
         SelectObject(hdcMEM, bmp);
         hOld = SelectObject(hdcImage, hBitmap);
    
         StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY);
    
         SelectObject(hdcImage, hOld);
         DeleteObject(hOld);
    }

    但以上代码似乎没有用到hBitmap,当然屏幕上也不会有任何输出,但网上的资料基本一样。查了一番资料,才明白如果hDC中已经有位图数据,BitBlt的时候,就会直接把hDC中的数据画到内存缓冲区里。所以,还需要建一DC,名为hdcImage,把要画的位图选入内存hdcImage中,然后再在内存缓冲区上绘图。

    整理代码如下:

    void DrawBmp(HDC hDC, HBITMAP hBitmap)
    {
         HDC hdcImage;
         HDC hdcMEM; //注意此处,创建了两个HDC
         hdcMEM = CreateCompatibleDC(hDC);
         hdcImage = CreateCompatibleDC(hDC);
         HBITMAP bmp = ::CreateCompatibleBitmap(hDC, nWidth, nHeight);//创建与hDC环境相关的设备兼容的位图
    
         SelectObject(hdcMEM, bmp);
    
         SelectObject(hdcImage, hBitmap);//注意此处,将要画的位图选入hdcImage
    
         StretchBlt(hdcMEM, 0, 0, 100, 100, hdcImage, 0, 0, 100, 100, SRCCOPY); //这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区
         StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY); //再将内存缓冲区中的数据绘制到屏幕上.
    
         DeleteObject(hdcImage);
    
    }

    当然,要注意的一点就是,如果要绘制多张图片,比如两张,如果大家这样调用:
    DrawBmp(hDC, hBitmap1);
    DrawBmp(hDC, hBitmap2);
    依然会发生闪烁,下面解释原因:
    举个例子,屏幕绘图就像现场作画,如果两次调用绘图函数,就相当于在观众面前作画,第一次画第一张(例如中国象棋的背景)。第二次画第二张(如棋盘)。这样,在画背景和棋盘时,由于颜色有反差,必然在贴第二张图时会发现闪烁,这样利用双缓冲相当于没用,还浪费了内存空间。

    双缓冲的原理是:在内存中先把第一张图画好,此时不要转画到屏幕上,然后继续在原来的内存中画第二张,等把所有的图全画好后,再一次性贴到屏幕上。那样内存中存在的就是完整的图形,观众看不到绘图的过程,只能看到绘图的结果,而最后是一次性复制到屏幕上的,当然不会发生闪烁现象。

    为了更好解释双缓冲的原理,附图片如下:

     

    PS:以上照片来自网络,只为能更好理解,本人无意侵权。

    在以上代码的基础上作如下更改:

    void DrawBmp(HDC hDC, HBITMAP hBitmap) //此处返回类型改为HDC
    {
         HDC hdcMEM;
         hdcMEM = CreateCompatibleDC(hDC);
    
         SelectObject(hdcMEM, hBitmap); //将位图选择进内存DC
    
         StretchBlt(hDC, 0, 0, 100, 100, hdcMEM, 0, 0, 100, 100, SRCCOPY);//这里才能正常画图,将hdcImage中的位图直接复制到内存缓冲区
    
         DeleteObject(hdcMEM);
    }

    调用以上函数在内存中画第一张图:
    DrawBmp(hdcTmp , hBitmap1);
    画第二张图
    DrawBmp(hdcTmp, hBitmap2); //此时传的为hdcTmp,其中hdcTmp中已经有第一张图片的数据,此次调用后就会把第二张图片绘到原来的基础上。

    如果要画多张图,就依次调用本函数绘制,记得一定要把所有的图全画到一个设备DC上,最后再一次性画到屏幕上,才不会出现闪烁现象。
    等把所有图全画到hdcTmp中后,hdcTmp中已经有了完整的图形,再把完整的图形绘制到屏幕上:
    BitBlt(hDC, 0, 0, 100, 100, hdcTmp, 0, 0, SRCCOPY); //此处第一个参数才为hDC,即窗口句柄

    至此,双缓冲画多幅图绘制完毕。

  • 相关阅读:
    Android : iperf-2.0.4 网络测试工具
    Android : SELinux 简析&修改
    Android : 修改内核源码 and 编译、打包成新的boot.img
    Bluedroid: 音频数据的传输流程
    Bluedroid: 蓝牙协议栈源码剖析
    【Java】 剑指offer(54) 二叉搜索树的第k个结点
    【Java】 剑指offer(53-3) 数组中数值和下标相等的元素
    【Java】 剑指offer(53-2) 0到n-1中缺失的数字
    【Java】 剑指offer(53-1) 数字在排序数组中出现的次数
    【Java】 剑指offer(52) 两个链表的第一个公共结点
  • 原文地址:https://www.cnblogs.com/lujin49/p/4704831.html
Copyright © 2011-2022 走看看