File indexing completed on 2025-02-23 04:35:41

0001 /*
0002     SPDX-FileCopyrightText: 2010-2022 Mladen Milinkovic <max@smoothware.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "waverenderer.h"
0008 
0009 #include "appglobal.h"
0010 #include "application.h"
0011 #include "scconfig.h"
0012 #include "core/richtext/richdocument.h"
0013 #include "gui/waveform/wavebuffer.h"
0014 #include "gui/waveform/waveformwidget.h"
0015 #include "gui/waveform/wavesubtitle.h"
0016 #include "gui/waveform/zoombuffer.h"
0017 
0018 #include <QBoxLayout>
0019 #include <QPainter>
0020 #include <QScrollBar>
0021 #include <QTextLayout>
0022 
0023 using namespace SubtitleComposer;
0024 
0025 
0026 WaveRenderer::WaveRenderer(WaveformWidget *parent)
0027     : QWidget(parent),
0028       m_wfw(parent)
0029 {
0030     setAttribute(Qt::WA_OpaquePaintEvent, true);
0031     setAttribute(Qt::WA_NoSystemBackground, true);
0032     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0033     setMouseTracking(true);
0034 
0035     connect(SCConfig::self(), &SCConfig::configChanged, this, &WaveRenderer::onConfigChanged);
0036     onConfigChanged();
0037 }
0038 
0039 bool
0040 WaveRenderer::showTranslation() const
0041 {
0042     return m_wfw->m_showTranslation;
0043 }
0044 
0045 void
0046 WaveRenderer::onConfigChanged()
0047 {
0048     m_fontNumber = QFont(SCConfig::wfFontFamily(), SCConfig::wfSubNumberFontSize());
0049     m_fontNumberHeight = QFontMetrics(m_fontNumber).height();
0050     m_fontText = QFont(SCConfig::wfFontFamily(), SCConfig::wfSubTextFontSize());
0051 
0052     m_subBorderWidth = SCConfig::wfSubBorderWidth();
0053 
0054     m_subNumberColor = QPen(QColor(SCConfig::wfSubNumberColor()), 0, Qt::SolidLine);
0055     m_subTextColor = QPen(QColor(SCConfig::wfSubTextColor()), 0, Qt::SolidLine);
0056 
0057     // FIXME: instead of using devicePixelRatioF() for pen width we should draw waveform in higher resolution
0058     m_waveInner = QPen(QColor(SCConfig::wfInnerColor()), devicePixelRatioF(), Qt::SolidLine);
0059     m_waveOuter = QPen(QColor(SCConfig::wfOuterColor()), devicePixelRatioF(), Qt::SolidLine);
0060 
0061     m_subtitleBack = QColor(SCConfig::wfSubBackground());
0062     m_subtitleBorder = QColor(SCConfig::wfSubBorder());
0063 
0064     m_selectedBack = QColor(SCConfig::wfSelBackground());
0065     m_selectedBorder = QColor(SCConfig::wfSelBorder());
0066 
0067     m_playColor = QPen(QColor(SCConfig::wfPlayLocation()), 0, Qt::SolidLine);
0068     m_mouseColor = QPen(QColor(SCConfig::wfMouseLocation()), 0, Qt::DotLine);
0069 }
0070 
0071 bool
0072 WaveRenderer::event(QEvent *evt)
0073 {
0074     switch(evt->type()) {
0075     case QEvent::Resize: {
0076         bool vertical = height() > width();
0077         if(m_vertical != vertical) {
0078             m_vertical = vertical;
0079             m_wfw->onWaveformRotate(m_vertical);
0080         }
0081         m_wfw->onWaveformResize(span());
0082         break;
0083     }
0084 
0085     case QEvent::Paint: {
0086         QPainter painter(this);
0087         painter.fillRect(static_cast<QPaintEvent *>(evt)->rect(), Qt::black);
0088         paintGraphics(painter);
0089         return true;
0090     }
0091 
0092 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0093 #define position pos
0094 #endif
0095 
0096     case QEvent::MouseMove: {
0097         QMouseEvent *mouse = static_cast<QMouseEvent *>(evt);
0098         m_wfw->updatePointerTime(m_vertical ? mouse->position().y() : mouse->position().x());
0099         update();
0100         break;
0101     }
0102 
0103     case QEvent::MouseButtonDblClick: {
0104         QMouseEvent *mouse = static_cast<QMouseEvent *>(evt);
0105         emit m_wfw->doubleClick(m_wfw->timeAt(m_vertical ? mouse->position().y() : mouse->position().x()));
0106         return true;
0107     }
0108 
0109     case QEvent::MouseButtonPress: {
0110         QMouseEvent *mouse = static_cast<QMouseEvent *>(evt);
0111         int y = m_vertical ? mouse->position().y() : mouse->position().x();
0112         if(m_wfw->mousePress(y, mouse->button()))
0113             return true;
0114         break;
0115     }
0116 
0117 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0118 #define globalPosition globalPos
0119 #endif
0120 
0121     case QEvent::MouseButtonRelease: {
0122         QMouseEvent *mouse = static_cast<QMouseEvent *>(evt);
0123         int y = m_vertical ? mouse->position().y() : mouse->position().x();
0124         if(m_wfw->mouseRelease(y, mouse->button(), mouse->globalPosition()))
0125             return true;
0126         break;
0127     }
0128 
0129     default:
0130         break;
0131     }
0132 
0133     return QWidget::event(evt);
0134 }
0135 
0136 void
0137 WaveRenderer::paintWaveform(QPainter &painter, quint32 widgetWidth, quint32 widgetHeight)
0138 {
0139     const quint16 chans = m_wfw->m_wfBuffer->channels();
0140     if(!chans || !m_wfw->m_zoomDataLen)
0141         return;
0142 
0143     const quint32 chHalfWidth = (m_vertical ? widgetWidth : widgetHeight) / chans / 2;
0144 
0145     for(quint16 ch = 0; ch < chans; ch++) {
0146         const qint32 chCenter = (ch * 2 + 1) * chHalfWidth;
0147         for(quint32 y = 0; y < m_wfw->m_zoomDataLen; y++) {
0148             const qint32 xMin = m_wfw->m_zoomData[ch][y].min * chHalfWidth / SAMPLE_MAX;
0149             const qint32 xMax = m_wfw->m_zoomData[ch][y].max * chHalfWidth / SAMPLE_MAX;
0150 
0151             painter.setPen(m_waveOuter);
0152             if(m_vertical)
0153                 painter.drawLine(chCenter - xMax, y, chCenter + xMax, y);
0154             else
0155                 painter.drawLine(y, chCenter - xMax, y, chCenter + xMax);
0156             painter.setPen(m_waveInner);
0157             if(m_vertical)
0158                 painter.drawLine(chCenter - xMin, y, chCenter + xMin, y);
0159             else
0160                 painter.drawLine(y, chCenter - xMin, y, chCenter + xMin);
0161         }
0162     }
0163 }
0164 
0165 void
0166 WaveRenderer::paintGraphics(QPainter &painter)
0167 {
0168     const quint32 msWindowSize = m_wfw->windowSize();
0169     const quint32 widgetHeight = height();
0170     const quint32 widgetWidth = width();
0171     const quint32 widgetSpan = m_vertical ? widgetHeight : widgetWidth;
0172 
0173     painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
0174 
0175     if(widgetSpan)
0176         paintWaveform(painter, widgetWidth, widgetHeight);
0177 
0178     m_wfw->updateVisibleLines();
0179 
0180     const RangeList &selection = app()->linesWidget()->selectionRanges();
0181     for(const WaveSubtitle *sub: qAsConst(m_wfw->m_visibleLines)) {
0182         const Time timeShow = sub->showTime();
0183         const Time timeHide = sub->hideTime();
0184         if(timeShow > m_wfw->m_timeEnd || m_wfw->m_timeStart > timeHide)
0185             continue;
0186 
0187         const bool selected = selection.contains(sub->line()->index());
0188         const int showY = widgetSpan * (timeShow.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize;
0189         const int hideY = widgetSpan * (timeHide.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize;
0190         QRect box;
0191 
0192         // draw background
0193         if(m_vertical)
0194             box = QRect(2, showY + m_subBorderWidth, widgetWidth - 4, hideY - showY - 2 * m_subBorderWidth);
0195         else
0196             box = QRect(showY + m_subBorderWidth, 2, hideY - showY - 2 * m_subBorderWidth, widgetHeight - 4);
0197 
0198         if(!m_wfw->m_subtitle || !m_wfw->m_subtitle->hasAnchors() || m_wfw->m_subtitle->isLineAnchored(sub->line()))
0199             painter.setOpacity(1.);
0200         else
0201             painter.setOpacity(.5);
0202 
0203         painter.fillRect(box, selected ? m_selectedBack : m_subtitleBack);
0204 
0205         // draw border lines
0206         if(m_subBorderWidth) {
0207             if(m_vertical) {
0208                 painter.fillRect(0, showY, widgetWidth, m_subBorderWidth, selected ? m_selectedBorder : m_subtitleBorder);
0209                 painter.fillRect(0, hideY - m_subBorderWidth, widgetWidth, m_subBorderWidth, selected ? m_selectedBorder : m_subtitleBorder);
0210             } else {
0211                 painter.fillRect(showY, 0, m_subBorderWidth, widgetHeight, selected ? m_selectedBorder : m_subtitleBorder);
0212                 painter.fillRect(hideY - m_subBorderWidth, 0, m_subBorderWidth, widgetHeight, selected ? m_selectedBorder : m_subtitleBorder);
0213             }
0214         }
0215 
0216         // draw text
0217         painter.save();
0218         painter.setClipRect(box);
0219         painter.translate(box.center());
0220         if(!m_vertical) // TODO: make rotation angle configurable
0221             painter.rotate(-45.);
0222         const QImage &st = sub->image();
0223         painter.drawImage(st.width() / -2, st.height() / -2, st, 0, 0, -1, -1);
0224         painter.restore();
0225 
0226         // draw subtitle index
0227         painter.setPen(m_subNumberColor);
0228         painter.setFont(m_fontNumber);
0229         if(m_vertical)
0230             painter.drawText(m_fontNumberHeight / 2, showY + m_fontNumberHeight + 2, QString::number(sub->line()->number()));
0231         else
0232             painter.drawText(showY + m_fontNumberHeight / 2, m_fontNumberHeight + 2, QString::number(sub->line()->number()));
0233 
0234         // draw anchor icon
0235         if(m_wfw->m_subtitle && m_wfw->m_subtitle->isLineAnchored(sub->line())) {
0236             static QFont fontAnchor("sans-serif", 12);
0237             painter.setFont(fontAnchor);
0238             if(m_vertical)
0239                 painter.drawText(box, Qt::AlignTop | Qt::AlignRight, QStringLiteral("\u2693"));
0240             else
0241                 painter.drawText(box, Qt::AlignBottom | Qt::AlignLeft, QStringLiteral("\u2693"));
0242         }
0243     }
0244 
0245     if(m_wfw->m_RMBDown) {
0246         int showY = widgetSpan * (m_wfw->m_timeRMBPress.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize;
0247         int hideY = widgetSpan * (m_wfw->m_timeRMBRelease.toMillis() - m_wfw->m_timeStart.toMillis()) / msWindowSize;
0248 
0249         QRect box;
0250         if(m_vertical)
0251             box = QRect(0, showY + m_subBorderWidth, widgetWidth, hideY - showY - 2 * m_subBorderWidth);
0252         else
0253             box = QRect(showY + m_subBorderWidth, 0, hideY - showY - 2 * m_subBorderWidth, widgetHeight);
0254 
0255         painter.fillRect(box, m_selectedBack);
0256     }
0257 
0258     int playY = widgetSpan * (m_wfw->m_timeCurrent - m_wfw->m_timeStart).toMillis() / msWindowSize;
0259     painter.setPen(m_playColor);
0260     if(m_vertical)
0261         painter.drawLine(0, playY, widgetWidth, playY);
0262     else
0263         painter.drawLine(playY, 0, playY, widgetHeight);
0264 
0265     painter.setPen(m_subTextColor);
0266     painter.setFont(m_fontText);
0267     if(m_vertical) {
0268         QRect textRect(6, 4, widgetWidth - 12, widgetHeight - 8);
0269         painter.drawText(textRect, Qt::AlignRight | Qt::AlignTop, m_wfw->m_timeStart.toString());
0270         painter.drawText(textRect, Qt::AlignRight | Qt::AlignBottom, m_wfw->m_timeEnd.toString());
0271     } else {
0272         QRect textRect(4, 6, widgetWidth - 8, widgetHeight - 12);
0273         painter.drawText(textRect, Qt::AlignLeft | Qt::AlignTop, m_wfw->m_timeStart.toString());
0274         painter.drawText(textRect, Qt::AlignRight | Qt::AlignTop, m_wfw->m_timeEnd.toString());
0275     }
0276 
0277     painter.setPen(m_mouseColor);
0278     playY = widgetSpan * (m_wfw->m_pointerTime - m_wfw->m_timeStart).toMillis() / msWindowSize;
0279     if(m_vertical)
0280         painter.drawLine(0, playY, widgetWidth, playY);
0281     else
0282         painter.drawLine(playY, 0, playY, widgetHeight);
0283 }