雙緩沖
1.雙緩沖原理
單緩沖:在paintEvent中直接繪制到屏幕,繪制過程被用戶看到
雙緩沖:先在redrawBuffer繪制到緩沖區,然后一次性顯示完整結果
代碼結構
單緩沖:所有繪制邏輯在paintEvent中
雙緩沖:繪制邏輯分離到redrawBuffer,paintEvent僅負責顯示緩沖區
性能影響
單緩沖:每次更新需要重復計算和繪制
雙緩沖:可以優化為只在必要時重繪緩沖區(如數據變化時)
減少的時間是重繪的那部分時間,單緩沖是逐個繪制,雙緩沖是整體繪制。
測試案例
1.Qt 實現繪制 5000 個小球移動,單緩沖和雙緩沖效果對比
雙緩沖和單緩沖效果對比
2.代碼
關鍵代碼對比:
單緩沖繪制: paintEvent 中通過 painter 繪制每一個 circle,一次 paintEvent 繪制 5000 次
雙緩沖繪制: paintEvent 中直接繪制提前賦好像素值的成員變量 buffer 中的像素
doublebufferwidget.h
#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QPixmap>
#include <QPointF>
#include <QVector>
#include <cmath>
#include <QTime>
#include <QDebug>class DoubleBufferWidget : public QWidget {Q_OBJECT
public:DoubleBufferWidget(QWidget *parent = nullptr) : QWidget(parent) {// 初始化緩沖區setWindowTitle("DoubleBuffer");buffer = QPixmap(size());buffer.fill(Qt::white);// 初始化動畫數據for (int i = 0; i < 5000; ++i) {circles.append({QPointF(rand() % width(), rand() % height()),5.f + rand() % 10,QColor(rand() % 256, rand() % 256, rand() % 256),QPointF((rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0)});}// 設置定時器更新動畫QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &DoubleBufferWidget::updateAnimation);timer->start(16);}void resizeEvent(QResizeEvent *event) override {buffer = QPixmap(size());buffer.fill(Qt::white);QWidget::resizeEvent(event);}void paintEvent(QPaintEvent *event) override {QTime doublePaint;doublePaint.start();Q_UNUSED(event);QPainter painter(this);painter.drawPixmap(0, 0, buffer);qInfo() << "void paintEvent(QPaintEvent *event) doublePaint cost times:" << doublePaint.elapsed() << "ms";}private slots:void updateAnimation() {// 更新所有圓形位置for (auto &circle : circles) {circle.pos += circle.velocity;if (circle.pos.x() - circle.radius < 0 || circle.pos.x() + circle.radius > width())circle.velocity.setX(-circle.velocity.x());if (circle.pos.y() - circle.radius < 0 || circle.pos.y() + circle.radius > height())circle.velocity.setY(-circle.velocity.y());}// 重新繪制到緩沖區redrawBuffer();update();}private:struct Circle {QPointF pos;qreal radius;QColor color;QPointF velocity;};QPixmap buffer;QVector<Circle> circles;void redrawBuffer() {buffer.fill(Qt::white);QPainter bufferPainter(&buffer);bufferPainter.setRenderHint(QPainter::Antialiasing);// 模擬長時間繪制過程for (int i = 0; i < 1000; ++i) {double temp = std::sin(i);Q_UNUSED(temp);}// 繪制所有圓形for (const auto &circle : circles) {bufferPainter.setPen(Qt::NoPen);bufferPainter.setBrush(circle.color);bufferPainter.drawEllipse(circle.pos, circle.radius, circle.radius);}}
};
singlebufferwidget.h
#include <QWidget>
#include <QTime>
#include <QPainter>
#include <QTimer>
#include <math.h>
#include <qmath.h>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class SingleBufferWidget; }
QT_END_NAMESPACEclass SingleBufferWidget : public QWidget {Q_OBJECT
public:SingleBufferWidget(QWidget *parent = nullptr) : QWidget(parent) {// 初始化動畫數據for (int i = 0; i < 5000; ++i) {circles.append({QPointF(rand() % width(), rand() % height()),5.f + rand() % 10,QColor(rand() % 256, rand() % 256, rand() % 256),QPointF((rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0)});}// 設置定時器更新動畫QTimer *timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &SingleBufferWidget::updateAnimation);timer->start(16);}void paintEvent(QPaintEvent *event) override {QTime SingpaintTimer;SingpaintTimer.start();Q_UNUSED(event);QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 模擬長時間繪制過程for (int i = 0; i < 1000; ++i) {// 這個循環會延長繪制時間,加劇閃爍現象double temp = std::sin(i);Q_UNUSED(temp);}// 繪制所有圓形for (const auto &circle : circles) {painter.setPen(Qt::NoPen);painter.setBrush(circle.color);painter.drawEllipse(circle.pos, circle.radius, circle.radius);}qInfo() << "void paintEvent(QPaintEvent *event) SingpaintTimer cost times:" << SingpaintTimer.elapsed() << "ms";}private slots:void updateAnimation() {// 更新所有圓形位置for (auto &circle : circles) {circle.pos += circle.velocity;if (circle.pos.x() - circle.radius < 0 || circle.pos.x() + circle.radius > width())circle.velocity.setX(-circle.velocity.x());if (circle.pos.y() - circle.radius < 0 || circle.pos.y() + circle.radius > height())circle.velocity.setY(-circle.velocity.y());}update();}private:struct Circle {QPointF pos;qreal radius;QColor color;QPointF velocity;};QVector<Circle> circles;
};