diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp index 990bba4f0..3b2900e35 100644 --- a/src/qtgui/plotter.cpp +++ b/src/qtgui/plotter.cpp @@ -149,7 +149,7 @@ CPlotter::CPlotter(QWidget *parent) : QFrame(parent) m_DrawOverlay = true; m_2DPixmap = QPixmap(); m_OverlayPixmap = QPixmap(); - m_WaterfallPixmap = QPixmap(); + m_WaterfallImage = QImage(); m_Size = QSize(0,0); m_GrabPosition = 0; m_Percent2DScreen = 35; //percent of screen used for 2D display @@ -586,10 +586,10 @@ void CPlotter::setWaterfallSpan(quint64 span_ms) { wf_span = span_ms; quint64 tnow = QDateTime::currentMSecsSinceEpoch(); - if (!m_WaterfallPixmap.isNull()) { + if (!m_WaterfallImage.isNull()) { wf_epoch = tnow; wf_count = 0; - msec_per_wfline = (double)wf_span / (qreal)m_WaterfallPixmap.height(); + msec_per_wfline = (double)wf_span / (qreal)m_WaterfallImage.height(); } wf_valid_since_ms = tnow; clearWaterfallBuf(); @@ -1022,32 +1022,31 @@ void CPlotter::resizeEvent(QResizeEvent* ) m_2DPixmap = QPixmap(w, plotHeight); m_2DPixmap.fill(QColor::fromRgba(PLOTTER_BGD_COLOR)); - // No waterfall, use null pixmap + // No waterfall, use null image if (wfHeight == 0) { - m_WaterfallPixmap = QPixmap(); + m_WaterfallImage = QImage(); } // New waterfall, create blank area - else if (m_WaterfallPixmap.isNull()) { - m_WaterfallPixmap = QPixmap(w, wfHeight); - m_WaterfallPixmap.fill(Qt::black); + else if (m_WaterfallImage.isNull()) { + m_WaterfallImage = QImage(w, wfHeight, QImage::Format_RGB32); + m_WaterfallImage.setDevicePixelRatio(m_DPR); + m_WaterfallImage.fill(Qt::black); } // Existing waterfall, rescale width but no height as that would // invalidate time else { - QPixmap oldWaterfall = m_WaterfallPixmap.scaled( - w, m_WaterfallPixmap.height(), + QImage oldWaterfall = m_WaterfallImage.scaled( + w, m_WaterfallImage.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_WaterfallPixmap = QPixmap(w, wfHeight); - m_WaterfallPixmap.fill(Qt::black); - QRect copyRect(0, 0, - qMin(w, oldWaterfall.width()), - qMin(wfHeight, oldWaterfall.height())); - QPainter painter(&m_WaterfallPixmap); - painter.drawPixmap(QPointF(0.0, 0.0), oldWaterfall, copyRect); + m_WaterfallImage = QImage(w, wfHeight, 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())); } // Invalidate on resize @@ -1090,17 +1089,9 @@ void CPlotter::paintEvent(QPaintEvent *) painter.drawPixmap(plotRectT, m_2DPixmap, plotRectS); } - if (!m_WaterfallPixmap.isNull()) + if (!m_WaterfallImage.isNull()) { - const int wfWidthS = m_WaterfallPixmap.width(); - const int wfHeightS = m_WaterfallPixmap.height(); - const QRectF wfRectS(0.0, 0.0, wfWidthS, wfHeightS); - - const int wfWidthT = qRound((qreal)wfWidthS / m_DPR); - const int wfHeightT = qRound((qreal)wfHeightS / m_DPR); - const QRectF wfRectT(0.0, plotHeightT, wfWidthT, wfHeightT); - - painter.drawPixmap(wfRectT, m_WaterfallPixmap, wfRectS); + painter.drawImage(QPointF(0.0, plotHeightT), m_WaterfallImage); } } @@ -1168,7 +1159,7 @@ void CPlotter::draw(bool newData) const qint32 maxbin = std::min(endBin + 1, m_fftDataSize - 1); const qint32 xmin = qRound((double)(minbin - startBin) * xScale); - const qint32 xmax = qRound((double)(maxbin - startBin) * xScale); + const qint32 xmax = std::min(qRound((double)(maxbin - startBin) * xScale), qRound(w)); const float frameTime = 1.0f / (float)fft_rate; @@ -1201,7 +1192,7 @@ void CPlotter::draw(bool newData) // Waterfall is advanced only if visible and running, and if there is new // data. Repaints for other reasons do not require any action here. - const bool doWaterfall = !m_WaterfallPixmap.isNull() && m_Running && newData; + const bool doWaterfall = !m_WaterfallImage.isNull() && m_Running && newData; // Draw avg line, except in max mode. Suppress if it would clutter histogram. const bool doAvgLine = m_PlotMode != PLOT_MODE_MAX @@ -1414,15 +1405,12 @@ void CPlotter::draw(bool newData) tlast_wf_drawn_ms = tnow_ms; // move current data down one line(must do before attaching a QPainter object) - m_WaterfallPixmap.scroll(0, 1, m_WaterfallPixmap.rect()); - - QPainter painter1(&m_WaterfallPixmap); + memmove(m_WaterfallImage.scanLine(1), m_WaterfallImage.scanLine(0), + m_WaterfallImage.bytesPerLine() * (m_WaterfallImage.height() - 1)); // draw new line of fft data at top of waterfall bitmap // draw black areas where data will not be draw - painter1.setPen(QPen(Qt::black)); - painter1.drawLine(0.0, 0.0, xmin - 1, 0.0); - painter1.drawLine(xmax, 0.0, w - 1, 0.0); + memset(m_WaterfallImage.scanLine(0), 0, m_WaterfallImage.bytesPerLine()); const bool useWfBuf = msec_per_wfline > 0; float _lineFactor; @@ -1437,12 +1425,10 @@ void CPlotter::draw(bool newData) for (i = 0; i < npts; ++i) { const int ix = i + xmin; - const qreal ixPlot = (qreal)ix; 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); - painter1.setPen(m_ColorTbl[255 - cidx]); - painter1.drawPoint(QPointF(ixPlot, 0)); + m_WaterfallImage.setPixel(ix, 0, m_ColorTbl[255 - cidx].rgb()); } wf_avg_count = 0; @@ -2457,8 +2443,8 @@ void CPlotter::setMarkers(qint64 a, qint64 b) void CPlotter::clearWaterfall() { - if (!m_WaterfallPixmap.isNull()) { - m_WaterfallPixmap.fill(Qt::black); + if (!m_WaterfallImage.isNull()) { + m_WaterfallImage.fill(Qt::black); } } diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h index 7e322440c..e6277df8a 100644 --- a/src/qtgui/plotter.h +++ b/src/qtgui/plotter.h @@ -259,7 +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_WaterfallPixmap; + QImage m_WaterfallImage; QColor m_ColorTbl[256]; QSize m_Size; qreal m_DPR{};