双缓冲解决控制台应用程序输出“闪屏”(C/C++,Windows)

使用 C 语言编写游戏的小伙伴们想必起初都要遇到这样的问题,在不断清屏输出数据的过程中,控制台中的输出内容会不断地闪屏。出现这个问题的原因是程序对数据处理花掉的时间影响到了数据显示,或许你可以使用局部覆盖更新方法(减少更新数据量)来缓解闪屏,但是这种方法并不适用于所有场合,尤其是更新数据本身就非常大的场合。

  本文将讲述解决控制台应用程序输出闪屏的终级解决方法——双缓冲。

问题呈现

  下面的代码演示了在高速不断清屏输出数据的过程的闪屏问题,特邀您一试:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#include <stdio.h>

int main()

{

    while (1)

    {

        for (char c=‘a‘; c<‘z‘; c++)

        {

            system("cls");

            for (int i=0; i<800; i++)

            {

                printf("%c",c);

            }

        }

    }

}

不完全解决方案:局部覆盖更新

  本例代码将使用两个 Win32 API 函数,GetStdHandle、SetConsoleCursorPosition,

图例 名称 说明
HANDLE GetStdHandle(
  _In_  DWORD nStdHandle
);
获取标准设备句柄


nStdHandle 标准设备,可取值:
STD_INPUT_HANDLE (DWORD)-10,输入设备
STD_OUTPUT_HANDLE (DWORD)-11,输出设备
STD_ERROR_HANDLE (DWORD)-12,错误设备


调用返回:
成功,返回设备句柄(HANDLE);
失败,返回 INVALID_HANDLE_VALUE;
如果没有标准设备,返回 NULL。
BOOL SetConsoleCursorPosition(
  _In_  HANDLE hConsoleOutput,
  _In_  COORD dwCursorPosition
);
设置控制台光标位置


hConsoleOutput 控制台输出设备句柄
dwCursorPosition 光标位置

函数参数中使用到 COORD 结构体:

图例 名称 说明
X
SHORT X;
水平坐标或列值,从 0 开始
Y
SHORT X;
垂直坐标或行值,从 0 开始

  示例代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include <stdio.h>

#include <Windows.h>

int main()

{

    HANDLE hOutput;

    COORD coord={0,0};

    hOutput=GetStdHandle(STD_OUTPUT_HANDLE);

    while (1)

    {

        for (char c=‘a‘; c<‘z‘; c++)

        {

            SetConsoleCursorPosition(hOutput, coord);

            for (int i=0; i<800; i++)

            {

                printf("%c",c);

            }

        }

    }

}

闪屏原因及解决方案

  首先,要说明的是,只有“显示缓存区”里面的数据才会被显示。默认的控制台应用程序的显示结构是这样的:

  在输出大量数据的时候,由于数据经过处理需要时间,导致数据到达显示缓存区时出现了先后顺序。即是说显示器在显示数据时,可能只有部分显示数据到达了显示缓存区,而其他数据还没有到达,从而使图像按部分呈现最终显示完整。这是更新大量显示数据出现闪屏的根本原因。

完全解决方案:使用双缓冲技术

  在图形处理编程过程中,双缓冲是基本技术之一,它是解决闪屏的有效解决方案。尤其在游戏编程领域,双缓冲技术得到了广泛地应用。

  如此看来,看似揪心的问题,其实我们只需要多一个缓冲区就可以完全解决这个问题。如果应用了双缓冲技术,那么这个控制台程序的结构将会有点变化:

  由于默认的缓冲区有标准输入输出流的支持,所以为了输入输出的方便,我们将默认的显示缓冲区作为后台缓冲区,而将新建的显示缓冲区作为活动的屏幕显示。基本过程是,先将要显示的数据传输到默认缓冲区,等到数据全部写入后,再一次性填充到新建的显示缓存区。

  为了实现这个过程,我们还需要调用几个 Win32 API(CreateConsoleScreenBuffer、SetConsoleActiveScreenBuffer、SetConsoleCursorInfo、ReadConsoleOutputCharacterA、WriteConsoleOutputCharacterA),

图例 名称 说明
HANDLE WINAPICreateConsoleScreenBuffer(
  _In_        DWORD dwDesiredAccess,
  _In_        DWORD dwShareMode,
  _In_opt_    const SECURITY_ATTRIBUTES *lpSecurityAttributes,
  _In_        DWORD dwFlags,
  _Reserved_  LPVOIDlpScreenBufferData
);
创建控制台显示缓冲


dwDesiredAccess,控制台缓冲安全与访问权限,可取值:
GENERIC_READ (0x80000000L),读权限
GENERIC_WRITE (0x40000000L),写权限


dwShareMode,共享模式,可取值:
FILE_SHARE_READ,读共享
FILE_SHARE_WRITE,写共享


lpSecurityAttributes,安全属性,NULL


dwFlags,缓冲区类型,仅可选:CONSOLE_TEXTMODE_BUFFER,控制台文本模式缓冲


lpScreenBufferData,保留,NULL
BOOL WINAPISetConsoleActiveScreenBuffer(
  _In_  HANDLE hConsoleOutput
);
设置控制台活动显示缓冲


hConsoleOutput,控制台输出设备句柄
BOOL WINAPISetConsoleCursorInfo(
  _In_  HANDLE hConsoleOutput,
  _In_  const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);
设置控制台光标信息


hConsoleOutput,控制台输出设备句柄
lpConsoleCursorInfo,光标信息(大小、可见性)
BOOL WINAPIReadConsoleOutputCharacterA(
  _In_  HANDLE hConsoleOutput,
  _Out_ LPTSTR lpCharacter,
  _In_  DWORD nLength,
  _In_  COORD dwReadCoord,
  _Out_ LPDWORDlpNumbersOfCharsRead
);
读取控制台输出到字符数组


hConsoleOutput,控制台输出设备句柄
lpCharacter,保存的字符数组指针
nLength,读取长度dwReadCoord,读取起始坐标lpNumbersOfCharsRead,实际读取长度
BOOL WINAPIWriteConsoleOutputCharacterA(
  _In_  HANDLE hConsoleOutput,
  _In_ LPTSTR lpCharacter,
  _In_  DWORD nLength,
  _In_  COORD dwWriteCoord,
  _Out_ LPDWORDlpNumberOfCharsWritten
);
写入字符数组到控制台输出


hConsoleOutput,控制台输出设备句柄
lpCharacter,写入的字符数组指针
nLength,写入长度dwWriteCoord,写入起始坐标lpNumberOfCharsWritten,实际写入长度

函数参数中使用到 CONSOLE_CURSOR_INFO 结构体:

图例 名称 说明
dwSize
DWORD dwSize;
光标大小,在范围 1 到 100 中取值。
bVisible
BOOL bVisible;
可见性,可取值:
FALSE,0,不可见;TRUE,1,可见。

  示例代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

#include <stdio.h>

#include <Windows.h>

int main(){

    //获取默认标准显示缓冲区句柄

    HANDLE hOutput;

    COORD coord={0,0};

    hOutput=GetStdHandle(STD_OUTPUT_HANDLE);

    //创建新的缓冲区

    HANDLE hOutBuf = CreateConsoleScreenBuffer(

        GENERIC_READ | GENERIC_WRITE,

        FILE_SHARE_READ | FILE_SHARE_WRITE,

        NULL,

        CONSOLE_TEXTMODE_BUFFER,

        NULL

    );

    //设置新的缓冲区为活动显示缓冲

    SetConsoleActiveScreenBuffer(hOutBuf);

    //隐藏两个缓冲区的光标

    CONSOLE_CURSOR_INFO cci;

    cci.bVisible=0;

    cci.dwSize=1;

    SetConsoleCursorInfo(hOutput, &cci);

    SetConsoleCursorInfo(hOutBuf, &cci);

    //双缓冲处理显示

    DWORD bytes=0;

    char data[800];

    while (1)

    {

        for (char c=‘a‘; c<‘z‘; c++)

        {

            system("cls");

            for (int i=0; i<800; i++)

            {

                printf("%c",c);

            }

            ReadConsoleOutputCharacterA(hOutput, data, 800, coord, &bytes);

            WriteConsoleOutputCharacterA(hOutBuf, data, 800, coord, &bytes);

        }

    }

    return 0;

}

时间: 2024-10-09 07:25:20

双缓冲解决控制台应用程序输出“闪屏”(C/C++,Windows)的相关文章

vue cavnas绘制矩形,并解决由clearRec带来的闪屏问题

起因:在cavnas绘制矩形时 鼠标移动一直在监测中,所以鼠标移动的轨迹会留下一个个的矩形框, 要想清除矩形框官方给出了ctx.clearRect() 但是这样是把整个画布给清空了,因此需要不断 向画布展示新的图片,这样就出现了不断闪屏的问题. 那么怎么解决呢? microsoft提供了双缓冲图形技术,可以点击看看这边文章. 具体就是画图的时候做两个 cavnas层,一个临时层 一个显示层,鼠标的监听事件放在显示层处理, 每次清空的时候只清空临时层,这样就可以解决闪屏问题了. 部分代码如下: <

非控制台应用程序输出信息到输出面板

1.Console.WriteLine(“输出到控制台窗口,即命令提示符窗口”);2.System.Diagnostics.Debug.WriteLine(“打印信息到输出窗口,但是只能在Debug版本运行,到了release版本中,Debug类的函数都会被忽略”);3.System.Diagnostics.Trace.WriteLine(“打印信息到输出窗口,可以同时在Debug和release版本运行”);注:第2.3点都必须在Debug模式下才能打印信息到输出窗口 原文地址:https:/

C# 控制台应用程序输出颜色字体

最佳解决方案的代码: 1 static void Main(string[] args) 2 { 3 Console.ForegroundColor = ConsoleColor.Green; 4 Console.WriteLine("Hello, color text!"); 5 Console.ForegroundColor = ConsoleColor.Red; 6 Console.WriteLine("Hello, color text!"); 7 Cons

C#解决MDI子窗体切换闪屏的方法

解决方法: 在主窗体任意位置添加以下代码: protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; return cp; } }

Createprocess控制台程序输出重定向

在Windows编程中,并非每一个应用程序都需要一个图形用户界面(GUI),很多情况下,我们可以编写一个控制台应用程序,这样程序更小,加载更快,传输时间也短,同时也丝毫不牺牲程序应有的功能.这种程序特别适合那些在后台运行的程序,比如压缩.杀毒.上传下载等等.如果我们的确需要在GUI执行这些程序,以完成某些比如类似于磁盘格式化的功能,我们可以在GUI程序中创建一个新的进程,调用这些已有的控制台应用程序,帮助完成这些功能.然而令人失望的是,我们每次加载这些控制台应用程序时,图形程序总会在加载的过程中

C#绘图双缓冲

C#绘图双缓冲 C#双缓冲解释: 简单说就是当我们在进行画图操作时,系统并不是直接把内容呈现到屏幕上,而是先在内存中保存,然后一次性把结果输出来,如果没用双缓冲的话,你会发现在画图过程中屏幕会闪的很厉害,因为后台一直在刷新,而如果等用户画完之后再输出就不会出现这种情况,具体的做法,其实也就是先创建一个位图对象,然后把内容保存在里面,最后把图呈现出来. GDI+的双缓冲问题 一直以来的误区:.net1.1 和 .net 2.0 在处理控件双缓冲上是有区别的. .net 1.1 中,使用:this.

双缓冲技术讲解

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:<手把手教你架构3D游戏引擎>电子工业出版社和<Unity3D实战核心技术详解>电子工业出版社等. CSDN视频网址:http://edu.csdn.net/lecturer/144 首先要搞清楚计算机运行原理,计算机载运行时是将将最大的任务分解成多个任务,然后一个接一个地执行. 一个典型的例子,每个游戏引擎必须解决的问题是渲染. 当游戏画出用户看到的世界时,比如

另类解决Win10游戏会闪屏的方法

在Win10系统中遇到游戏时闪屏另很多朋友很苦恼,可是又找不到解决方法,下面小编分享一个另类的解决方法,或许可以帮助你解决Win10下游戏时闪屏的问题,. 解决步骤: 1.打开游戏后先将游戏界面调整成"窗口化",或者"无边窗口化(全屏无边框)"; 2.任意打开一个另外的程序窗口,按组合键Win+Tab(不是Alt+Tab)切换,然后点击+添加桌面,添加一个"桌面2"; 3.接着将游戏窗口拖入"桌面2"里去; 4.然后点击&qu

设置 phoneGap/Cordova 3.4 应用程序启动动画闪屏 SplashScreen

当Cordova 程序打包并安装到手机中后,我们会发现启动程序时,会有数秒的黑屏现象,常见的解决方法则是设置闪屏后面. 这里以 Android 程序为例,介绍Cordova设置启动画面的方法. 1. 添加动画闪屏支持 打开cmd,进入项目目录下,执行命令: cordova plugin add org.apache.cordova.splashscreen 2. 制作启动画面图片 根据 platforms\android\res\ 目录下的不同目录下的图片大小,分别制作不同屏幕适应的 png 格