culturered_client/ProjectorDisplay/ScreenWidget.cpp

532 lines
17 KiB
C++
Raw Permalink Normal View History

2024-09-07 03:34:44 +00:00
#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);
// 顶点属性默认是禁用的启用顶点属性0location=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();
}