source: ntrip/trunk/BNC/qwt/qwt_plot_spectrogram.cpp@ 11169

Last change on this file since 11169 was 11158, checked in by stuerze, 8 days ago

deprecated functions exchanged libs updated

File size: 20.0 KB
Line 
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
58 conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel;
59#if 0
60 conrecFlags |= QwtRasterData::IgnoreOutOfRange;
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;
91 QwtRasterData::ConrecFlags conrecFlags;
92
93 int colorTableSize;
94 QVector< QRgb > colorTable;
95};
96
97/*!
98 Sets the following item attributes:
99 - QwtPlotItem::AutoScale: true
100 - QwtPlotItem::Legend: false
101
102 The z value is initialized by 8.0.
103
104 \param title Title
105
106 \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
107 */
108QwtPlotSpectrogram::QwtPlotSpectrogram( const QString& title )
109 : QwtPlotRasterItem( title )
110{
111 m_data = new PrivateData();
112
113 setItemAttribute( QwtPlotItem::AutoScale, true );
114 setItemAttribute( QwtPlotItem::Legend, false );
115
116 setZ( 8.0 );
117}
118
119//! Destructor
120QwtPlotSpectrogram::~QwtPlotSpectrogram()
121{
122 delete m_data;
123}
124
125//! \return QwtPlotItem::Rtti_PlotSpectrogram
126int QwtPlotSpectrogram::rtti() const
127{
128 return QwtPlotItem::Rtti_PlotSpectrogram;
129}
130
131/*!
132 The display mode controls how the raster data will be represented.
133
134 \param mode Display mode
135 \param on On/Off
136
137 The default setting enables ImageMode.
138
139 \sa DisplayMode, displayMode()
140 */
141void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on )
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
151 legendChanged();
152 itemChanged();
153}
154
155/*!
156 The display mode controls how the raster data will be represented.
157
158 \param mode Display mode
159 \return true if mode is enabled
160 */
161bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const
162{
163 return ( m_data->displayMode & mode );
164}
165
166/*!
167 Change the color map
168
169 Often it is useful to display the mapping between intensities and
170 colors as an additional plot axis, showing a color bar.
171
172 \param colorMap Color Map
173
174 \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
175 QwtScaleWidget::setColorMap()
176 */
177void QwtPlotSpectrogram::setColorMap( QwtColorMap* colorMap )
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
190 invalidateCache();
191
192 legendChanged();
193 itemChanged();
194}
195
196/*!
197 \return Color Map used for mapping the intensity values to colors
198 \sa setColorMap()
199 */
200const QwtColorMap* QwtPlotSpectrogram::colorMap() const
201{
202 return m_data->colorMap;
203}
204
205/*!
206 Limit the number of colors being used by the color map
207
208 When using a color table the mapping from the value into a color
209 is usually faster as it can be done by simple lookups into a
210 precalculated color table.
211
212 Setting a table size > 0 enables using a color table, while setting
213 the size to 0 disables it.
214
215 The default size = 0, and no color table is used.
216
217 \param numColors Number of colors. 0 means not using a color table
218 \note The colorTableSize has no effect when using a color table
219 of QwtColorMap::Indexed, where the size is always 256.
220
221
222 \sa QwtColorMap::colorTable(), colorTableSize()
223 */
224void QwtPlotSpectrogram::setColorTableSize( int numColors )
225{
226 numColors = qMax( numColors, 0 );
227 if ( numColors != m_data->colorTableSize )
228 {
229 m_data->colorTableSize = numColors;
230 m_data->updateColorTable();
231 invalidateCache();
232 }
233}
234/*!
235 \return Size of the color table, 0 means not using a color table
236 \sa QwtColorMap::colorTable(), setColorTableSize()
237 */
238int QwtPlotSpectrogram::colorTableSize() const
239{
240 return m_data->colorTableSize;
241}
242
243/*!
244 Build and assign the default pen for the contour lines
245
246 In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
247 non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
248 to hide this incompatibility.
249
250 \param color Pen color
251 \param width Pen width
252 \param style Pen style
253
254 \sa pen(), brush()
255 */
256void QwtPlotSpectrogram::setDefaultContourPen(
257 const QColor& color, qreal width, Qt::PenStyle style )
258{
259 setDefaultContourPen( QPen( color, width, style ) );
260}
261
262/*!
263 \brief Set the default pen for the contour lines
264
265 If the spectrogram has a valid default contour pen
266 a contour line is painted using the default contour pen.
267 Otherwise (pen.style() == Qt::NoPen) the pen is calculated
268 for each contour level using contourPen().
269
270 \sa defaultContourPen(), contourPen()
271 */
272void QwtPlotSpectrogram::setDefaultContourPen( const QPen& pen )
273{
274 if ( pen != m_data->defaultContourPen )
275 {
276 m_data->defaultContourPen = pen;
277
278 legendChanged();
279 itemChanged();
280 }
281}
282
283/*!
284 \return Default contour pen
285 \sa setDefaultContourPen()
286 */
287QPen QwtPlotSpectrogram::defaultContourPen() const
288{
289 return m_data->defaultContourPen;
290}
291
292/*!
293 \brief Calculate the pen for a contour line
294
295 The color of the pen is the color for level calculated by the color map
296
297 \param level Contour level
298 \return Pen for the contour line
299 \note contourPen is only used if defaultContourPen().style() == Qt::NoPen
300
301 \sa setDefaultContourPen(), setColorMap(), setContourLevels()
302 */
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
314/*!
315 Modify an attribute of the CONREC algorithm, used to calculate
316 the contour lines.
317
318 \param flag CONREC flag
319 \param on On/Off
320
321 \sa testConrecFlag(), renderContourLines(),
322 QwtRasterData::contourLines()
323 */
324void QwtPlotSpectrogram::setConrecFlag(
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
338/*!
339 Test an attribute of the CONREC algorithm, used to calculate
340 the contour lines.
341
342 \param flag CONREC flag
343 \return true, is enabled
344
345 The default setting enables QwtRasterData::IgnoreAllVerticesOnLevel
346
347 \sa setConrecClag(), renderContourLines(),
348 QwtRasterData::contourLines()
349 */
350bool QwtPlotSpectrogram::testConrecFlag(
351 QwtRasterData::ConrecFlag flag ) const
352{
353 return m_data->conrecFlags & flag;
354}
355
356/*!
357 Set the levels of the contour lines
358
359 \param levels Values of the contour levels
360 \sa contourLevels(), renderContourLines(),
361 QwtRasterData::contourLines()
362
363 \note contourLevels returns the same levels but sorted.
364 */
365void QwtPlotSpectrogram::setContourLevels( const QList< double >& levels )
366{
367 m_data->contourLevels = levels;
368 std::sort( m_data->contourLevels.begin(), m_data->contourLevels.end() );
369
370 legendChanged();
371 itemChanged();
372}
373
374/*!
375 \return Levels of the contour lines.
376
377 The levels are sorted in increasing order.
378
379 \sa contourLevels(), renderContourLines(),
380 QwtRasterData::contourLines()
381 */
382QList< double > QwtPlotSpectrogram::contourLevels() const
383{
384 return m_data->contourLevels;
385}
386
387/*!
388 Set the data to be displayed
389
390 \param data Spectrogram Data
391 \sa data()
392 */
393void QwtPlotSpectrogram::setData( QwtRasterData* data )
394{
395 if ( data != m_data->data )
396 {
397 delete m_data->data;
398 m_data->data = data;
399
400 invalidateCache();
401 itemChanged();
402 }
403}
404
405/*!
406 \return Spectrogram data
407 \sa setData()
408 */
409const QwtRasterData* QwtPlotSpectrogram::data() const
410{
411 return m_data->data;
412}
413
414/*!
415 \return Spectrogram data
416 \sa setData()
417 */
418QwtRasterData* QwtPlotSpectrogram::data()
419{
420 return m_data->data;
421}
422
423/*!
424 \return Bounding interval for an axis
425
426 The default implementation returns the interval of the
427 associated raster data object.
428
429 \param axis X, Y, or Z axis
430 \sa QwtRasterData::interval()
431 */
432QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const
433{
434 if ( m_data->data == NULL )
435 return QwtInterval();
436
437 return m_data->data->interval( axis );
438}
439
440/*!
441 \brief Pixel hint
442
443 The geometry of a pixel is used to calculated the resolution and
444 alignment of the rendered image.
445
446 The default implementation returns data()->pixelHint( rect );
447
448 \param area In most implementations the resolution of the data doesn't
449 depend on the requested area.
450
451 \return Bounding rectangle of a pixel
452
453 \sa QwtPlotRasterItem::pixelHint(), QwtRasterData::pixelHint(),
454 render(), renderImage()
455 */
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
464/*!
465 \brief Render an image from data and color map.
466
467 For each pixel of area the value is mapped into a color.
468
469 \param xMap X-Scale Map
470 \param yMap Y-Scale Map
471 \param area Requested area for the image in scale coordinates
472 \param imageSize Size of the requested image
473
474 \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
475 on the color map.
476
477 \sa QwtRasterData::value(), QwtColorMap::rgb(),
478 QwtColorMap::colorIndex()
479 */
480QImage QwtPlotSpectrogram::renderImage(
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
535 &QwtPlotSpectrogram::renderTile, this,
536#else
537 this, &QwtPlotSpectrogram::renderTile,
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
561/*!
562 \brief Render a tile of an image.
563
564 Rendering in tiles can be used to composite an image in parallel
565 threads.
566
567 \param xMap X-Scale Map
568 \param yMap Y-Scale Map
569 \param tile Geometry of the tile in image coordinates
570 \param image Image to be rendered
571 */
572void QwtPlotSpectrogram::renderTile(
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
646/*!
647 \brief Return the raster to be used by the CONREC contour algorithm.
648
649 A larger size will improve the precision of the CONREC algorithm,
650 but will slow down the time that is needed to calculate the lines.
651
652 The default implementation returns rect.size() / 2 bounded to
653 the resolution depending on pixelSize().
654
655 \param area Rectangle, where to calculate the contour lines
656 \param rect Rectangle in pixel coordinates, where to paint the contour lines
657 \return Raster to be used by the CONREC contour algorithm.
658
659 \note The size will be bounded to rect.size().
660
661 \sa drawContourLines(), QwtRasterData::contourLines()
662 */
663QSize QwtPlotSpectrogram::contourRasterSize(
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
679/*!
680 Calculate contour lines
681
682 \param rect Rectangle, where to calculate the contour lines
683 \param raster Raster, used by the CONREC algorithm
684 \return Calculated contour lines
685
686 \sa contourLevels(), setConrecFlag(),
687 QwtRasterData::contourLines()
688 */
689QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
690 const QRectF& rect, const QSize& raster ) const
691{
692 if ( m_data->data == NULL )
693 return QwtRasterData::ContourLines();
694
695 return m_data->data->contourLines( rect, raster,
696 m_data->contourLevels, m_data->conrecFlags );
697}
698
699/*!
700 Paint the contour lines
701
702 \param painter Painter
703 \param xMap Maps x-values into pixel coordinates.
704 \param yMap Maps y-values into pixel coordinates.
705 \param contourLines Contour lines
706
707 \sa renderContourLines(), defaultContourPen(), contourPen()
708 */
709void QwtPlotSpectrogram::drawContourLines( QPainter* painter,
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
743/*!
744 \brief Draw the spectrogram
745
746 \param painter Painter
747 \param xMap Maps x-values into pixel coordinates.
748 \param yMap Maps y-values into pixel coordinates.
749 \param canvasRect Contents rectangle of the canvas in painter coordinates
750
751 \sa setDisplayMode(), renderImage(),
752 QwtPlotRasterItem::draw(), drawContourLines()
753 */
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}
Note: See TracBrowser for help on using the repository browser.