00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_plot_spectrogram.h"
00011 #include "qwt_painter.h"
00012 #include "qwt_interval.h"
00013 #include "qwt_scale_map.h"
00014 #include "qwt_color_map.h"
00015 #include <qimage.h>
00016 #include <qpen.h>
00017 #include <qpainter.h>
00018 #include <qmath.h>
00019 #include <qalgorithms.h>
00020 #include <qthread.h>
00021 #include <qfuture.h>
00022 #include <qtconcurrentrun.h>
00023
00024 #define DEBUG_RENDER 0
00025
00026 #if DEBUG_RENDER
00027 #include <QElapsedTimer>
00028 #endif
00029
00030 static inline bool qwtIsNaN( double d )
00031 {
00032
00033
00034
00035 const uchar *ch = (const uchar *)&d;
00036 if ( QSysInfo::ByteOrder == QSysInfo::BigEndian )
00037 {
00038 return (ch[0] & 0x7f) == 0x7f && ch[1] > 0xf0;
00039 }
00040 else
00041 {
00042 return (ch[7] & 0x7f) == 0x7f && ch[6] > 0xf0;
00043 }
00044 }
00045
00046 class QwtPlotSpectrogram::PrivateData
00047 {
00048 public:
00049 PrivateData():
00050 data( NULL ),
00051 maxRGBColorTableSize( 0 )
00052 {
00053 colorMap = new QwtLinearColorMap();
00054 displayMode = ImageMode;
00055
00056 conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel;
00057 #if 0
00058 conrecFlags |= QwtRasterData::IgnoreOutOfRange;
00059 #endif
00060 }
00061 ~PrivateData()
00062 {
00063 delete data;
00064 delete colorMap;
00065 }
00066
00067 void updateColorTable()
00068 {
00069 if ( colorMap->format() == QwtColorMap::Indexed )
00070 {
00071 colorTable = colorMap->colorTable256();
00072 }
00073 else
00074 {
00075 if ( maxRGBColorTableSize == 0 )
00076 colorTable.clear();
00077 else
00078 colorTable = colorMap->colorTable( maxRGBColorTableSize );
00079 }
00080 }
00081
00082 QwtRasterData *data;
00083 QwtColorMap *colorMap;
00084 DisplayModes displayMode;
00085
00086 QList<double> contourLevels;
00087 QPen defaultContourPen;
00088 QwtRasterData::ConrecFlags conrecFlags;
00089
00090 int maxRGBColorTableSize;
00091 QVector<QRgb> colorTable;
00092 };
00093
00105 QwtPlotSpectrogram::QwtPlotSpectrogram( const QString &title ):
00106 QwtPlotRasterItem( title )
00107 {
00108 d_data = new PrivateData();
00109
00110 setItemAttribute( QwtPlotItem::AutoScale, true );
00111 setItemAttribute( QwtPlotItem::Legend, false );
00112
00113 setZ( 8.0 );
00114 }
00115
00117 QwtPlotSpectrogram::~QwtPlotSpectrogram()
00118 {
00119 delete d_data;
00120 }
00121
00123 int QwtPlotSpectrogram::rtti() const
00124 {
00125 return QwtPlotItem::Rtti_PlotSpectrogram;
00126 }
00127
00138 void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on )
00139 {
00140 if ( on != bool( mode & d_data->displayMode ) )
00141 {
00142 if ( on )
00143 d_data->displayMode |= mode;
00144 else
00145 d_data->displayMode &= ~mode;
00146 }
00147
00148 legendChanged();
00149 itemChanged();
00150 }
00151
00158 bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const
00159 {
00160 return ( d_data->displayMode & mode );
00161 }
00162
00174 void QwtPlotSpectrogram::setColorMap( QwtColorMap *colorMap )
00175 {
00176 if ( colorMap == NULL )
00177 return;
00178
00179 if ( colorMap != d_data->colorMap )
00180 {
00181 delete d_data->colorMap;
00182 d_data->colorMap = colorMap;
00183 }
00184
00185 d_data->updateColorTable();
00186
00187 invalidateCache();
00188
00189 legendChanged();
00190 itemChanged();
00191 }
00192
00197 const QwtColorMap *QwtPlotSpectrogram::colorMap() const
00198 {
00199 return d_data->colorMap;
00200 }
00201
00202 void QwtPlotSpectrogram::setMaxRGBTableSize( int numColors )
00203 {
00204 numColors = qMax( numColors, 0 );
00205 if ( numColors != d_data->maxRGBColorTableSize )
00206 {
00207 d_data->maxRGBColorTableSize = numColors;
00208 d_data->updateColorTable();
00209 invalidateCache();
00210 }
00211 }
00212
00213 int QwtPlotSpectrogram::maxRGBTableSize() const
00214 {
00215 return d_data->maxRGBColorTableSize;
00216 }
00217
00231 void QwtPlotSpectrogram::setDefaultContourPen(
00232 const QColor &color, qreal width, Qt::PenStyle style )
00233 {
00234 setDefaultContourPen( QPen( color, width, style ) );
00235 }
00236
00247 void QwtPlotSpectrogram::setDefaultContourPen( const QPen &pen )
00248 {
00249 if ( pen != d_data->defaultContourPen )
00250 {
00251 d_data->defaultContourPen = pen;
00252
00253 legendChanged();
00254 itemChanged();
00255 }
00256 }
00257
00262 QPen QwtPlotSpectrogram::defaultContourPen() const
00263 {
00264 return d_data->defaultContourPen;
00265 }
00266
00278 QPen QwtPlotSpectrogram::contourPen( double level ) const
00279 {
00280 if ( d_data->data == NULL || d_data->colorMap == NULL )
00281 return QPen();
00282
00283 const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis);
00284 const QColor c( d_data->colorMap->rgb( intensityRange, level ) );
00285
00286 return QPen( c );
00287 }
00288
00299 void QwtPlotSpectrogram::setConrecFlag(
00300 QwtRasterData::ConrecFlag flag, bool on )
00301 {
00302 if ( bool( d_data->conrecFlags & flag ) == on )
00303 return;
00304
00305 if ( on )
00306 d_data->conrecFlags |= flag;
00307 else
00308 d_data->conrecFlags &= ~flag;
00309
00310 itemChanged();
00311 }
00312
00325 bool QwtPlotSpectrogram::testConrecFlag(
00326 QwtRasterData::ConrecFlag flag ) const
00327 {
00328 return d_data->conrecFlags & flag;
00329 }
00330
00340 void QwtPlotSpectrogram::setContourLevels( const QList<double> &levels )
00341 {
00342 d_data->contourLevels = levels;
00343 qSort( d_data->contourLevels );
00344
00345 legendChanged();
00346 itemChanged();
00347 }
00348
00357 QList<double> QwtPlotSpectrogram::contourLevels() const
00358 {
00359 return d_data->contourLevels;
00360 }
00361
00368 void QwtPlotSpectrogram::setData( QwtRasterData *data )
00369 {
00370 if ( data != d_data->data )
00371 {
00372 delete d_data->data;
00373 d_data->data = data;
00374
00375 invalidateCache();
00376 itemChanged();
00377 }
00378 }
00379
00384 const QwtRasterData *QwtPlotSpectrogram::data() const
00385 {
00386 return d_data->data;
00387 }
00388
00393 QwtRasterData *QwtPlotSpectrogram::data()
00394 {
00395 return d_data->data;
00396 }
00397
00407 QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const
00408 {
00409 if ( d_data->data == NULL )
00410 return QwtInterval();
00411
00412 return d_data->data->interval( axis );
00413 }
00414
00431 QRectF QwtPlotSpectrogram::pixelHint( const QRectF &area ) const
00432 {
00433 if ( d_data->data == NULL )
00434 return QRectF();
00435
00436 return d_data->data->pixelHint( area );
00437 }
00438
00455 QImage QwtPlotSpectrogram::renderImage(
00456 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00457 const QRectF &area, const QSize &imageSize ) const
00458 {
00459 if ( imageSize.isEmpty() || d_data->data == NULL
00460 || d_data->colorMap == NULL )
00461 {
00462 return QImage();
00463 }
00464
00465 const QwtInterval intensityRange = d_data->data->interval( Qt::ZAxis );
00466 if ( !intensityRange.isValid() )
00467 return QImage();
00468
00469 const QImage::Format format = ( d_data->colorMap->format() == QwtColorMap::RGB )
00470 ? QImage::Format_ARGB32 : QImage::Format_Indexed8;
00471
00472 QImage image( imageSize, format );
00473
00474 if ( d_data->colorMap->format() == QwtColorMap::Indexed )
00475 image.setColorTable( d_data->colorMap->colorTable256() );
00476
00477 d_data->data->initRaster( area, image.size() );
00478
00479 #if DEBUG_RENDER
00480 QElapsedTimer time;
00481 time.start();
00482 #endif
00483
00484 #if !defined(QT_NO_QFUTURE)
00485 uint numThreads = renderThreadCount();
00486
00487 if ( numThreads <= 0 )
00488 numThreads = QThread::idealThreadCount();
00489
00490 if ( numThreads <= 0 )
00491 numThreads = 1;
00492
00493 const int numRows = imageSize.height() / numThreads;
00494
00495 QList< QFuture<void> > futures;
00496 for ( uint i = 0; i < numThreads; i++ )
00497 {
00498 QRect tile( 0, i * numRows, image.width(), numRows );
00499 if ( i == numThreads - 1 )
00500 {
00501 tile.setHeight( image.height() - i * numRows );
00502 renderTile( xMap, yMap, tile, &image );
00503 }
00504 else
00505 {
00506 futures += QtConcurrent::run(
00507 this, &QwtPlotSpectrogram::renderTile,
00508 xMap, yMap, tile, &image );
00509 }
00510 }
00511 for ( int i = 0; i < futures.size(); i++ )
00512 futures[i].waitForFinished();
00513
00514 #else
00515 const QRect tile( 0, 0, image.width(), image.height() );
00516 renderTile( xMap, yMap, tile, &image );
00517 #endif
00518
00519 #if DEBUG_RENDER
00520 const qint64 elapsed = time.elapsed();
00521 qDebug() << "renderImage" << imageSize << elapsed;
00522 #endif
00523
00524 d_data->data->discardRaster();
00525
00526 return image;
00527 }
00528
00540 void QwtPlotSpectrogram::renderTile(
00541 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00542 const QRect &tile, QImage *image ) const
00543 {
00544 const QwtInterval range = d_data->data->interval( Qt::ZAxis );
00545 if ( range.width() <= 0.0 )
00546 return;
00547
00548 const bool hasGaps = !d_data->data->testAttribute( QwtRasterData::WithoutGaps );
00549
00550 if ( d_data->colorMap->format() == QwtColorMap::RGB )
00551 {
00552 const int numColors = d_data->colorTable.size();
00553 const QRgb *rgbTable = d_data->colorTable.constData();
00554 const QwtColorMap *colorMap = d_data->colorMap;
00555
00556 for ( int y = tile.top(); y <= tile.bottom(); y++ )
00557 {
00558 const double ty = yMap.invTransform( y );
00559
00560 QRgb *line = reinterpret_cast<QRgb *>( image->scanLine( y ) );
00561 line += tile.left();
00562
00563 for ( int x = tile.left(); x <= tile.right(); x++ )
00564 {
00565 const double tx = xMap.invTransform( x );
00566
00567 const double value = d_data->data->value( tx, ty );
00568
00569 if ( hasGaps && qwtIsNaN( value ) )
00570 {
00571 *line++ = 0u;
00572 }
00573 else if ( numColors == 0 )
00574 {
00575 *line++ = colorMap->rgb( range, value );
00576 }
00577 else
00578 {
00579 const uint index = colorMap->colorIndex( numColors, range, value );
00580 *line++ = rgbTable[index];
00581 }
00582 }
00583 }
00584 }
00585 else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
00586 {
00587 for ( int y = tile.top(); y <= tile.bottom(); y++ )
00588 {
00589 const double ty = yMap.invTransform( y );
00590
00591 unsigned char *line = image->scanLine( y );
00592 line += tile.left();
00593
00594 for ( int x = tile.left(); x <= tile.right(); x++ )
00595 {
00596 const double tx = xMap.invTransform( x );
00597
00598 const double value = d_data->data->value( tx, ty );
00599
00600 if ( hasGaps && qwtIsNaN( value ) )
00601 {
00602 *line++ = 0;
00603 }
00604 else
00605 {
00606 const uint index = d_data->colorMap->colorIndex( 256, range, value );
00607 *line++ = static_cast<unsigned char>( index );
00608 }
00609 }
00610 }
00611 }
00612 }
00613
00631 QSize QwtPlotSpectrogram::contourRasterSize(
00632 const QRectF &area, const QRect &rect ) const
00633 {
00634 QSize raster = rect.size() / 2;
00635
00636 const QRectF pixelRect = pixelHint( area );
00637 if ( !pixelRect.isEmpty() )
00638 {
00639 const QSize res( qCeil( rect.width() / pixelRect.width() ),
00640 qCeil( rect.height() / pixelRect.height() ) );
00641 raster = raster.boundedTo( res );
00642 }
00643
00644 return raster;
00645 }
00646
00657 QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
00658 const QRectF &rect, const QSize &raster ) const
00659 {
00660 if ( d_data->data == NULL )
00661 return QwtRasterData::ContourLines();
00662
00663 return d_data->data->contourLines( rect, raster,
00664 d_data->contourLevels, d_data->conrecFlags );
00665 }
00666
00677 void QwtPlotSpectrogram::drawContourLines( QPainter *painter,
00678 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00679 const QwtRasterData::ContourLines &contourLines ) const
00680 {
00681 if ( d_data->data == NULL )
00682 return;
00683
00684 const int numLevels = d_data->contourLevels.size();
00685 for ( int l = 0; l < numLevels; l++ )
00686 {
00687 const double level = d_data->contourLevels[l];
00688
00689 QPen pen = defaultContourPen();
00690 if ( pen.style() == Qt::NoPen )
00691 pen = contourPen( level );
00692
00693 if ( pen.style() == Qt::NoPen )
00694 continue;
00695
00696 painter->setPen( pen );
00697
00698 const QPolygonF &lines = contourLines[level];
00699 for ( int i = 0; i < lines.size(); i += 2 )
00700 {
00701 const QPointF p1( xMap.transform( lines[i].x() ),
00702 yMap.transform( lines[i].y() ) );
00703 const QPointF p2( xMap.transform( lines[i+1].x() ),
00704 yMap.transform( lines[i+1].y() ) );
00705
00706 QwtPainter::drawLine( painter, p1, p2 );
00707 }
00708 }
00709 }
00710
00722 void QwtPlotSpectrogram::draw( QPainter *painter,
00723 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
00724 const QRectF &canvasRect ) const
00725 {
00726 if ( d_data->displayMode & ImageMode )
00727 QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect );
00728
00729 if ( d_data->displayMode & ContourMode )
00730 {
00731
00732 const int margin = 2;
00733 QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin,
00734 canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin );
00735
00736 QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect );
00737
00738 const QRectF br = boundingRect();
00739 if ( br.isValid() )
00740 {
00741 area &= br;
00742 if ( area.isEmpty() )
00743 return;
00744
00745 rasterRect = QwtScaleMap::transform( xMap, yMap, area );
00746 }
00747
00748 QSize raster = contourRasterSize( area, rasterRect.toRect() );
00749 raster = raster.boundedTo( rasterRect.toRect().size() );
00750 if ( raster.isValid() )
00751 {
00752 const QwtRasterData::ContourLines lines =
00753 renderContourLines( area, raster );
00754
00755 drawContourLines( painter, xMap, yMap, lines );
00756 }
00757 }
00758 }