问题标题: 双缓冲解决闪屏问题

0
1
已解决
薛乘志
薛乘志
初级启示者
初级启示者

众所周知,无论是system("cls")还是自己写的cls()函数,都有一个问题:屏幕显示区上下部分的输出时间不一致

经过我的一番查找及研究,我找出了最终极的解决方法:双缓冲

 

原理:

windows下的屏幕输出方式如图:

其中程序->屏幕缓冲区可能没有输出完,屏幕缓冲区->屏幕就已经发送了,导致输出了一半的界面出现在用户的眼前

再说一句,win下可以创建多个屏幕缓冲区,但是只有活动屏幕缓冲区的能发送信息给屏幕

所以,可以人为修改输出模式为:

 

代码:


#include <bits/stdc++.h>
#include <windows.h>

namespace DoubleBuffer {
    COORD Size;
    HANDLE buf1 = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE buf2 = CreateConsoleScreenBuffer(
                      GENERIC_READ | GENERIC_WRITE,
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                      NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
    HANDLE Nowhandle;
    DWORD written;
    void setsize(SHORT width, SHORT height) {
        HANDLE hStdOutput = Nowhandle;
        SMALL_RECT wrt = {0, 0, short(width - 1), short(height - 1)};
        SetConsoleWindowInfo(hStdOutput, TRUE, &wrt);
        COORD coord = {width, height};
        SetConsoleScreenBufferSize(hStdOutput, coord);
    }
    HANDLE handle(DWORD type) {
        if (type == STD_OUTPUT_HANDLE) {
            return Nowhandle;
        } else {
            return GetStdHandle(type);
        }
    }
    void print(const char* format, ...) {
        va_list args;
        va_start(args, format);
        char tmp_str[1024];
        size_t sizes = vsprintf(tmp_str, format, args);
        WriteConsoleA(Nowhandle, tmp_str, sizes, &written, NULL);
        va_end(args);
    }
    bool nowbuf = true; //true:buf1 | false:buf2
    void swap_buffer() {
        nowbuf = !nowbuf;
        if (nowbuf) {
            Nowhandle = buf1;
        } else {
            Nowhandle = buf2;
        }
        setsize(Size.Y, Size.X);
    }
    void print_buffer() {
        SetConsoleActiveScreenBuffer(Nowhandle);
    }
    void _initbuf(short SizeX, short SizeY) { //SizeX为高、SizeY为宽
        DoubleBuffer::Size.X = SizeX, DoubleBuffer::Size.Y = SizeY;
        DoubleBuffer::swap_buffer();
    }
    void _stopbuf() {
        Nowhandle = buf1;
        SetConsoleActiveScreenBuffer(buf1);
    }
#define endl '\n'
    class stdio_output {
        public:
#define _output(T, F) stdio_output operator<<(T data) {print(F, data); return *this;}
            _output(const bool, "%d");
            _output(const int, "%d");
            _output(const short, "%d");
            _output(const long long, "%d");
            _output(const long int, "%d");
            _output(const unsigned int, "%u");
            _output(const unsigned short, "%u");
            _output(const unsigned long long, "%u");
            _output(const unsigned long int, "%d");
            _output(const float, "%g");
            _output(const double, "%g");
            _output(const char, "%c");
            _output(const char*, "%s");
            _output(const void*, "%p");
#undef _output
            stdio_output operator<<(std::string data) {
                WriteConsoleA(Nowhandle, data.c_str(), data.size(), &written, NULL);
                return *this;
            }
    };
    stdio_output _cout;
}

#define initbuf (DoubleBuffer::_initbuf)
#define GetStdHandle(nStdHandle) (DoubleBuffer::handle(nStdHandle))
#define printf (DoubleBuffer::print)
#define swapbuf (DoubleBuffer::swap_buffer)
#define printbuf (DoubleBuffer::print_buffer)
#define cout (DoubleBuffer::_cout)
#define stopbuf (DoubleBuffer::_stopbuf)

void clrscr() { //清屏
    HANDLE hdout = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hdout, &csbi);
    DWORD size = csbi.dwSize.X * csbi.dwSize.Y, num = 0;
    COORD pos = {0, 0};
    FillConsoleOutputCharacterA(hdout, ' ', size, pos, &num);
    FillConsoleOutputAttribute(hdout, 255, size, pos, &num );
    SetConsoleCursorPosition(hdout, pos);
}

int main() {
    initbuf(25, 80); //初始化双缓冲,并设置窗口高和宽
    while (1) {
        swapbuf(); //交换缓冲区

        clrscr(); //清屏
        //自己写输出信息的代码,可使用printf或cout,其余暂时无效

        printbuf(); //设为活动缓冲区
    }
    stopbuf(); //停止使用双缓冲
    return 0;
}

使用双缓冲的XACRAFT3.2与原版对比:


1
我要回答