#include "mainwindow.h" #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_updatingSeek(false) { setWindowTitle("Retro CRT Video Player (C++/Qt6 & Native CUDA Zero-Copy)"); resize(1200, 720); m_glWidget = new GLWidget(this); m_videoDecoder = new VideoDecoder(this); m_glWidget->setVideoDecoder(m_videoDecoder); setupUI(); // Conectar las señales de posición y finalización del hilo descodificador connect(m_videoDecoder, &VideoDecoder::positionChanged, this, &MainWindow::onPositionChanged); connect(m_videoDecoder, &VideoDecoder::playbackFinished, this, &MainWindow::onPlaybackFinished); } MainWindow::~MainWindow() { m_videoDecoder->stopThread(); } void MainWindow::setupUI() { // Aplicar hoja de estilos (QSS) para un look oscuro premium y retro-futurista this->setStyleSheet( "QMainWindow { background-color: #08080f; }" "QWidget { color: #f0f0f5; font-family: 'Outfit', 'Segoe UI', sans-serif; font-size: 13px; }" "QFrame#sidebar { background-color: #11111d; border-left: 1px solid #22223b; border-radius: 0px; }" "QPushButton { background-color: #ff007f; color: white; border: none; padding: 8px 15px; border-radius: 6px; font-weight: bold; min-width: 60px; }" "QPushButton:hover { background-color: #ff3399; }" "QPushButton:pressed { background-color: #cc0066; }" "QPushButton:disabled { background-color: #444; color: #888; }" "QSlider::groove:horizontal { border: 1px solid #2d2d44; height: 6px; background: #1a1a2e; border-radius: 3px; }" "QSlider::sub-page:horizontal { background: #ff007f; border-radius: 3px; }" "QSlider::handle:horizontal { background: #ffffff; border: 1px solid #ff007f; width: 14px; height: 14px; margin: -4px 0; border-radius: 7px; }" "QSlider::handle:horizontal:hover { background: #ff007f; }" "QLabel { color: #a0a0c0; }" "QCheckBox { spacing: 8px; }" "QCheckBox::indicator { width: 18px; height: 18px; border: 1px solid #ff007f; border-radius: 4px; background: #1a1a2e; }" "QCheckBox::indicator:checked { background-color: #ff007f; image: url(check.png); /* O usar un color plano */ }" "QGroupBox { font-weight: bold; border: 1px solid #22223b; border-radius: 8px; margin-top: 15px; padding-top: 15px; color: #ff007f; }" ); QWidget *centralWidget = new QWidget(this); setCentralWidget(centralWidget); QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget); mainLayout->setContentsMargins(10, 10, 10, 10); mainLayout->setSpacing(10); // --- COLUMNA IZQUIERDA: REPRODUCTOR --- QVBoxLayout *playerLayout = new QVBoxLayout(); playerLayout->addWidget(m_glWidget, 1); // Controles Multimedia QHBoxLayout *controlsLayout = new QHBoxLayout(); QPushButton *openBtn = new QPushButton("Abrir Video", this); openBtn->setStyleSheet("background-color: #00bfff;"); connect(openBtn, &QPushButton::clicked, this, &MainWindow::openVideoFile); controlsLayout->addWidget(openBtn); m_playBtn = new QPushButton("▶", this); connect(m_playBtn, &QPushButton::clicked, this, &MainWindow::togglePlayPause); controlsLayout->addWidget(m_playBtn); m_muteBtn = new QPushButton("🔊", this); m_muteBtn->setStyleSheet("background-color: #4a4e69;"); connect(m_muteBtn, &QPushButton::clicked, this, &MainWindow::toggleMute); controlsLayout->addWidget(m_muteBtn); m_seekBar = new QSlider(Qt::Horizontal, this); m_seekBar->setRange(0, 1000); m_seekBar->setEnabled(false); connect(m_seekBar, &QSlider::sliderMoved, this, &MainWindow::setVideoPosition); controlsLayout->addWidget(m_seekBar, 1); m_timeLabel = new QLabel("00:00 / 00:00", this); m_timeLabel->setMinimumWidth(90); m_timeLabel->setAlignment(Qt::AlignCenter); controlsLayout->addWidget(m_timeLabel); playerLayout->addLayout(controlsLayout); mainLayout->addLayout(playerLayout, 3); // 3/4 del ancho de ventana // --- COLUMNA DERECHA: SIDEBAR DE AJUSTES --- QFrame *sidebar = new QFrame(this); sidebar->setObjectName("sidebar"); sidebar->setFixedWidth(300); QVBoxLayout *sidebarLayout = new QVBoxLayout(sidebar); sidebarLayout->setContentsMargins(15, 15, 15, 15); QLabel *sidebarTitle = new QLabel("AJUSTES CRT RETRO", this); sidebarTitle->setStyleSheet("font-size: 16px; font-weight: bold; color: #ff007f; margin-bottom: 10px;"); sidebarTitle->setAlignment(Qt::AlignCenter); sidebarLayout->addWidget(sidebarTitle); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); scrollArea->setFrameShape(QFrame::NoFrame); QWidget *scrollWidget = new QWidget(this); QVBoxLayout *scrollLayout = new QVBoxLayout(scrollWidget); scrollLayout->setContentsMargins(0, 0, 0, 0); // Activador General m_crtCheckbox = new QCheckBox("Habilitar Efecto CRT", this); m_crtCheckbox->setChecked(true); m_crtCheckbox->setStyleSheet("font-weight: bold; font-size: 14px; color: #ffffff;"); connect(m_crtCheckbox, &QCheckBox::toggled, this, &MainWindow::onCrtToggled); scrollLayout->addWidget(m_crtCheckbox); // Grupo de Controles Geométricos QGroupBox *geoGroup = new QGroupBox("Geometría y Lente", this); QVBoxLayout *geoLayout = new QVBoxLayout(geoGroup); QLabel *curvLabel = new QLabel("Curvatura de Pantalla:", this); geoLayout->addWidget(curvLabel); QHBoxLayout *curvLayout = new QHBoxLayout(); m_curvatureSlider = new QSlider(Qt::Horizontal, this); m_curvatureSlider->setRange(0, 50); // 0.0 a 5.0 m_curvatureSlider->setValue(20); connect(m_curvatureSlider, &QSlider::valueChanged, this, &MainWindow::onCurvatureChanged); curvLayout->addWidget(m_curvatureSlider); m_curvatureLabel = new QLabel("2.0", this); m_curvatureLabel->setFixedWidth(30); curvLayout->addWidget(m_curvatureLabel); geoLayout->addLayout(curvLayout); scrollLayout->addWidget(geoGroup); // Grupo de Estilo Scanline QGroupBox *scanlineGroup = new QGroupBox("Efecto Scanlines", this); QVBoxLayout *scanlineLayout = new QVBoxLayout(scanlineGroup); QLabel *densLabel = new QLabel("Densidad de Líneas:", this); scanlineLayout->addWidget(densLabel); QHBoxLayout *densLayout = new QHBoxLayout(); m_scanlineDensitySlider = new QSlider(Qt::Horizontal, this); m_scanlineDensitySlider->setRange(100, 1000); m_scanlineDensitySlider->setValue(400); connect(m_scanlineDensitySlider, &QSlider::valueChanged, this, &MainWindow::onScanlineDensityChanged); densLayout->addWidget(m_scanlineDensitySlider); m_scanlineDensityLabel = new QLabel("400", this); m_scanlineDensityLabel->setFixedWidth(30); densLayout->addWidget(m_scanlineDensityLabel); scanlineLayout->addLayout(densLayout); QLabel *opacLabel = new QLabel("Opacidad de Líneas:", this); scanlineLayout->addWidget(opacLabel); QHBoxLayout *opacLayout = new QHBoxLayout(); m_scanlineOpacitySlider = new QSlider(Qt::Horizontal, this); m_scanlineOpacitySlider->setRange(0, 100); // 0% a 100% m_scanlineOpacitySlider->setValue(35); connect(m_scanlineOpacitySlider, &QSlider::valueChanged, this, &MainWindow::onScanlineOpacityChanged); opacLayout->addWidget(m_scanlineOpacitySlider); m_scanlineOpacityLabel = new QLabel("0.35", this); m_scanlineOpacityLabel->setFixedWidth(30); opacLayout->addWidget(m_scanlineOpacityLabel); scanlineLayout->addLayout(opacLayout); scrollLayout->addWidget(scanlineGroup); // Grupo de Imperfecciones QGroupBox *imperfGroup = new QGroupBox("Óptica y Color", this); QVBoxLayout *imperfLayout = new QVBoxLayout(imperfGroup); QLabel *aberrLabel = new QLabel("Aberración Cromática (RGB split):", this); imperfLayout->addWidget(aberrLabel); QHBoxLayout *aberrLayout = new QHBoxLayout(); m_aberrationSlider = new QSlider(Qt::Horizontal, this); m_aberrationSlider->setRange(0, 100); // 0.0000 a 0.0100 m_aberrationSlider->setValue(15); connect(m_aberrationSlider, &QSlider::valueChanged, this, &MainWindow::onAberrationChanged); aberrLayout->addWidget(m_aberrationSlider); m_aberrationLabel = new QLabel("0.0015", this); m_aberrationLabel->setFixedWidth(45); aberrLayout->addWidget(m_aberrationLabel); imperfLayout->addLayout(aberrLayout); QLabel *vigLabel = new QLabel("Viñeteado (Esquinas oscuras):", this); imperfLayout->addWidget(vigLabel); QHBoxLayout *vigLayout = new QHBoxLayout(); m_vignetteSlider = new QSlider(Qt::Horizontal, this); m_vignetteSlider->setRange(5, 50); // 0.05 a 0.50 m_vignetteSlider->setValue(20); connect(m_vignetteSlider, &QSlider::valueChanged, this, &MainWindow::onVignetteChanged); vigLayout->addWidget(m_vignetteSlider); m_vignetteLabel = new QLabel("0.20", this); m_vignetteLabel->setFixedWidth(30); vigLayout->addWidget(m_vignetteLabel); imperfLayout->addLayout(vigLayout); QLabel *brightLabel = new QLabel("Brillo Adicional:", this); imperfLayout->addWidget(brightLabel); QHBoxLayout *brightLayout = new QHBoxLayout(); m_brightnessSlider = new QSlider(Qt::Horizontal, this); m_brightnessSlider->setRange(80, 150); // 0.80 a 1.50 m_brightnessSlider->setValue(110); connect(m_brightnessSlider, &QSlider::valueChanged, this, &MainWindow::onBrightnessChanged); brightLayout->addWidget(m_brightnessSlider); m_brightnessLabel = new QLabel("1.10", this); m_brightnessLabel->setFixedWidth(30); brightLayout->addWidget(m_brightnessLabel); imperfLayout->addLayout(brightLayout); scrollLayout->addWidget(imperfGroup); scrollLayout->addStretch(1); scrollArea->setWidget(scrollWidget); sidebarLayout->addWidget(scrollArea); mainLayout->addWidget(sidebar); } void MainWindow::openVideoFile() { QString fileName = QFileDialog::getOpenFileName(this, "Abrir archivo de video", "", "Videos (*.mp4 *.avi *.mkv *.webm *.mov)"); if (!fileName.isEmpty()) { openVideoFileDirect(fileName); } } void MainWindow::openVideoFileDirect(const QString &fileName) { m_glWidget->clearVideo(); m_videoDecoder->stopThread(); if (m_videoDecoder->openFile(fileName)) { m_seekBar->setEnabled(m_videoDecoder->getDuration() > 0.0); m_playBtn->setText("⏸"); m_videoDecoder->start(); // Arranca el hilo de decodificación m_videoDecoder->play(); // Inicia la reproducción } } void MainWindow::togglePlayPause() { if (m_videoDecoder->isPaused()) { m_videoDecoder->play(); m_playBtn->setText("⏸"); } else { m_videoDecoder->pause(); m_playBtn->setText("▶"); } } void MainWindow::toggleMute() { static bool muted = false; muted = !muted; m_muteBtn->setText(muted ? "🔇" : "🔊"); } void MainWindow::setVideoPosition(int position) { if (m_videoDecoder->getDuration() > 0.0) { m_updatingSeek = true; double targetSecs = (static_cast(position) / 1000.0) * m_videoDecoder->getDuration(); m_videoDecoder->seek(targetSecs); m_updatingSeek = false; } } void MainWindow::onPositionChanged(qint64 positionMs) { double durationSecs = m_videoDecoder->getDuration(); if (durationSecs > 0.0 && !m_updatingSeek) { m_updatingSeek = true; double progress = static_cast(positionMs) / (durationSecs * 1000.0); m_seekBar->setValue(static_cast(progress * 1000.0)); m_updatingSeek = false; } // Formatear tiempos auto format = [](qint64 ms) -> QString { qint64 totalSecs = ms / 1000; int mins = totalSecs / 60; int secs = totalSecs % 60; return QString("%1:%2").arg(mins, 2, 10, QChar('0')).arg(secs, 2, 10, QChar('0')); }; m_timeLabel->setText(format(positionMs) + " / " + format(static_cast(durationSecs * 1000.0))); } void MainWindow::onPlaybackFinished() { m_playBtn->setText("▶"); m_seekBar->setValue(0); } // --- SLOTS DE PARÁMETROS DEL SHADER --- void MainWindow::onCrtToggled(bool checked) { m_glWidget->setCrtEnabled(checked); } void MainWindow::onCurvatureChanged(int value) { float curvatureVal = value / 10.0f; m_curvatureLabel->setText(QString::number(curvatureVal, 'f', 1)); m_glWidget->setCurvature(curvatureVal); } void MainWindow::onScanlineDensityChanged(int value) { m_scanlineDensityLabel->setText(QString::number(value)); m_glWidget->setScanlineDensity(static_cast(value)); } void MainWindow::onScanlineOpacityChanged(int value) { float opacityVal = value / 100.0f; m_scanlineOpacityLabel->setText(QString::number(opacityVal, 'f', 2)); m_glWidget->setScanlineOpacity(opacityVal); } void MainWindow::onAberrationChanged(int value) { float aberrationVal = value / 10000.0f; m_aberrationLabel->setText(QString::number(aberrationVal, 'f', 4)); m_glWidget->setAberration(aberrationVal); } void MainWindow::onVignetteChanged(int value) { float vignetteVal = value / 100.0f; m_vignetteLabel->setText(QString::number(vignetteVal, 'f', 2)); m_glWidget->setVignette(vignetteVal); } void MainWindow::onBrightnessChanged(int value) { float brightnessVal = value / 100.0f; m_brightnessLabel->setText(QString::number(brightnessVal, 'f', 2)); m_glWidget->setBrightness(brightnessVal); }