532 lines
17 KiB
C++
532 lines
17 KiB
C++
#include "ScreenWidget.h"
|
||
|
||
#include "MediaPlayer.h"
|
||
|
||
#include "FileHelper.h"
|
||
|
||
#include <QGridLayout>
|
||
#include <QLabel>
|
||
#include <QMessageBox>
|
||
#include <QPixmap>
|
||
#include <QParallelAnimationGroup>
|
||
#include <QApplication>
|
||
#include <QPropertyAnimation>
|
||
#include <QOpenGLPixelTransferOptions>
|
||
#include <QRunnable>
|
||
#include <QSettings>
|
||
#include <QPainter>
|
||
#include <QThreadPool>
|
||
#include <QDir>
|
||
|
||
#include <QGridLayout>
|
||
|
||
#include "MainWindow.h"
|
||
#include "Config.h"
|
||
#include "ShapeWidget.h"
|
||
#include "VideoFrameDisplay.h"
|
||
|
||
QMap<int, QPoint> 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<void(int index, QImage image)>;
|
||
public:
|
||
LoadImageTask(int index, const QVector<QString>& 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<QString>& 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<QString> 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();
|
||
}
|
||
|