Skip to content

Commit

Permalink
Merge pull request #1304 from gqrx-sdr/waterfall-qimage
Browse files Browse the repository at this point in the history
Use a QImage for the waterfall
  • Loading branch information
argilo authored Oct 5, 2023
2 parents 79b2c8a + c8113af commit 05246df
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 41 deletions.
66 changes: 26 additions & 40 deletions src/qtgui/plotter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/qtgui/plotter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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{};
Expand Down

0 comments on commit 05246df

Please sign in to comment.