Qt之OpenGL实时显示图像

工作中在用Qt写界面程序时需要完成一项功能客户端和服务端连接成功后需要实时显示从服务端发送过来的图片,并可以用鼠标滚轮进行缩放以及拖拽。由于之前学习过些许OpenGL关于纹理贴图的技术,且Qt已集成OpenGL模块,因此打算用该技术完成。OpenGL显示图片使用GPU渲染,如果程序需要做到毫秒级的刷新频率,用该方法可以很大程度上缓解CPU的压力,图片的动态显示也更流畅。

下面我用一个demo程序简要记录下自己的使用方法,我会把代码都贴上来。我创建了一个简单的对话框应用程序,对话框放了一个widget控件,然后自己写了一个GL_Image类,继承自QGLWidget,实现了我需要完成的鼠标控制功能,同时为防止图片显示变形做了必要的等比例缩放,GL_Image类的对象将图片绘制在widget控件上。

也可以在将GL_Image类改成继承QOpenGLWidget的实现,修改很简单,见博客下方评论。

1、在pro文件加入Qt对OpenGL模块的支持,在QT+=的最后加上opengl

QT       += core gui opengl

2、GL_Image类

gl_image.h

#ifndef GL_IMAGE_H
#define GL_IMAGE_H

#include 
#include 
#include 

class GL_Image : public QGLWidget
{
    Q_OBJECT

public:
    enum
    {
        Left_Bottom_X,
        Left_Bottom_Y,
        Right_Bottom_X,
        Right_Bottom_Y,
        Right_Top_X,
        Right_Top_Y,
        Left_Top_X,
        Left_Top_Y,
        Pos_Max
    };

    GL_Image(QWidget* parent = nullptr);
    // 设置实时显示的数据源
    void setImageData(uchar* imageSrc, uint width, uint height);

protected:
    // 重写QGLWidget类的接口
    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);

    // 鼠标事件
    void wheelEvent(QWheelEvent* e);
    void mouseMoveEvent(QMouseEvent* e);
    void mousePressEvent(QMouseEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
    void mouseDoubleClickEvent(QMouseEvent* e);

private:
    uchar* imageData_;           //纹理显示的数据源
    QSize imageSize_;            //图片尺寸
    QSize adaptImageSize_;       //适配尺寸
    QSize Ortho2DSize_;          //窗口尺寸
    GLuint textureId_;           //纹理对象ID
    int vertexPos_[Pos_Max];     //窗口坐标
    float texturePos_[Pos_Max];  //纹理坐标
    bool dragFlag_;              //鼠标拖拽状态
    QPoint dragPos_;             //鼠标拖拽位置
    float scaleVal_;             //缩放倍率
};

#endif // GL_IMAGE_H

gl_image.cpp

#include "gl_image.h"

GL_Image::GL_Image(QWidget* parent):
    QGLWidget(parent)
{
    imageData_ = nullptr;
    dragFlag_ = false;
    scaleVal_ = 1.0;
}

// 设置待显示的数据源和尺寸
void GL_Image::setImageData(uchar* imageSrc, uint width, uint height)
{
    imageData_ = imageSrc;
    imageSize_.setWidth(width);
    imageSize_.setHeight(height);
}

void GL_Image::initializeGL()
{
    // 生成一个纹理ID
    glGenTextures(1, &textureId_);
    // 绑定该纹理ID到二维纹理上
    glBindTexture(GL_TEXTURE_2D, textureId_);
    // 用线性插值实现图像缩放
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}

// 窗口绘制函数
void GL_Image::paintGL()
{
    static bool initTextureFlag = false;
    // 设置背景颜色
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if(imageData_ == nullptr){
        return;
    }

    glBindTexture(GL_TEXTURE_2D, textureId_);

    if(!initTextureFlag)
    {
        // 生成纹理
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageSize_.width(), imageSize_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData_);

        // 初始化顶点坐标(居中显示)
        int x_offset = 0;
        int y_offset = 0;
        if(imageSize_.width()delta() > 0)
    {
        scaleVal_ += 0.1;
        scaleVal_ = scaleVal_>3? 3:scaleVal_;
    }
    else
    {
        scaleVal_ -= 0.1;
        scaleVal_ = scaleVal_<0.1? 0.1:scaleVal_;
    }

    uint16_t showImgWidth = adaptImageSize_.width() * scaleVal_;
    uint16_t showImgHeight = adaptImageSize_.height() * scaleVal_;

    int xoffset = (Ortho2DSize_.width() - showImgWidth)/2;
    int yoffset = (Ortho2DSize_.height() - showImgHeight)/2;

    vertexPos_[Left_Bottom_X] = xoffset;
    vertexPos_[Left_Bottom_Y] = yoffset;
    vertexPos_[Right_Bottom_X] = xoffset + showImgWidth;
    vertexPos_[Right_Bottom_Y] = yoffset;
    vertexPos_[Right_Top_X] = xoffset + showImgWidth;
    vertexPos_[Right_Top_Y] = yoffset + showImgHeight;
    vertexPos_[Left_Top_X] = xoffset;
    vertexPos_[Left_Top_Y] = yoffset + showImgHeight;

    paintGL();
}

// 实现鼠标拖拽图片,鼠标在拖拽过程中会反复调用此函数,因此一个连续的拖拽过程可以
// 分解为多次移动的过程,每次移动都是在上一个位置的基础上进行一次位置调节
void GL_Image::mouseMoveEvent(QMouseEvent* e)
{
    if (dragFlag_)
    {
        int scaledMoveX = e->x()-dragPos_.x();
        int scaledMoveY = e->y()-dragPos_.y();

        vertexPos_[Left_Bottom_X] += scaledMoveX;
        vertexPos_[Left_Bottom_Y] += scaledMoveY;
        vertexPos_[Left_Top_X] += scaledMoveX;
        vertexPos_[Left_Top_Y] += scaledMoveY;
        vertexPos_[Right_Top_X] += scaledMoveX;
        vertexPos_[Right_Top_Y] += scaledMoveY;
        vertexPos_[Right_Bottom_X] += scaledMoveX;
        vertexPos_[Right_Bottom_Y] += scaledMoveY;

        dragPos_.setX(e->x());
        dragPos_.setY(e->y());
        paintGL();
    }
}

void GL_Image::mousePressEvent(QMouseEvent* e)
{
    if(scaleVal_ > 0)
    {
        dragFlag_ = true;
        dragPos_.setX(e->x());
        dragPos_.setY(e->y());
    }
}

void GL_Image::mouseReleaseEvent(QMouseEvent* e)
{
    dragFlag_ = false;
}

// 双击实现原比例显示,缩放倍率设置为1.0
void GL_Image::mouseDoubleClickEvent(QMouseEvent* e)
{
    scaleVal_ = 1.0;

    uint16_t showImgWidth = adaptImageSize_.width() * scaleVal_;
    uint16_t showImgHeight = adaptImageSize_.height() * scaleVal_;

    int xoffset = (Ortho2DSize_.width() - showImgWidth)/2;
    int yoffset = (Ortho2DSize_.height() - showImgHeight)/2;

    vertexPos_[Left_Bottom_X] = xoffset;
    vertexPos_[Left_Bottom_Y] = yoffset;
    vertexPos_[Right_Bottom_X] = xoffset + showImgWidth;
    vertexPos_[Right_Bottom_Y] = yoffset;
    vertexPos_[Right_Top_X] = xoffset + showImgWidth;
    vertexPos_[Right_Top_Y] = yoffset + showImgHeight;
    vertexPos_[Left_Top_X] = xoffset;
    vertexPos_[Left_Top_Y] = yoffset + showImgHeight;

    paintGL();
}

对话框头文件dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include 
#include 
#include "gl_image.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

private slots:
    void slotTimeOut();

private:
    Ui::Dialog *ui;
    GL_Image* glImage;
    QTimer timer;
};

#endif // DIALOG_H

对话框实现文件dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    // 将widget控件作为绘制窗口
    glImage = new GL_Image(ui->widget);
    glImage->setFixedSize(ui->widget->size());

    connect(&timer, SIGNAL(timeout()), this, SLOT(slotTimeOut()));
    timer.setTimerType(Qt::PreciseTimer);
    timer.start(100);
}

Dialog::~Dialog()
{
    delete ui;
}

// 主界面开启定时器,在界面循环显示4个方向的图片
void Dialog::slotTimeOut()
{
    static uint i = 0;

    char imageName[100];
    // 需要修改为自己的图片路径
    sprintf(imageName, "/home/gk/program/Qt/QtOpenGLWidget/images/lena%d.jpg", i++%4);
    QImage image(imageName);
    QImage rgba = image.rgbSwapped(); //qimage加载的颜色通道顺序和opengl显示的颜色通道顺序不一致,调换R通道和B通道
    glImage->setImageData(rgba.bits(), rgba.width(), rgba.height());
    glImage->repaint(); //窗口重绘,repaint会调用paintEvent函数,paintEvent会调用paintGL函数实现重绘
}

入口文件main.cpp

#include 
#include "dialog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

gif效果图简单演示了实时显示和鼠标滚轮缩放、拖拽功能

至此demo代码都贴完了,另外demo工程也打包上传到了这里。

你可能感兴趣的:(QT,OpenGL,QT,OpenGL)