From e51e727483b9c04d9e9f32d3ba0bce39b80e1f7b Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 3 Oct 2024 20:37:47 -0400 Subject: [PATCH 01/17] cache meter font --- src/qtgui/meter.cpp | 11 +++++------ src/qtgui/meter.h | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qtgui/meter.cpp b/src/qtgui/meter.cpp index 78eee2bb4..89357a588 100644 --- a/src/qtgui/meter.cpp +++ b/src/qtgui/meter.cpp @@ -53,6 +53,7 @@ CMeter::CMeter(QWidget *parent) : QFrame(parent) m_dBFS = MIN_DB; m_Sql = -150.0; + m_font = QFont("Arial"); } CMeter::~CMeter() @@ -116,9 +117,8 @@ void CMeter::draw(QPainter &painter) painter.drawLine(QLineF(x, hline, x, hline + 8)); } - QFont font("Arial"); - font.setPixelSize(height() / 4); - painter.setFont(font); + m_font.setPixelSize(height() / 4); + painter.setFont(m_font); painter.setPen(QColor(0xDA, 0xDA, 0xDA, 0xFF)); painter.drawText(marg, height() - 2, QString::number((double)m_dBFS, 'f', 1) + " dBFS" ); @@ -148,9 +148,8 @@ void CMeter::drawOverlay(QPainter &painter) } // draw scale text - QFont font("Arial"); - font.setPixelSize(height() / 4); - painter.setFont(font); + m_font.setPixelSize(height() / 4); + painter.setFont(m_font); qreal rwidth = (hstop - marg) / 5.0; QRectF rect(marg - rwidth / 2, 0, rwidth, majstart); diff --git a/src/qtgui/meter.h b/src/qtgui/meter.h index f24a56194..cb319bcf3 100644 --- a/src/qtgui/meter.h +++ b/src/qtgui/meter.h @@ -58,4 +58,5 @@ public slots: float m_dBFS; float m_Sql; + QFont m_font; }; From 0b74e49f86b576287e71d1d966e283c567598cac Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 3 Oct 2024 22:33:23 -0400 Subject: [PATCH 02/17] cache peak circles --- src/qtgui/plotter.cpp | 37 ++++++++++++++++++++++++------------- src/qtgui/plotter.h | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index b3065006d..15ecadd24 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -149,6 +149,7 @@ CPlotter::CPlotter(QWidget *parent) : QFrame(parent) m_DrawOverlay = true; m_2DPixmap = QPixmap(); m_OverlayPixmap = QPixmap(); + m_PeakPixmap = QPixmap(); m_WaterfallImage = QImage(); m_Size = QSize(0,0); m_GrabPosition = 0; @@ -1718,22 +1719,31 @@ void CPlotter::draw(bool newData) } // Paint peaks with shadow - QPen peakPen(m_maxFftColor, m_DPR); - QPen peakShadowPen(Qt::black, m_DPR); - peakPen.setWidthF(m_DPR); + if (m_PeakPixmap.isNull()) + { + m_PeakPixmap = QPixmap(qRound((10.0 + shadowOffset) * m_DPR), qRound((10.0 + shadowOffset) * m_DPR)); + m_PeakPixmap.fill(Qt::transparent); + QPainter peakPainter(&m_PeakPixmap); + peakPainter.translate(QPointF(0.5, 0.5)); + QPen peakPen(m_maxFftColor, m_DPR); + QPen peakShadowPen(Qt::black, m_DPR); + peakPen.setWidthF(m_DPR); + peakPainter.setPen(peakShadowPen); + peakPainter.drawEllipse( + QRectF(shadowOffset, + shadowOffset, + 10.0 * m_DPR, 10.0 * m_DPR)); + peakPainter.setPen(peakPen); + peakPainter.drawEllipse( + QRectF(0, + 0, + 10.0 * m_DPR, 10.0 * m_DPR)); + } + const int peakPixmapOffset = m_PeakPixmap.width() / 2; for(auto peakx : m_Peaks.keys()) { const qreal peakxPlot = (qreal)peakx; const qreal peakv = m_Peaks.value(peakx); - painter2.setPen(peakShadowPen); - painter2.drawEllipse( - QRectF(peakxPlot - 5.0 * m_DPR + shadowOffset, - peakv - 5.0 * m_DPR + shadowOffset, - 10.0 * m_DPR, 10.0 * m_DPR)); - painter2.setPen(peakPen); - painter2.drawEllipse( - QRectF(peakxPlot - 5.0 * m_DPR, - peakv - 5.0 * m_DPR, - 10.0 * m_DPR, 10.0 * m_DPR)); + painter2.drawPixmap(QPointF(peakxPlot - peakPixmapOffset, peakv - peakPixmapOffset), m_PeakPixmap); } } @@ -2382,6 +2392,7 @@ void CPlotter::setFftPlotColor(const QColor& color) m_avgFftColor = color; m_maxFftColor = color; // m_maxFftColor.setAlpha(192); + m_PeakPixmap = QPixmap(); m_FftFillCol = color; m_FftFillCol.setAlpha(26); m_MaxHoldColor = color; diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index e6277df8a..c3729bc92 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -259,6 +259,7 @@ public slots: eCapturetype m_CursorCaptured; QPixmap m_2DPixmap; // Composite of everything displayed in the 2D plotter area QPixmap m_OverlayPixmap; // Grid, axes ... things that need to be drawn infrequently + QPixmap m_PeakPixmap; QImage m_WaterfallImage; QColor m_ColorTbl[256]; QSize m_Size; From 56d7cc0fb57892906f6b48bf62e8ad779cb55d6e Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sat, 5 Oct 2024 08:11:05 -0400 Subject: [PATCH 03/17] sliding window waterfall implementation --- src/qtgui/plotter.cpp | 31 +++++++++++++++++++++---------- src/qtgui/plotter.h | 2 ++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 15ecadd24..a3096e48d 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1033,9 +1033,10 @@ void CPlotter::resizeEvent(QResizeEvent* ) // New waterfall, create blank area else if (m_WaterfallImage.isNull()) { - m_WaterfallImage = QImage(w, wfHeight, QImage::Format_RGB32); + m_WaterfallImage = QImage(w, wfHeight * 2, QImage::Format_RGB32); m_WaterfallImage.setDevicePixelRatio(m_DPR); m_WaterfallImage.fill(Qt::black); + m_WaterfallOffset = wfHeight; } // Existing waterfall, rescale width but no height as that would @@ -1045,12 +1046,14 @@ void CPlotter::resizeEvent(QResizeEvent* ) QImage oldWaterfall = m_WaterfallImage.scaled( w, m_WaterfallImage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_WaterfallImage = QImage(w, wfHeight, QImage::Format_RGB32); + m_WaterfallImage = QImage(w, wfHeight * 2, QImage::Format_RGB32); m_WaterfallImage.setDevicePixelRatio(m_DPR); m_WaterfallImage.fill(Qt::black); - memcpy(m_WaterfallImage.bits(), oldWaterfall.bits(), - m_WaterfallImage.bytesPerLine() * std::min(m_WaterfallImage.height(), oldWaterfall.height())); + memcpy(m_WaterfallImage.scanLine(wfHeight), oldWaterfall.scanLine(m_WaterfallOffset), + m_WaterfallImage.bytesPerLine() * std::min(wfHeight, m_WaterfallHeight)); + m_WaterfallOffset = wfHeight; } + m_WaterfallHeight = wfHeight; // Invalidate on resize m_MaxHoldValid = false; @@ -1094,7 +1097,8 @@ void CPlotter::paintEvent(QPaintEvent *) if (!m_WaterfallImage.isNull()) { - painter.drawImage(QPointF(0.0, plotHeightT), m_WaterfallImage); + painter.drawImage(QPointF(0.0, plotHeightT), m_WaterfallImage, + QRectF(0, m_WaterfallOffset, m_WaterfallImage.width(), m_WaterfallHeight)); } } @@ -1407,13 +1411,20 @@ void CPlotter::draw(bool newData) wf_valid_since_ms = tnow_ms; tlast_wf_drawn_ms = tnow_ms; - // move current data down one line(must do before attaching a QPainter object) - memmove(m_WaterfallImage.scanLine(1), m_WaterfallImage.scanLine(0), - m_WaterfallImage.bytesPerLine() * (m_WaterfallImage.height() - 1)); + // move current offset up one line + // if the offset was zero, copy the top half of the waterfall + // to the bottom half of the waterfall and reset offset + if(m_WaterfallOffset-- == 0) + { + memcpy(m_WaterfallImage.scanLine(m_WaterfallHeight + 1), m_WaterfallImage.scanLine(0), + m_WaterfallImage.bytesPerLine() * (m_WaterfallHeight - 1)); + m_WaterfallOffset = m_WaterfallHeight; + } + // draw new line of fft data at top of waterfall bitmap // draw black areas where data will not be draw - memset(m_WaterfallImage.scanLine(0), 0, m_WaterfallImage.bytesPerLine()); + memset(m_WaterfallImage.scanLine(m_WaterfallOffset), 0, m_WaterfallImage.bytesPerLine()); const bool useWfBuf = msec_per_wfline > 0; float _lineFactor; @@ -1431,7 +1442,7 @@ void CPlotter::draw(bool newData) const float v = useWfBuf ? m_wfbuf[ix] * lineFactor : dataSource[ix]; qint32 cidx = qRound((m_WfMaxdB - 10.0f * log10f(v)) * wfdBGainFactor); cidx = std::max(std::min(cidx, 255), 0); - m_WaterfallImage.setPixel(ix, 0, m_ColorTbl[255 - cidx].rgb()); + m_WaterfallImage.setPixel(ix, m_WaterfallOffset, m_ColorTbl[255 - cidx].rgb()); } wf_avg_count = 0; diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index c3729bc92..ca67c3553 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -261,6 +261,8 @@ public slots: QPixmap m_OverlayPixmap; // Grid, axes ... things that need to be drawn infrequently QPixmap m_PeakPixmap; QImage m_WaterfallImage; + int m_WaterfallHeight; + int m_WaterfallOffset; QColor m_ColorTbl[256]; QSize m_Size; qreal m_DPR{}; From 74c5a6fd47760bb14b7dbdcf27aebdb8ff870bb2 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sat, 5 Oct 2024 19:23:05 -0400 Subject: [PATCH 04/17] use polygons for fills --- src/qtgui/plotter.cpp | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index a3096e48d..a4e3963e1 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1539,6 +1539,9 @@ void CPlotter::draw(bool newData) const int maxMarker = std::max(ax, bx); const float binSizeY = (float)plotHeight / (float)histBinsDisplayed; + QPolygonF abPolygon; + QPolygonF underPolygon; + QPolygonF avgMaxPolygon; for (i = 0; i < npts; i++) { const int ix = i + xmin; @@ -1584,18 +1587,43 @@ void CPlotter::draw(bool newData) // Fill area between markers, even if they are off screen qreal yFill = m_PlotMode == PLOT_MODE_MAX ? yMaxD : yAvgD; if (fillMarkers && (ix) > minMarker && (ix) < maxMarker) { - painter2.fillRect(QRectF(ixPlot, yFill + 1.0, 1.0, plotHeight - yFill), abFillBrush); + abPolygon << QPointF(ixPlot, yFill + 1.0); } if (m_FftFill && m_PlotMode != PLOT_MODE_HISTOGRAM) { - painter2.fillRect(QRectF(ixPlot, yFill + 1.0, 1.0, plotHeight - yFill), m_FftFillCol); + underPolygon << QPointF(ixPlot, yFill + 1.0); } if (m_PlotMode == PLOT_MODE_FILLED) { - painter2.fillRect(QRectF(ixPlot, yMaxD + 1.0, 1.0, yAvgD - yMaxD), maxFillBrush); + avgMaxPolygon << maxLineBuf[i]; } } + if (!abPolygon.isEmpty()) { + abPolygon << QPointF(abPolygon.last().x(), plotHeight); + abPolygon << QPointF(abPolygon.first().x(), plotHeight); + painter2.setBrush(abFillBrush); + painter2.drawPolygon(abPolygon); + } + + if (!underPolygon.isEmpty()) + { + underPolygon << QPointF(underPolygon.last().x(), plotHeight); + underPolygon << QPointF(underPolygon.first().x(), plotHeight); + painter2.setBrush(m_FftFillCol); + painter2.drawPolygon(underPolygon); + } + + if (!avgMaxPolygon.isEmpty()) + { + for (i = npts - 1; i >= 0; i--) + { + avgMaxPolygon << avgLineBuf[i]; + } + painter2.setBrush(maxFillBrush); + painter2.drawPolygon(avgMaxPolygon); + } + if (doMaxLine) { // NOT scaling to DPR due to performance painter2.setPen(maxLinePen); From 9a33ba9acf18a2cd453c9014c9502f40230c4f44 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sun, 6 Oct 2024 12:51:52 -0400 Subject: [PATCH 05/17] prevent rounding errors --- src/qtgui/plotter.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index a4e3963e1..268ba34d4 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1016,8 +1016,11 @@ void CPlotter::resizeEvent(QResizeEvent* ) // Higher resolution pixmaps are used with higher DPR. They are // rescaled in paintEvent(). const int w = qRound((qreal)s.width() * m_DPR); - const int plotHeight = qRound((qreal)m_Percent2DScreen * (qreal)s.height() / 100.0 * m_DPR); - const int wfHeight = qRound((qreal)s.height() * m_DPR) - plotHeight; + const int rawHeight = s.height(); + const int rawPlotHeight = qRound((qreal)m_Percent2DScreen / 100.0 * (qreal)rawHeight); + const int rawWfHeight = rawHeight - rawPlotHeight; + const int plotHeight = qRound((qreal)rawPlotHeight * m_DPR); + const int wfHeight = qRound((qreal)rawWfHeight * m_DPR); m_OverlayPixmap = QPixmap(w, plotHeight); m_OverlayPixmap.fill(Qt::transparent); @@ -1086,19 +1089,19 @@ void CPlotter::paintEvent(QPaintEvent *) { const int plotWidthS = m_2DPixmap.width(); const int plotHeightS = m_2DPixmap.height(); - const QRectF plotRectS(0.0, 0.0, plotWidthS, plotHeightS); + const QRect plotRectS(0, 0, plotWidthS, plotHeightS); const int plotWidthT = qRound((qreal)plotWidthS / m_DPR); plotHeightT = qRound((qreal)plotHeightS / m_DPR); - const QRectF plotRectT(0.0, 0.0, plotWidthT, plotHeightT); + const QRect plotRectT(0, 0, plotWidthT, plotHeightT); painter.drawPixmap(plotRectT, m_2DPixmap, plotRectS); } if (!m_WaterfallImage.isNull()) { - painter.drawImage(QPointF(0.0, plotHeightT), m_WaterfallImage, - QRectF(0, m_WaterfallOffset, m_WaterfallImage.width(), m_WaterfallHeight)); + painter.drawImage(QPoint(0, plotHeightT), m_WaterfallImage, + QRect(0, m_WaterfallOffset, m_WaterfallImage.width(), m_WaterfallHeight)); } } From b051f2e84a1d735d7de3e25ee67b0a40b0fc083b Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Tue, 8 Oct 2024 18:29:30 -0400 Subject: [PATCH 06/17] avoid duplicate fills on mac --- src/applications/gqrx/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/gqrx/mainwindow.ui b/src/applications/gqrx/mainwindow.ui index 0700defc5..2e47aad6a 100644 --- a/src/applications/gqrx/mainwindow.ui +++ b/src/applications/gqrx/mainwindow.ui @@ -21,7 +21,7 @@ QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks - true + false From 8e473d9d761c282f424cdddbaf98a00f6e54638e Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Tue, 8 Oct 2024 20:33:26 -0400 Subject: [PATCH 07/17] stop using alpha composition --- src/qtgui/plotter.cpp | 91 +++++++++++++++++++++---------------------- src/qtgui/plotter.h | 8 ++++ 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 268ba34d4..437174af5 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1136,6 +1136,7 @@ void CPlotter::draw(bool newData) QPointF avgLineBuf[MAX_SCREENSIZE]; QPointF maxLineBuf[MAX_SCREENSIZE]; + QPointF holdLineBuf[MAX_SCREENSIZE]; const quint64 tnow_ms = QDateTime::currentMSecsSinceEpoch(); @@ -1492,17 +1493,17 @@ void CPlotter::draw(bool newData) { tlast_plot_drawn_ms = tnow_ms; - m_2DPixmap.fill(QColor::fromRgba(PLOTTER_BGD_COLOR)); + QColor bgColor = QColor::fromRgba(PLOTTER_BGD_COLOR); + m_2DPixmap.fill(bgColor); QPainter painter2(&m_2DPixmap); painter2.translate(QPointF(0.5, 0.5)); // draw the pandapter - QBrush fillBrush = QBrush(m_FftFillCol); + QBrush fillBrush = QBrush(blend(bgColor, m_FftFillCol, 26)); // Fill between max and avg - QColor maxFillCol = m_FftFillCol; - maxFillCol.setAlpha(80); + QColor maxFillCol = blend(bgColor, m_FftFillCol, 80); QBrush maxFillBrush = QBrush(maxFillCol); // Diagonal fill for area between markers. Scale the pattern to DPR. @@ -1510,11 +1511,11 @@ void CPlotter::draw(bool newData) abFillColor.setAlpha(128); QBrush abFillBrush = QBrush(abFillColor, Qt::BDiagPattern); - QColor maxLineColor = QColor(m_FftFillCol); + QColor maxLineColor; if (m_PlotMode == PLOT_MODE_FILLED) - maxLineColor.setAlpha(128); + maxLineColor = blend(bgColor, m_FftFillCol, 128); else - maxLineColor.setAlpha(255); + maxLineColor = blend(bgColor, m_FftFillCol, 255); QPen maxLinePen = QPen(maxLineColor); @@ -1522,13 +1523,11 @@ void CPlotter::draw(bool newData) QPen avgLinePen; if (m_PlotMode == PLOT_MODE_AVG || m_PlotMode == PLOT_MODE_HISTOGRAM) { - QColor avgLineCol = m_FftFillCol; - avgLineCol.setAlpha(255); + QColor avgLineCol = blend(bgColor, m_FftFillCol, 255); avgLinePen = QPen(avgLineCol); } else { - QColor avgLineCol = QColor(Qt::cyan); - avgLineCol.setAlpha(192); + QColor avgLineCol = blend(bgColor, QColor(Qt::cyan), 192); avgLinePen = QPen(avgLineCol); } @@ -1602,40 +1601,19 @@ void CPlotter::draw(bool newData) } } - if (!abPolygon.isEmpty()) { - abPolygon << QPointF(abPolygon.last().x(), plotHeight); - abPolygon << QPointF(abPolygon.first().x(), plotHeight); - painter2.setBrush(abFillBrush); - painter2.drawPolygon(abPolygon); - } - if (!underPolygon.isEmpty()) { underPolygon << QPointF(underPolygon.last().x(), plotHeight); underPolygon << QPointF(underPolygon.first().x(), plotHeight); - painter2.setBrush(m_FftFillCol); + painter2.setBrush(fillBrush); painter2.drawPolygon(underPolygon); } - if (!avgMaxPolygon.isEmpty()) - { - for (i = npts - 1; i >= 0; i--) - { - avgMaxPolygon << avgLineBuf[i]; - } - painter2.setBrush(maxFillBrush); - painter2.drawPolygon(avgMaxPolygon); - } - - if (doMaxLine) { - // NOT scaling to DPR due to performance - painter2.setPen(maxLinePen); - painter2.drawPolyline(maxLineBuf, npts); - } - if (doAvgLine) { - // NOT scaling to DPR due to performance - painter2.setPen(avgLinePen); - painter2.drawPolyline(avgLineBuf, npts); + if (!abPolygon.isEmpty()) { + abPolygon << QPointF(abPolygon.last().x(), plotHeight); + abPolygon << QPointF(abPolygon.first().x(), plotHeight); + painter2.setBrush(abFillBrush); + painter2.drawPolygon(abPolygon); } // Max hold @@ -1649,11 +1627,11 @@ void CPlotter::draw(bool newData) const qreal yMaxHoldD = (qreal)std::max(std::min( panddBGainFactor * (m_PandMaxdB - 10.0f * log10f(m_fftMaxHoldBuf[ix])), (float)plotHeight), 0.0f); - maxLineBuf[i] = QPointF(ixPlot, yMaxHoldD); + holdLineBuf[i] = QPointF(ixPlot, yMaxHoldD); } // NOT scaling to DPR due to performance painter2.setPen(m_MaxHoldColor); - painter2.drawPolyline(maxLineBuf, npts); + painter2.drawPolyline(holdLineBuf, npts); m_MaxHoldValid = true; } @@ -1669,15 +1647,36 @@ void CPlotter::draw(bool newData) const qreal yMinHoldD = (qreal)std::max(std::min( panddBGainFactor * (m_PandMaxdB - 10.0f * log10f(m_fftMinHoldBuf[ix])), (float)plotHeight), 0.0f); - maxLineBuf[i] = QPointF(ixPlot, yMinHoldD); + holdLineBuf[i] = QPointF(ixPlot, yMinHoldD); } // NOT scaling to DPR due to performance painter2.setPen(m_MinHoldColor); - painter2.drawPolyline(maxLineBuf, npts); + painter2.drawPolyline(holdLineBuf, npts); m_MinHoldValid = true; } + if (!avgMaxPolygon.isEmpty()) + { + for (i = npts - 1; i >= 0; i--) + { + avgMaxPolygon << avgLineBuf[i]; + } + painter2.setBrush(maxFillBrush); + painter2.drawPolygon(avgMaxPolygon); + } + + if (doMaxLine) { + // NOT scaling to DPR due to performance + painter2.setPen(maxLinePen); + painter2.drawPolyline(maxLineBuf, npts); + } + if (doAvgLine) { + // NOT scaling to DPR due to performance + painter2.setPen(avgLinePen); + painter2.drawPolyline(avgLineBuf, npts); + } + // Peak detection if (m_PeakDetectActive) { @@ -2435,12 +2434,10 @@ void CPlotter::setFftPlotColor(const QColor& color) m_maxFftColor = color; // m_maxFftColor.setAlpha(192); m_PeakPixmap = QPixmap(); + QColor bgColor = QColor::fromRgba(PLOTTER_BGD_COLOR); m_FftFillCol = color; - m_FftFillCol.setAlpha(26); - m_MaxHoldColor = color; - m_MaxHoldColor.setAlpha(80); - m_MinHoldColor = color; - m_MinHoldColor.setAlpha(80); + m_MaxHoldColor = blend(bgColor, color, 80); + m_MinHoldColor = blend(bgColor, color, 80); } /** Enable/disable filling the area below the FFT plot. */ diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index ca67c3553..7633b9be1 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -221,6 +221,14 @@ public slots: static qint64 roundFreq(qint64 freq, int resolution); quint64 msecFromY(int y); void clampDemodParameters(); + static QColor blend(QColor base, QColor over, int alpha255) + { + qreal alpha = alpha255 / 255.0; + qreal oneMinusAlpha = 1.0 - alpha; + return QColor(qRound(alpha * over.red() + oneMinusAlpha * base.red()), + qRound(alpha * over.green() + oneMinusAlpha * base.green()), + qRound(alpha * over.blue() + oneMinusAlpha * base.blue())); + } static bool isPointCloseTo(int x, int xr, int delta) { return ((x > (xr - delta)) && (x < (xr + delta))); From 007c2867695360e06cd178a2475469ff3d22e472 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Fri, 11 Oct 2024 08:53:15 -0500 Subject: [PATCH 08/17] use precomputed solid colors --- src/qtgui/plotter.cpp | 33 +++++++++++++++------------------ src/qtgui/plotter.h | 2 +- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 437174af5..5ec09ecfa 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1500,11 +1500,10 @@ void CPlotter::draw(bool newData) // draw the pandapter - QBrush fillBrush = QBrush(blend(bgColor, m_FftFillCol, 26)); + QBrush fillBrush = QBrush(m_FftFillCol); // Fill between max and avg - QColor maxFillCol = blend(bgColor, m_FftFillCol, 80); - QBrush maxFillBrush = QBrush(maxFillCol); + QBrush maxFillBrush = QBrush(m_FilledModeFillCol); // Diagonal fill for area between markers. Scale the pattern to DPR. QColor abFillColor = QColor::fromRgba(PLOTTER_MARKER_COLOR); @@ -1513,9 +1512,9 @@ void CPlotter::draw(bool newData) QColor maxLineColor; if (m_PlotMode == PLOT_MODE_FILLED) - maxLineColor = blend(bgColor, m_FftFillCol, 128); + maxLineColor = m_FilledModeMaxLineCol; else - maxLineColor = blend(bgColor, m_FftFillCol, 255); + maxLineColor = m_MainLineCol; QPen maxLinePen = QPen(maxLineColor); @@ -1523,12 +1522,10 @@ void CPlotter::draw(bool newData) QPen avgLinePen; if (m_PlotMode == PLOT_MODE_AVG || m_PlotMode == PLOT_MODE_HISTOGRAM) { - QColor avgLineCol = blend(bgColor, m_FftFillCol, 255); - avgLinePen = QPen(avgLineCol); + avgLinePen = QPen(m_MainLineCol); } else { - QColor avgLineCol = blend(bgColor, QColor(Qt::cyan), 192); - avgLinePen = QPen(avgLineCol); + avgLinePen = QPen(m_FilledModeAvgLineCol); } // The m_Marker{AB}X values are one cycle old, which makes for a laggy @@ -1630,7 +1627,7 @@ void CPlotter::draw(bool newData) holdLineBuf[i] = QPointF(ixPlot, yMaxHoldD); } // NOT scaling to DPR due to performance - painter2.setPen(m_MaxHoldColor); + painter2.setPen(m_HoldLineCol); painter2.drawPolyline(holdLineBuf, npts); m_MaxHoldValid = true; @@ -1650,7 +1647,7 @@ void CPlotter::draw(bool newData) holdLineBuf[i] = QPointF(ixPlot, yMinHoldD); } // NOT scaling to DPR due to performance - painter2.setPen(m_MinHoldColor); + painter2.setPen(m_HoldLineCol); painter2.drawPolyline(holdLineBuf, npts); m_MinHoldValid = true; @@ -1766,7 +1763,7 @@ void CPlotter::draw(bool newData) m_PeakPixmap.fill(Qt::transparent); QPainter peakPainter(&m_PeakPixmap); peakPainter.translate(QPointF(0.5, 0.5)); - QPen peakPen(m_maxFftColor, m_DPR); + QPen peakPen(m_MainLineCol, m_DPR); QPen peakShadowPen(Qt::black, m_DPR); peakPen.setWidthF(m_DPR); peakPainter.setPen(peakShadowPen); @@ -2430,14 +2427,14 @@ void CPlotter::moveToDemodFreq() /** Set FFT plot color. */ void CPlotter::setFftPlotColor(const QColor& color) { - m_avgFftColor = color; - m_maxFftColor = color; - // m_maxFftColor.setAlpha(192); m_PeakPixmap = QPixmap(); QColor bgColor = QColor::fromRgba(PLOTTER_BGD_COLOR); - m_FftFillCol = color; - m_MaxHoldColor = blend(bgColor, color, 80); - m_MinHoldColor = blend(bgColor, color, 80); + m_FftFillCol = blend(bgColor, color, 26); + m_MainLineCol = color; + m_HoldLineCol = blend(bgColor, color, 80); + m_FilledModeFillCol = blend(bgColor, color, 80); + m_FilledModeMaxLineCol = blend(bgColor, color, 128); + m_FilledModeAvgLineCol = blend(bgColor, QColor(Qt::cyan), 192); } /** Enable/disable filling the area below the FFT plot. */ diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index 7633b9be1..51584f3db 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -339,7 +339,7 @@ public slots: quint32 m_LastSampleRate{}; - QColor m_avgFftColor, m_maxFftColor, m_FftFillCol, m_MaxHoldColor, m_MinHoldColor; + QColor m_FftFillCol, m_FilledModeFillCol, m_FilledModeMaxLineCol, m_FilledModeAvgLineCol, m_MainLineCol, m_HoldLineCol; bool m_FftFill{}; QMap m_Peaks; From c96f009c5b8d17e0d230520b4a9000f21a7c8c88 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sun, 13 Oct 2024 06:52:21 -0500 Subject: [PATCH 09/17] static allocation --- src/qtgui/plotter.cpp | 33 ++++++++++++++++----------------- src/qtgui/plotter.h | 3 +++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 5ec09ecfa..5ed413631 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1134,10 +1134,6 @@ void CPlotter::draw(bool newData) return; } - QPointF avgLineBuf[MAX_SCREENSIZE]; - QPointF maxLineBuf[MAX_SCREENSIZE]; - QPointF holdLineBuf[MAX_SCREENSIZE]; - const quint64 tnow_ms = QDateTime::currentMSecsSinceEpoch(); // Pixmaps might be null, so scale up m_Size to get width. @@ -1579,9 +1575,9 @@ void CPlotter::draw(bool newData) // Add max, average points if they will be drawn if (doMaxLine) - maxLineBuf[i] = QPointF(ixPlot, yMaxD); + m_maxLineBuf[i] = QPointF(ixPlot, yMaxD); if (doAvgLine) - avgLineBuf[i] = QPointF(ixPlot, yAvgD); + m_avgLineBuf[i] = QPointF(ixPlot, yAvgD); // Fill area between markers, even if they are off screen qreal yFill = m_PlotMode == PLOT_MODE_MAX ? yMaxD : yAvgD; @@ -1594,7 +1590,7 @@ void CPlotter::draw(bool newData) } if (m_PlotMode == PLOT_MODE_FILLED) { - avgMaxPolygon << maxLineBuf[i]; + avgMaxPolygon << m_maxLineBuf[i]; } } @@ -1606,7 +1602,8 @@ void CPlotter::draw(bool newData) painter2.drawPolygon(underPolygon); } - if (!abPolygon.isEmpty()) { + if (!abPolygon.isEmpty()) + { abPolygon << QPointF(abPolygon.last().x(), plotHeight); abPolygon << QPointF(abPolygon.first().x(), plotHeight); painter2.setBrush(abFillBrush); @@ -1624,11 +1621,11 @@ void CPlotter::draw(bool newData) const qreal yMaxHoldD = (qreal)std::max(std::min( panddBGainFactor * (m_PandMaxdB - 10.0f * log10f(m_fftMaxHoldBuf[ix])), (float)plotHeight), 0.0f); - holdLineBuf[i] = QPointF(ixPlot, yMaxHoldD); + m_holdLineBuf[i] = QPointF(ixPlot, yMaxHoldD); } // NOT scaling to DPR due to performance painter2.setPen(m_HoldLineCol); - painter2.drawPolyline(holdLineBuf, npts); + painter2.drawPolyline(m_holdLineBuf, npts); m_MaxHoldValid = true; } @@ -1644,11 +1641,11 @@ void CPlotter::draw(bool newData) const qreal yMinHoldD = (qreal)std::max(std::min( panddBGainFactor * (m_PandMaxdB - 10.0f * log10f(m_fftMinHoldBuf[ix])), (float)plotHeight), 0.0f); - holdLineBuf[i] = QPointF(ixPlot, yMinHoldD); + m_holdLineBuf[i] = QPointF(ixPlot, yMinHoldD); } // NOT scaling to DPR due to performance painter2.setPen(m_HoldLineCol); - painter2.drawPolyline(holdLineBuf, npts); + painter2.drawPolyline(m_holdLineBuf, npts); m_MinHoldValid = true; } @@ -1657,21 +1654,23 @@ void CPlotter::draw(bool newData) { for (i = npts - 1; i >= 0; i--) { - avgMaxPolygon << avgLineBuf[i]; + avgMaxPolygon << m_avgLineBuf[i]; } painter2.setBrush(maxFillBrush); painter2.drawPolygon(avgMaxPolygon); } - if (doMaxLine) { + if (doMaxLine) + { // NOT scaling to DPR due to performance painter2.setPen(maxLinePen); - painter2.drawPolyline(maxLineBuf, npts); + painter2.drawPolyline(m_maxLineBuf, npts); } - if (doAvgLine) { + if (doAvgLine) + { // NOT scaling to DPR due to performance painter2.setPen(avgLinePen); - painter2.drawPolyline(avgLineBuf, npts); + painter2.drawPolyline(m_avgLineBuf, npts); } // Peak detection diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index 51584f3db..5971587ba 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -250,6 +250,9 @@ public slots: float m_wfAvgBuf[MAX_SCREENSIZE]{}; float m_histogram[MAX_SCREENSIZE][MAX_HISTOGRAM_SIZE]{}; float m_histIIR[MAX_SCREENSIZE][MAX_HISTOGRAM_SIZE]{}; + QPointF m_avgLineBuf[MAX_SCREENSIZE]{}; + QPointF m_maxLineBuf[MAX_SCREENSIZE]{}; + QPointF m_holdLineBuf[MAX_SCREENSIZE]{}; float m_histMaxIIR; std::vector m_fftIIR; std::vector m_fftData; From 37732a5e5ebe66bd79220aca7718d9c72ef1507b Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sun, 13 Oct 2024 06:52:40 -0500 Subject: [PATCH 10/17] opaque drawing without background fills --- src/applications/gqrx/mainwindow.ui | 3 --- src/qtgui/plotter.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/applications/gqrx/mainwindow.ui b/src/applications/gqrx/mainwindow.ui index 2e47aad6a..4790b522a 100644 --- a/src/applications/gqrx/mainwindow.ui +++ b/src/applications/gqrx/mainwindow.ui @@ -349,9 +349,6 @@ 0 - - true - QFrame::StyledPanel diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 5ed413631..992706551 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -89,7 +89,7 @@ CPlotter::CPlotter(QWidget *parent) : QFrame(parent) setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_PaintOnScreen,false); setAutoFillBackground(false); - setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_NoSystemBackground, true); setMouseTracking(true); From 67a6bac74998adc7aba2eec17e8e6fd8afa6147a Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sun, 13 Oct 2024 21:15:25 -0500 Subject: [PATCH 11/17] prevent extra redraws on meter --- src/qtgui/meter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qtgui/meter.cpp b/src/qtgui/meter.cpp index 89357a588..33c5dc788 100644 --- a/src/qtgui/meter.cpp +++ b/src/qtgui/meter.cpp @@ -72,9 +72,14 @@ QSize CMeter::sizeHint() const void CMeter::setLevel(float dbfs) { + const float old = m_dBFS; float alpha = dbfs < m_dBFS ? ALPHA_DECAY : ALPHA_RISE; m_dBFS -= alpha * (m_dBFS - dbfs); - update(); + // only redraw when the label needs to change + if (qRound(m_dBFS * 10) != qRound(old * 10)) + { + update(); + } } void CMeter::setSqlLevel(float dbfs) From 779f5a5b25dd57d9cc466ce5e13f8ce281f7543a Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sun, 13 Oct 2024 21:56:42 -0500 Subject: [PATCH 12/17] add to authors list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9f1a8eebe..7344f7645 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ The following people and organisations have contributed to gqrx: * Grigory Shipunov * Gwenhael Goavec-Merou * Herman Semenov +* James Yuzawa * Jaroslav Škarvada * Jeff Long * Jiawei Chen From 1e7961b6414a774ee8f03009fde8af03c520e20e Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 17 Oct 2024 08:17:27 -0400 Subject: [PATCH 13/17] fix fills --- src/qtgui/plotter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 992706551..4602c03ec 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1582,11 +1582,11 @@ void CPlotter::draw(bool newData) // Fill area between markers, even if they are off screen qreal yFill = m_PlotMode == PLOT_MODE_MAX ? yMaxD : yAvgD; if (fillMarkers && (ix) > minMarker && (ix) < maxMarker) { - abPolygon << QPointF(ixPlot, yFill + 1.0); + abPolygon << QPointF(ixPlot, yFill); } if (m_FftFill && m_PlotMode != PLOT_MODE_HISTOGRAM) { - underPolygon << QPointF(ixPlot, yFill + 1.0); + underPolygon << QPointF(ixPlot, yFill); } if (m_PlotMode == PLOT_MODE_FILLED) { From f56cfcf5ac9d1e78261b8436287fb391e387cc38 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 17 Oct 2024 20:05:29 -0400 Subject: [PATCH 14/17] fix centering on peak pixmap --- src/qtgui/plotter.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 4602c03ec..77bde5244 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1758,23 +1758,26 @@ void CPlotter::draw(bool newData) // Paint peaks with shadow if (m_PeakPixmap.isNull()) { - m_PeakPixmap = QPixmap(qRound((10.0 + shadowOffset) * m_DPR), qRound((10.0 + shadowOffset) * m_DPR)); + const qreal radius = 5.0 * m_DPR; + const qreal diameter = radius * 2; + const int half = qRound(radius + m_DPR + shadowOffset); + const int full = half * 2; + m_PeakPixmap = QPixmap(full, full); m_PeakPixmap.fill(Qt::transparent); QPainter peakPainter(&m_PeakPixmap); - peakPainter.translate(QPointF(0.5, 0.5)); + peakPainter.translate(half, half); QPen peakPen(m_MainLineCol, m_DPR); QPen peakShadowPen(Qt::black, m_DPR); - peakPen.setWidthF(m_DPR); peakPainter.setPen(peakShadowPen); peakPainter.drawEllipse( - QRectF(shadowOffset, - shadowOffset, - 10.0 * m_DPR, 10.0 * m_DPR)); + QRectF(shadowOffset - radius, + shadowOffset - radius, + diameter, diameter)); peakPainter.setPen(peakPen); peakPainter.drawEllipse( - QRectF(0, - 0, - 10.0 * m_DPR, 10.0 * m_DPR)); + QRectF(-radius, + -radius, + diameter, diameter)); } const int peakPixmapOffset = m_PeakPixmap.width() / 2; for(auto peakx : m_Peaks.keys()) { From 22d2adc01514ecb7e02a31cf324d4118adf62913 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Thu, 17 Oct 2024 21:35:29 -0400 Subject: [PATCH 15/17] use two draws for waterfall --- src/qtgui/plotter.cpp | 58 ++++++++++++++++++++++++++----------------- src/qtgui/plotter.h | 1 - 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 77bde5244..208cf4184 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1036,7 +1036,7 @@ void CPlotter::resizeEvent(QResizeEvent* ) // New waterfall, create blank area else if (m_WaterfallImage.isNull()) { - m_WaterfallImage = QImage(w, wfHeight * 2, QImage::Format_RGB32); + m_WaterfallImage = QImage(w, wfHeight, QImage::Format_RGB32); m_WaterfallImage.setDevicePixelRatio(m_DPR); m_WaterfallImage.fill(Qt::black); m_WaterfallOffset = wfHeight; @@ -1046,17 +1046,21 @@ void CPlotter::resizeEvent(QResizeEvent* ) // invalidate time else { + const int wfHeightOld = m_WaterfallImage.height(); QImage oldWaterfall = m_WaterfallImage.scaled( - w, m_WaterfallImage.height(), + w, wfHeightOld, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_WaterfallImage = QImage(w, wfHeight * 2, QImage::Format_RGB32); + m_WaterfallImage = QImage(w, wfHeight, QImage::Format_RGB32); m_WaterfallImage.setDevicePixelRatio(m_DPR); m_WaterfallImage.fill(Qt::black); - memcpy(m_WaterfallImage.scanLine(wfHeight), oldWaterfall.scanLine(m_WaterfallOffset), - m_WaterfallImage.bytesPerLine() * std::min(wfHeight, m_WaterfallHeight)); + const int firstHeight = std::min(wfHeight, wfHeightOld - m_WaterfallOffset); + memcpy(m_WaterfallImage.scanLine(0), oldWaterfall.scanLine(m_WaterfallOffset), + m_WaterfallImage.bytesPerLine() * firstHeight); + const int secondHeight = std::min(wfHeight - firstHeight, m_WaterfallOffset); + memcpy(m_WaterfallImage.scanLine(firstHeight), oldWaterfall.scanLine(0), + m_WaterfallImage.bytesPerLine() * secondHeight); m_WaterfallOffset = wfHeight; } - m_WaterfallHeight = wfHeight; // Invalidate on resize m_MaxHoldValid = false; @@ -1064,7 +1068,7 @@ void CPlotter::resizeEvent(QResizeEvent* ) m_histIIRValid = false; // Do not need to invalidate IIR data (just histogram IIR) - // Waterfall accumulator my be the wrong size now, so invalidate. + // Waterfall accumulator may be the wrong size now, so invalidate. if (msec_per_wfline > 0) clearWaterfallBuf(); @@ -1084,24 +1088,35 @@ void CPlotter::paintEvent(QPaintEvent *) QPainter painter(this); + int plotWidthT = 0; int plotHeightT = 0; if (!m_2DPixmap.isNull()) { const int plotWidthS = m_2DPixmap.width(); const int plotHeightS = m_2DPixmap.height(); - const QRect plotRectS(0, 0, plotWidthS, plotHeightS); + const QRectF plotRectS(0.0, 0.0, plotWidthS, plotHeightS); - const int plotWidthT = qRound((qreal)plotWidthS / m_DPR); + plotWidthT = qRound((qreal)plotWidthS / m_DPR); plotHeightT = qRound((qreal)plotHeightS / m_DPR); - const QRect plotRectT(0, 0, plotWidthT, plotHeightT); + const QRectF plotRectT(0.0, 0.0, plotWidthT, plotHeightT); painter.drawPixmap(plotRectT, m_2DPixmap, plotRectS); } if (!m_WaterfallImage.isNull()) { - painter.drawImage(QPoint(0, plotHeightT), m_WaterfallImage, - QRect(0, m_WaterfallOffset, m_WaterfallImage.width(), m_WaterfallHeight)); + const int wfWidth = m_WaterfallImage.width(); + const int wfHeight = m_WaterfallImage.height(); + const int firstHeightS = wfHeight - m_WaterfallOffset; + const qreal firstHeightT = firstHeightS / m_DPR; + const qreal secondHeightT = m_WaterfallOffset / m_DPR; + // draw the waterfall in two parts based on the location of the offset: + // the first draw is the section below the offset to be drawm at top + painter.drawImage(QRectF(0.0, plotHeightT, plotWidthT, firstHeightT), m_WaterfallImage, + QRectF(0.0, m_WaterfallOffset, wfWidth, firstHeightS)); + // the second draw is the section above the offset to be drawn below + painter.drawImage(QRectF(0.0, plotHeightT + firstHeightT, plotWidthT, secondHeightT), m_WaterfallImage, + QRectF(0.0, 0.0, wfWidth, m_WaterfallOffset)); } } @@ -1411,17 +1426,10 @@ void CPlotter::draw(bool newData) wf_valid_since_ms = tnow_ms; tlast_wf_drawn_ms = tnow_ms; - // move current offset up one line - // if the offset was zero, copy the top half of the waterfall - // to the bottom half of the waterfall and reset offset - if(m_WaterfallOffset-- == 0) - { - memcpy(m_WaterfallImage.scanLine(m_WaterfallHeight + 1), m_WaterfallImage.scanLine(0), - m_WaterfallImage.bytesPerLine() * (m_WaterfallHeight - 1)); - m_WaterfallOffset = m_WaterfallHeight; - } - - + // move the offset "up" + // this changes how the resulting waterfall is drawn + // it is more efficient than moving all of the image scan lines + m_WaterfallOffset--; // draw new line of fft data at top of waterfall bitmap // draw black areas where data will not be draw memset(m_WaterfallImage.scanLine(m_WaterfallOffset), 0, m_WaterfallImage.bytesPerLine()); @@ -1444,6 +1452,10 @@ void CPlotter::draw(bool newData) cidx = std::max(std::min(cidx, 255), 0); m_WaterfallImage.setPixel(ix, m_WaterfallOffset, m_ColorTbl[255 - cidx].rgb()); } + if(m_WaterfallOffset == 0) + { + m_WaterfallOffset = m_WaterfallImage.height(); + } wf_avg_count = 0; if (msec_per_wfline > 0) diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index 5971587ba..10ca83c7b 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -272,7 +272,6 @@ public slots: QPixmap m_OverlayPixmap; // Grid, axes ... things that need to be drawn infrequently QPixmap m_PeakPixmap; QImage m_WaterfallImage; - int m_WaterfallHeight; int m_WaterfallOffset; QColor m_ColorTbl[256]; QSize m_Size; From 9ac42c622afb5936e390b1688aa039a3f97aaa9c Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Fri, 18 Oct 2024 18:28:38 -0400 Subject: [PATCH 16/17] fix no plotter case --- src/qtgui/plotter.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 208cf4184..c8bfe2d2a 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1088,7 +1088,6 @@ void CPlotter::paintEvent(QPaintEvent *) QPainter painter(this); - int plotWidthT = 0; int plotHeightT = 0; if (!m_2DPixmap.isNull()) { @@ -1096,7 +1095,7 @@ void CPlotter::paintEvent(QPaintEvent *) const int plotHeightS = m_2DPixmap.height(); const QRectF plotRectS(0.0, 0.0, plotWidthS, plotHeightS); - plotWidthT = qRound((qreal)plotWidthS / m_DPR); + const int plotWidthT = qRound((qreal)plotWidthS / m_DPR); plotHeightT = qRound((qreal)plotHeightS / m_DPR); const QRectF plotRectT(0.0, 0.0, plotWidthT, plotHeightT); @@ -1106,16 +1105,17 @@ void CPlotter::paintEvent(QPaintEvent *) if (!m_WaterfallImage.isNull()) { const int wfWidth = m_WaterfallImage.width(); + const int wfWidthT = qRound((qreal)wfWidth / m_DPR); const int wfHeight = m_WaterfallImage.height(); const int firstHeightS = wfHeight - m_WaterfallOffset; const qreal firstHeightT = firstHeightS / m_DPR; const qreal secondHeightT = m_WaterfallOffset / m_DPR; // draw the waterfall in two parts based on the location of the offset: // the first draw is the section below the offset to be drawm at top - painter.drawImage(QRectF(0.0, plotHeightT, plotWidthT, firstHeightT), m_WaterfallImage, + painter.drawImage(QRectF(0.0, plotHeightT, wfWidthT, firstHeightT), m_WaterfallImage, QRectF(0.0, m_WaterfallOffset, wfWidth, firstHeightS)); // the second draw is the section above the offset to be drawn below - painter.drawImage(QRectF(0.0, plotHeightT + firstHeightT, plotWidthT, secondHeightT), m_WaterfallImage, + painter.drawImage(QRectF(0.0, plotHeightT + firstHeightT, wfWidthT, secondHeightT), m_WaterfallImage, QRectF(0.0, 0.0, wfWidth, m_WaterfallOffset)); } } @@ -1775,7 +1775,10 @@ void CPlotter::draw(bool newData) const int half = qRound(radius + m_DPR + shadowOffset); const int full = half * 2; m_PeakPixmap = QPixmap(full, full); - m_PeakPixmap.fill(Qt::transparent); + QColor bg = QColor(m_MainLineCol); + bg.setAlpha(128); + m_PeakPixmap.fill(bg); + //m_PeakPixmap.fill(Qt::transparent); QPainter peakPainter(&m_PeakPixmap); peakPainter.translate(half, half); QPen peakPen(m_MainLineCol, m_DPR); From e3a8bb07c05a8c2f6e405292ebd00ee1219c924b Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Fri, 18 Oct 2024 20:58:24 -0400 Subject: [PATCH 17/17] fix peak alignment --- src/qtgui/plotter.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index c8bfe2d2a..a889f1b69 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -1772,13 +1772,10 @@ void CPlotter::draw(bool newData) { const qreal radius = 5.0 * m_DPR; const qreal diameter = radius * 2; - const int half = qRound(radius + m_DPR + shadowOffset); + const int half = qRound(radius + m_DPR * 2); const int full = half * 2; m_PeakPixmap = QPixmap(full, full); - QColor bg = QColor(m_MainLineCol); - bg.setAlpha(128); - m_PeakPixmap.fill(bg); - //m_PeakPixmap.fill(Qt::transparent); + m_PeakPixmap.fill(Qt::transparent); QPainter peakPainter(&m_PeakPixmap); peakPainter.translate(half, half); QPen peakPen(m_MainLineCol, m_DPR); @@ -1794,7 +1791,7 @@ void CPlotter::draw(bool newData) -radius, diameter, diameter)); } - const int peakPixmapOffset = m_PeakPixmap.width() / 2; + const int peakPixmapOffset = m_PeakPixmap.width() / 2 + 1; for(auto peakx : m_Peaks.keys()) { const qreal peakxPlot = (qreal)peakx; const qreal peakv = m_Peaks.value(peakx);