culturered_client/ProjectorDisplay/ScreenWidget.cpp
2024-09-07 11:34:44 +08:00

532 lines
17 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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();
}