Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_plot_spectrogram.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_plot_spectrogram.h"
11#include "qwt_painter.h"
12#include "qwt_interval.h"
13#include "qwt_scale_map.h"
14#include "qwt_color_map.h"
15#include "qwt_math.h"
16
17#include <qimage.h>
18#include <qpen.h>
19#include <qpainter.h>
20#include <qthread.h>
21#include <qfuture.h>
22#include <qtconcurrentrun.h>
23
24#define DEBUG_RENDER 0
25
26#if DEBUG_RENDER
27#include <qelapsedtimer.h>
28#endif
29
30#include <algorithm>
31
32static inline bool qwtIsNaN( double d )
33{
34 // qt_is_nan is private header and qIsNaN is not inlined
35 // so we need these code here too
36
37 const uchar* ch = (const uchar*)&d;
38 if ( QSysInfo::ByteOrder == QSysInfo::BigEndian )
39 {
40 return ( ch[0] & 0x7f ) == 0x7f && ch[1] > 0xf0;
41 }
42 else
43 {
44 return ( ch[7] & 0x7f ) == 0x7f && ch[6] > 0xf0;
45 }
46}
47
48class QwtPlotSpectrogram::PrivateData
49{
50 public:
51 PrivateData()
52 : data( NULL )
53 , colorTableSize( 0 )
54 {
55 colorMap = new QwtLinearColorMap();
56 displayMode = ImageMode;
57
59#if 0
61#endif
62 }
63
64 ~PrivateData()
65 {
66 delete data;
67 delete colorMap;
68 }
69
70 void updateColorTable()
71 {
72 if ( colorMap->format() == QwtColorMap::Indexed )
73 {
74 colorTable = colorMap->colorTable256();
75 }
76 else
77 {
78 if ( colorTableSize == 0 )
79 colorTable.clear();
80 else
81 colorTable = colorMap->colorTable( colorTableSize );
82 }
83 }
84
85 QwtRasterData* data;
86 QwtColorMap* colorMap;
87 DisplayModes displayMode;
88
89 QList< double > contourLevels;
90 QPen defaultContourPen;
92
93 int colorTableSize;
94 QVector< QRgb > colorTable;
95};
96
109 : QwtPlotRasterItem( title )
110{
111 m_data = new PrivateData();
112
115
116 setZ( 8.0 );
117}
118
121{
122 delete m_data;
123}
124
130
142{
143 if ( on != bool( mode & m_data->displayMode ) )
144 {
145 if ( on )
146 m_data->displayMode |= mode;
147 else
148 m_data->displayMode &= ~mode;
149 }
150
152 itemChanged();
153}
154
162{
163 return ( m_data->displayMode & mode );
164}
165
178{
179 if ( colorMap == NULL )
180 return;
181
182 if ( colorMap != m_data->colorMap )
183 {
184 delete m_data->colorMap;
185 m_data->colorMap = colorMap;
186 }
187
188 m_data->updateColorTable();
189
191
193 itemChanged();
194}
195
201{
202 return m_data->colorMap;
203}
204
225{
226 numColors = qMax( numColors, 0 );
227 if ( numColors != m_data->colorTableSize )
228 {
229 m_data->colorTableSize = numColors;
230 m_data->updateColorTable();
232 }
233}
239{
240 return m_data->colorTableSize;
241}
242
257 const QColor& color, qreal width, Qt::PenStyle style )
258{
259 setDefaultContourPen( QPen( color, width, style ) );
260}
261
273{
274 if ( pen != m_data->defaultContourPen )
275 {
276 m_data->defaultContourPen = pen;
277
279 itemChanged();
280 }
281}
282
288{
289 return m_data->defaultContourPen;
290}
291
303QPen QwtPlotSpectrogram::contourPen( double level ) const
304{
305 if ( m_data->data == NULL || m_data->colorMap == NULL )
306 return QPen();
307
308 const QwtInterval intensityRange = m_data->data->interval(Qt::ZAxis);
309 const QColor c( m_data->colorMap->rgb( intensityRange, level ) );
310
311 return QPen( c );
312}
313
325 QwtRasterData::ConrecFlag flag, bool on )
326{
327 if ( bool( m_data->conrecFlags & flag ) == on )
328 return;
329
330 if ( on )
331 m_data->conrecFlags |= flag;
332 else
333 m_data->conrecFlags &= ~flag;
334
335 itemChanged();
336}
337
351 QwtRasterData::ConrecFlag flag ) const
352{
353 return m_data->conrecFlags & flag;
354}
355
366{
367 m_data->contourLevels = levels;
368 std::sort( m_data->contourLevels.begin(), m_data->contourLevels.end() );
369
371 itemChanged();
372}
373
383{
384 return m_data->contourLevels;
385}
386
394{
395 if ( data != m_data->data )
396 {
397 delete m_data->data;
398 m_data->data = data;
399
401 itemChanged();
402 }
403}
404
410{
411 return m_data->data;
412}
413
419{
420 return m_data->data;
421}
422
433{
434 if ( m_data->data == NULL )
435 return QwtInterval();
436
437 return m_data->data->interval( axis );
438}
439
456QRectF QwtPlotSpectrogram::pixelHint( const QRectF& area ) const
457{
458 if ( m_data->data == NULL )
459 return QRectF();
460
461 return m_data->data->pixelHint( area );
462}
463
481 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
482 const QRectF& area, const QSize& imageSize ) const
483{
484 if ( imageSize.isEmpty() || m_data->data == NULL
485 || m_data->colorMap == NULL )
486 {
487 return QImage();
488 }
489
490 const QwtInterval intensityRange = m_data->data->interval( Qt::ZAxis );
491 if ( !intensityRange.isValid() )
492 return QImage();
493
494 const QImage::Format format = ( m_data->colorMap->format() == QwtColorMap::RGB )
495 ? QImage::Format_ARGB32 : QImage::Format_Indexed8;
496
497 QImage image( imageSize, format );
498
499 if ( m_data->colorMap->format() == QwtColorMap::Indexed )
500 image.setColorTable( m_data->colorMap->colorTable256() );
501
502 m_data->data->initRaster( area, image.size() );
503
504#if DEBUG_RENDER
505 QElapsedTimer time;
506 time.start();
507#endif
508
509#if !defined( QT_NO_QFUTURE )
510 uint numThreads = renderThreadCount();
511
512 if ( numThreads <= 0 )
513 numThreads = QThread::idealThreadCount();
514
515 if ( numThreads <= 0 )
516 numThreads = 1;
517
518 const int numRows = imageSize.height() / numThreads;
519
520 QVector< QFuture< void > > futures;
521 futures.reserve( numThreads - 1 );
522
523 for ( uint i = 0; i < numThreads; i++ )
524 {
525 QRect tile( 0, i * numRows, image.width(), numRows );
526 if ( i == numThreads - 1 )
527 {
528 tile.setHeight( image.height() - i * numRows );
529 renderTile( xMap, yMap, tile, &image );
530 }
531 else
532 {
533 futures += QtConcurrent::run(
534#if QT_VERSION >= 0x060000
536#else
538#endif
539 xMap, yMap, tile, &image );
540 }
541 }
542
543 for ( int i = 0; i < futures.size(); i++ )
544 futures[i].waitForFinished();
545
546#else
547 const QRect tile( 0, 0, image.width(), image.height() );
548 renderTile( xMap, yMap, tile, &image );
549#endif
550
551#if DEBUG_RENDER
552 const qint64 elapsed = time.elapsed();
553 qDebug() << "renderImage" << imageSize << elapsed;
554#endif
555
556 m_data->data->discardRaster();
557
558 return image;
559}
560
573 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
574 const QRect& tile, QImage* image ) const
575{
576 const QwtInterval range = m_data->data->interval( Qt::ZAxis );
577 if ( range.width() <= 0.0 )
578 return;
579
580 const bool hasGaps = !m_data->data->testAttribute( QwtRasterData::WithoutGaps );
581
582 if ( m_data->colorMap->format() == QwtColorMap::RGB )
583 {
584 const int numColors = m_data->colorTable.size();
585 const QRgb* rgbTable = m_data->colorTable.constData();
586 const QwtColorMap* colorMap = m_data->colorMap;
587
588 for ( int y = tile.top(); y <= tile.bottom(); y++ )
589 {
590 const double ty = yMap.invTransform( y );
591
592 QRgb* line = reinterpret_cast< QRgb* >( image->scanLine( y ) );
593 line += tile.left();
594
595 for ( int x = tile.left(); x <= tile.right(); x++ )
596 {
597 const double tx = xMap.invTransform( x );
598
599 const double value = m_data->data->value( tx, ty );
600
601 if ( hasGaps && qwtIsNaN( value ) )
602 {
603 *line++ = 0u;
604 }
605 else if ( numColors == 0 )
606 {
607 *line++ = colorMap->rgb( range, value );
608 }
609 else
610 {
611 const uint index = colorMap->colorIndex( numColors, range, value );
612 *line++ = rgbTable[index];
613 }
614 }
615 }
616 }
617 else if ( m_data->colorMap->format() == QwtColorMap::Indexed )
618 {
619 for ( int y = tile.top(); y <= tile.bottom(); y++ )
620 {
621 const double ty = yMap.invTransform( y );
622
623 unsigned char* line = image->scanLine( y );
624 line += tile.left();
625
626 for ( int x = tile.left(); x <= tile.right(); x++ )
627 {
628 const double tx = xMap.invTransform( x );
629
630 const double value = m_data->data->value( tx, ty );
631
632 if ( hasGaps && qwtIsNaN( value ) )
633 {
634 *line++ = 0;
635 }
636 else
637 {
638 const uint index = m_data->colorMap->colorIndex( 256, range, value );
639 *line++ = static_cast< unsigned char >( index );
640 }
641 }
642 }
643 }
644}
645
664 const QRectF& area, const QRect& rect ) const
665{
666 QSize raster = rect.size() / 2;
667
668 const QRectF pixelRect = pixelHint( area );
669 if ( !pixelRect.isEmpty() )
670 {
671 const QSize res( qwtCeil( rect.width() / pixelRect.width() ),
672 qwtCeil( rect.height() / pixelRect.height() ) );
673 raster = raster.boundedTo( res );
674 }
675
676 return raster;
677}
678
690 const QRectF& rect, const QSize& raster ) const
691{
692 if ( m_data->data == NULL )
694
695 return m_data->data->contourLines( rect, raster,
696 m_data->contourLevels, m_data->conrecFlags );
697}
698
710 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
711 const QwtRasterData::ContourLines& contourLines ) const
712{
713 if ( m_data->data == NULL )
714 return;
715
716 const int numLevels = m_data->contourLevels.size();
717 for ( int l = 0; l < numLevels; l++ )
718 {
719 const double level = m_data->contourLevels[l];
720
721 QPen pen = defaultContourPen();
722 if ( pen.style() == Qt::NoPen )
723 pen = contourPen( level );
724
725 if ( pen.style() == Qt::NoPen )
726 continue;
727
728 painter->setPen( pen );
729
730 const QPolygonF& lines = contourLines[level];
731 for ( int i = 0; i < lines.size(); i += 2 )
732 {
733 const QPointF p1( xMap.transform( lines[i].x() ),
734 yMap.transform( lines[i].y() ) );
735 const QPointF p2( xMap.transform( lines[i + 1].x() ),
736 yMap.transform( lines[i + 1].y() ) );
737
738 QwtPainter::drawLine( painter, p1, p2 );
739 }
740 }
741}
742
754void QwtPlotSpectrogram::draw( QPainter* painter,
755 const QwtScaleMap& xMap, const QwtScaleMap& yMap,
756 const QRectF& canvasRect ) const
757{
758 if ( m_data->displayMode & ImageMode )
759 QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect );
760
761 if ( m_data->displayMode & ContourMode )
762 {
763 // Add some pixels at the borders
764 const int margin = 2;
765 QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin,
766 canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin );
767
768 QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect );
769
770 const QRectF br = boundingRect();
771 if ( br.isValid() )
772 {
773 area &= br;
774 if ( area.isEmpty() )
775 return;
776
777 rasterRect = QwtScaleMap::transform( xMap, yMap, area );
778 }
779
780 QSize raster = contourRasterSize( area, rasterRect.toRect() );
781 raster = raster.boundedTo( rasterRect.toRect().size() );
782 if ( raster.isValid() )
783 {
784 const QwtRasterData::ContourLines lines =
785 renderContourLines( area, raster );
786
787 drawContourLines( painter, xMap, yMap, lines );
788 }
789 }
790}
QwtColorMap is used to map values into colors.
virtual uint colorIndex(int numColors, const QwtInterval &interval, double value) const
Map a value of a given interval into a color index.
@ RGB
The map is intended to map into RGB values.
virtual QVector< QRgb > colorTable(int numColors) const
virtual QVector< QRgb > colorTable256() const
virtual QRgb rgb(const QwtInterval &interval, double value) const =0
A class representing an interval.
double width() const
Return the width of an interval.
bool isValid() const
QwtLinearColorMap builds a color map from color stops.
static void drawLine(QPainter *, qreal x1, qreal y1, qreal x2, qreal y2)
Wrapper for QPainter::drawLine()
virtual void legendChanged()
void setZ(double z)
Set the z value.
void setItemAttribute(ItemAttribute, bool on=true)
@ Rtti_PlotSpectrogram
For QwtPlotSpectrogram.
virtual void itemChanged()
@ Legend
The item is represented on the legend.
uint renderThreadCount() const
A class, which displays raster data.
virtual void draw(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect) const override
Draw the raster data.
virtual QRectF boundingRect() const override
void setColorTableSize(int numColors)
virtual QPen contourPen(double level) const
Calculate the pen for a contour line.
QFlags< DisplayMode > DisplayModes
virtual QSize contourRasterSize(const QRectF &, const QRect &) const
Return the raster to be used by the CONREC contour algorithm.
virtual QwtRasterData::ContourLines renderContourLines(const QRectF &rect, const QSize &raster) const
QList< double > contourLevels() const
virtual QRectF pixelHint(const QRectF &) const override
Pixel hint.
void setDisplayMode(DisplayMode, bool on=true)
virtual QwtInterval interval(Qt::Axis) const override
void setColorMap(QwtColorMap *)
void setContourLevels(const QList< double > &)
void setData(QwtRasterData *data)
bool testConrecFlag(QwtRasterData::ConrecFlag) const
virtual void drawContourLines(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QwtRasterData::ContourLines &) const
void renderTile(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRect &tile, QImage *) const
Render a tile of an image.
@ ContourMode
The data is displayed using contour lines.
@ ImageMode
The values are mapped to colors using a color map.
QwtPlotSpectrogram(const QString &title=QString())
const QwtRasterData * data() const
const QwtColorMap * colorMap() const
bool testDisplayMode(DisplayMode) const
virtual void draw(QPainter *, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect) const override
Draw the spectrogram.
void setConrecFlag(QwtRasterData::ConrecFlag, bool on)
virtual int rtti() const override
virtual ~QwtPlotSpectrogram()
Destructor.
void setDefaultContourPen(const QColor &, qreal width=0.0, Qt::PenStyle=Qt::SolidLine)
virtual QImage renderImage(const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &area, const QSize &imageSize) const override
Render an image from data and color map.
QwtRasterData defines an interface to any type of raster data.
virtual QRectF pixelHint(const QRectF &) const
Pixel hint.
bool testAttribute(Attribute) const
QMap< double, QPolygonF > ContourLines
Contour lines.
QFlags< ConrecFlag > ConrecFlags
virtual void discardRaster()
Discard a raster.
virtual double value(double x, double y) const =0
virtual void initRaster(const QRectF &, const QSize &raster)
Initialize a raster.
virtual ContourLines contourLines(const QRectF &rect, const QSize &raster, const QList< double > &levels, ConrecFlags) const
virtual QwtInterval interval(Qt::Axis) const =0
ConrecFlag
Flags to modify the contour algorithm.
@ IgnoreOutOfRange
Ignore all values, that are out of range.
@ IgnoreAllVerticesOnLevel
Ignore all vertices on the same level.
A scale map.
double transform(double s) const
double invTransform(double p) const