#include "ScreenWidget.h" #include "MediaPlayer.h" #include "FileHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MainWindow.h" #include "Config.h" #include "ShapeWidget.h" #include "VideoFrameDisplay.h" QMap g_shapeOffsetPoint = { {1, {-15,-50}}, {2, {100, -35}}, {3, {95, -95}}, {4, {100, 0}}, {5, {0, -95}} }; class LoadImageTask : public QObject, public QRunnable { public: using HandleResoult = std::function; public: LoadImageTask(int index, const QVector& texturePaths, HandleResoult handle, QObject* parent = nullptr) : QObject(parent) , m_index(index) , m_texturePaths(texturePaths) , m_handle(std::move(handle)) { setAutoDelete(true); } void run() override { const QString& path = m_texturePaths[m_index]; QPixmap px(path); QImage image = px.toImage().mirrored(); m_handle(m_index, std::move(image)); } private: int m_index{ 0 }; const QVector& m_texturePaths; HandleResoult m_handle; }; ScreenWidget::ScreenWidget( QWidget* widget) :QOpenGLWidget(widget) { //setStyleSheet("background-color: rgb(0, 0, 0); background:transparent;"); makeCurrent(); setWindowFlags(Qt::FramelessWindowHint); const QString configPath = QApplication::applicationDirPath() + "/ProjectDisplay/project.ini"; QSettings setting(configPath, QSettings::IniFormat); m_x = setting.value("x", 0).toInt(); m_y = setting.value("y", 0).toInt(); m_sx = setting.value("sx", 1.0f).toFloat(); m_sy = setting.value("sy", 1.0f).toFloat(); m_r = setting.value("r", 0).toInt(); QString bg = setting.value("bg", 0).toString(); const QString path = QApplication::applicationDirPath() + "/ProjectDisplay/" + bg; m_bg.load(path); connect(&vlcMediaPlayer_, &VlcMediaPlayer::VideoDataOutput, this, &ScreenWidget::slotSetOneFrame, Qt::QueuedConnection); m_maskImage = QImage(m_bg.width(), m_bg.height(), QImage::Format_RGBA8888); m_maskImage.fill(QColor(0, 0, 0, 0)); InitMask(); //connect(&vlcMediaPlayer_, &VlcMediaPlayer::VideoDataOutput, Display, &VideoFrameDisplay::slotSetOneFrame, Qt::QueuedConnection); } ScreenWidget::~ScreenWidget() { makeCurrent(); if (vlcMediaPlayer_.IsPlaying()) { vlcMediaPlayer_.Stop(); } } void ScreenWidget::OnMessage(const QString& cmd) { const QStringList params = cmd.split("|"); if (params.count() < 3) { qWarning() << "invalid cmd:" << cmd; return; } makeCurrent(); m_maskImage.fill(QColor(0, 0, 0, 0)); QOpenGLPixelTransferOptions uploadOptions; uploadOptions.setAlignment(1); m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions); m_town = params[0].toInt(); m_place = params[1].toInt(); const QString townName = MainWindow::Get().GetTownName(m_town); m_scope = MainWindow::Get().GetScope(m_town, m_place); if (vlcMediaPlayer_.IsPlaying()) { vlcMediaPlayer_.Stop(); } m_cricleFrame = 0; int wh = qMin(m_scope.width, m_scope.height); m_moveRect = QRect(m_scope.x, m_scope.y, wh, wh); m_drawRect = QRect(m_moveRect.x(), m_bg.height() - m_moveRect.y() - m_moveRect.height(), m_moveRect.width(), m_moveRect.height()); m_lastTime = QTime::currentTime(); // PlayCricle(m_scope); //Play(scop); } void ScreenWidget::keyPressEvent(QKeyEvent* event) { #if 0 if (event->key() == Qt::Key_Control) { m_ctrlPress = true; } if (Qt::ControlModifier & event->modifiers()) { if (event->key() == Qt::Key_Up) { QRect rect = Display->geometry(); int w = rect.width(); rect.setWidth(w + 10); rect.setHeight(w + 10); Display->setGeometry(rect); } else if (event->key() == Qt::Key_Down) { QRect rect = Display->geometry(); int w = rect.width(); rect.setWidth(w - 10); rect.setHeight(w - 10); Display->setGeometry(rect); } /* else if (event->key() == Qt::Key_Left) { Display->Rotate(-5); } else if (event->key() == Qt::Key_Right) { Display->Rotate(5); }*/ /* else if (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_J) { m_shapeWidget->Shape(1); } else if (event->key() == Qt::Key_PageDown || event->key() == Qt::Key_K) { m_shapeWidget->Shape(-1); } else if (event->key() == Qt::Key_S) { Save(); }*/ } if (Qt::ShiftModifier & event->modifiers()) { if (event->key() == Qt::Key_Left) { QRect rect = Display->geometry(); rect.translate( -5, 0); Display->setGeometry(rect); } else if (event->key() == Qt::Key_Right) { QRect rect = Display->geometry(); rect.translate(5, 0); Display->setGeometry(rect); } else if (event->key() == Qt::Key_Up) { QRect rect = Display->geometry(); rect.translate(0, -5); Display->setGeometry(rect); } else if (event->key() == Qt::Key_Down) { QRect rect = Display->geometry(); rect.translate(0, 5); Display->setGeometry(rect); } else if (event->key() == Qt::Key_W) { m_sy *= 1.1; } else if (event->key() == Qt::Key_S) { m_sy *= 0.9; } else if (event->key() == Qt::Key_A) { m_sx *= 1.1; } else if (event->key() == Qt::Key_D) { m_sx *= 0.9; } else if (event->key() == Qt::Key_R) { m_r++; } else if (event->key() == Qt::Key_T) { m_r--; } /* else if (event->key() == Qt::Key_S) { const QString configPath = QApplication::applicationDirPath() + "/ProjectDisplay/project.ini"; QSettings setting(configPath, QSettings::IniFormat); setting.setValue("x", m_x); setting.setValue("y", m_y); setting.setValue("sx", m_sx); setting.setValue("sy", m_sy); setting.setValue("r", m_r); setting.sync(); }*/ update(); } #endif } void ScreenWidget::keyReleaseEvent(QKeyEvent* event) { m_ctrlPress = false; } void ScreenWidget::mouseReleaseEvent(QMouseEvent* event) { #if 0 if (m_ctrlPress && event->button() == Qt::LeftButton) { QRect rect = Display->geometry(); Display->move(event->pos() - QPoint(rect.width(), rect.height()) / 2); } #endif } void ScreenWidget::initializeGL() { makeCurrent(); initializeOpenGLFunctions(); static float vertices[] = { // 位置 // 颜色 // 纹理坐标 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下 -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 }; static unsigned int indices[] = { // 注意索引从0开始! // 此例的索引(0,1,2,3)就是顶点数组vertices的下标, // 这样可以由下标代表顶点组合成矩形 0, 1, 3, // 第一个三角形 1, 2, 3 // 第二个三角形 }; glClearColor(0.2f, 0.3f, 0.3f, 1.0f);// 设置清屏颜色 glDisable(GL_DEPTH_TEST); // 启用深度测试 glEnable(GL_COLOR_BUFFER_BIT); // 启用颜色缓冲区 const QString shaderDir = QApplication::applicationDirPath() + "/ProjectDisplay/shader/"; // 创建顶点着色器对象,并编译 m_vshader = new QOpenGLShader(QOpenGLShader::Vertex); bool success = m_vshader->compileSourceFile(shaderDir + "vertex_shader.vs"); if (!success) { const QString log = m_vshader->log(); qDebug() << log; } // 创建片段着色器对象,并编译 m_fshader = new QOpenGLShader(QOpenGLShader::Fragment); success = m_fshader->compileSourceFile(shaderDir + "screen_fragment_shader.fs"); if (!success) { const QString log = m_fshader->log(); qDebug() << log; } // 创建着色器程序对象,添加顶点着色器和片段着色器,并链接它们 m_program = new QOpenGLShaderProgram; m_program->addShader(m_vshader); m_program->addShader(m_fshader); m_program->link(); // 创建顶点数组对象,并绑定到当前上下文 m_vao = new QOpenGLVertexArrayObject; m_vao->create(); QOpenGLVertexArrayObject::Binder vaoBinder(m_vao); // 创建顶点缓冲对象, QOpenGLBuffer* vbo = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); vbo->create(); vbo->bind(); // 分配显存大小,并搬运至显存 vbo->allocate(vertices, sizeof(vertices)); // 创建元素缓冲对象, QOpenGLBuffer* ebo = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); ebo->create(); ebo->bind(); // 分配显存大小,并搬运至显存 ebo->allocate(indices, sizeof(indices)); // 链接顶点属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0); // 顶点属性默认是禁用的,启用顶点属性0(location=0) m_program->enableAttributeArray(0); // 颜色属性 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); m_program->enableAttributeArray(1); // 纹理坐标属性 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); m_program->enableAttributeArray(2); m_vao->release(); vbo->release(); m_program->bind(); //在修改uniform值之前,一定要绑定着色器程序到当前激活的opengl上下文 m_program->setUniformValue("bgMap", 0); m_program->setUniformValue("moiveMap", 1); //connect(&m_timer, &QTimer::timeout, this, &ScreenWidget::OnTimeout); //m_timer.start(40); //QPixmap px(m_backgourndTexturePaths[m_backgroundCurrentFrame]); m_background = new QOpenGLTexture(QOpenGLTexture::Target2D); m_background->setAutoMipMapGenerationEnabled(false); m_background->setFormat(QOpenGLTexture::RGBA8_UNorm); m_background->setSize(m_bg.width(), m_bg.height()); m_background->setMipLevels(1); m_background->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); // 设置纹理环绕方式 m_background->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); m_background->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); QImage image = m_bg.toImage().mirrored(); image = image.convertToFormat(QImage::Format_RGBA8888); QOpenGLPixelTransferOptions uploadOptions; uploadOptions.setAlignment(1); m_background->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits(), &uploadOptions); m_movie = new QOpenGLTexture(QOpenGLTexture::Target2D/*px.toImage().mirrored()*/); m_movie->setAutoMipMapGenerationEnabled(false); m_movie->setFormat(QOpenGLTexture::RGBA8_UNorm); m_movie->setSize(m_bg.width(), m_bg.height()); m_movie->setMipLevels(1); m_movie->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); // 设置纹理环绕方式 m_movie->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); m_movie->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); QImage dest(m_bg.width(), m_bg.height(), QImage::Format_RGBA8888); dest.fill(QColor(0, 0, 0, 0)); m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, dest.constBits(), &uploadOptions); } void ScreenWidget::paintGL() { makeCurrent(); glClearColor(0.f, 0.f, 0.f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); QMatrix4x4 projection; QMatrix4x4 view; view.translate(0.0f, 0.0f, -1.0000f); QMatrix4x4 model; m_background->bind(0); m_movie->bind(1); // 绑定着色器程序至当前上下文,相当于调用glUseProgram() m_program->bind(); m_program->setUniformValue("view", view); m_program->setUniformValue("projection", projection); m_program->setUniformValue("model", model); m_program->setUniformValue("alpha", m_alpha); // 绑定VAO,调用设置的一组状态 QOpenGLVertexArrayObject::Binder vaoBinder(m_vao); // 绘制三角形 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); m_program->release(); // 交换缓存 auto* context = QOpenGLContext::currentContext(); context->swapBuffers(context->surface()); } void ScreenWidget::resizeGL(int w, int h) { makeCurrent(); glViewport(0, 0, w, h); update(); } void ScreenWidget::Save() { #if 0 if (-1 == m_town || -1 == m_place) { qDebug() << __FUNCTION__ << m_place << ", m_town:" << m_town; return; } Scope scop = MainWindow::Get().GetScope(m_town, m_place); QRect rect = Display->geometry(); scop.x = rect.x(); scop.y = rect.y(); scop.width = rect.width(); scop.height = rect.height(); scop.rotate = m_shapeWidget->GetRotate(); //scop.shape = 1;// m_shapeWidget->GetShapeCode(); MainWindow::Get().SetScope(m_town, m_place, scop); MainWindow::Get().SaveConfig(); #endif } // //QPoint readPointFromFile() { // QFile file("D:/1.txt"); // if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { // qWarning() << "Cannot open file:" << file.errorString(); // return QPoint(); // 返回默认构造的QPoint,即(0, 0) // } // // QTextStream in(&file); // QString line = in.readLine(); // 读取文件的第一行 // // bool okX, okY; // int x = line.section(',', 0, 0).trimmed().toInt(&okX); // int y = line.section(',', 1, 1).trimmed().toInt(&okY); // // if (!okX || !okY) { // qWarning() << "Invalid data in file:"; // return QPoint(); // 数据无效,返回默认QPoint // } // // file.close(); // return QPoint(x, y); //} void ScreenWidget::PlayCricle(const Scope& scop) { if (m_cricleFrame >= m_cricleFrameCount - 1) { if (m_cricleFrame == m_cricleFrameCount - 1) { Play(scop); ++m_cricleFrame; } return; } QTime time = QTime::currentTime(); int elapsed = m_lastTime.msecsTo(time); if (elapsed <= MainWindow::Get().GetCrilceDelay()) { return; } m_lastTime = time; QRect rect = QRect(-m_bg.width(), -m_bg.height(), m_bg.width() * 2, m_bg.height() * 2); rect.moveCenter(m_drawRect.center() +g_shapeOffsetPoint[scop.shape]); makeCurrent(); m_maskImage.fill(QColor(0, 0, 0, 0)); QPainter painter(&m_maskImage); const QImage& maskImage = m_textureData[m_cricleFrame++ % m_cricleFrameCount]; painter.drawImage(rect, maskImage); painter.end(); QOpenGLPixelTransferOptions uploadOptions; uploadOptions.setAlignment(1); m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions); } void ScreenWidget::Play(const Scope& scop) { const QString moviePath = QApplication::applicationDirPath() + "/ProjectDisplay/movie/" + QString::number(scop.shape) + ".mov"; vlcMediaPlayer_.Play(moviePath); } void ScreenWidget::slotSetOneFrame(QImage image) { makeCurrent(); m_maskImage.fill(QColor(0, 0, 0, 0)); QPainter painter(&m_maskImage); image = image.mirrored(); QImage maskImage = m_textureData[m_cricleFrame++ % m_cricleFrameCount]; painter.drawImage(m_drawRect, image); painter.end(); QOpenGLPixelTransferOptions uploadOptions; uploadOptions.setAlignment(1); m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions); } void ScreenWidget::InitMask() { const QString imageDir = QApplication::applicationDirPath() + "/ProjectDisplay/circle"; QStringList jpgFiles; QDir dir(imageDir); QStringList filters; filters << "*.png"; QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files); m_cricleFrameCount = fileInfoList.size(); m_textureData.resize(fileInfoList.size()); QVector paths; foreach(const QFileInfo & fileInfo, fileInfoList) { paths.append(fileInfo.absoluteFilePath()); } auto handle = [&](int index, QImage image) { m_textureData[index] = std::move(image); }; QThreadPool pool; pool.setMaxThreadCount(std::thread::hardware_concurrency()); for (int i = 0; i < paths.count(); ++i) { LoadImageTask* task = new LoadImageTask(i, paths, handle); pool.start(task); } pool.waitForDone(); } void ScreenWidget::OnTimeout() { PlayCricle(m_scope); update(); }