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

Last change on this file since 8154 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

File size: 16.9 KB
Line 
1/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
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 <qimage.h>
16#include <qpen.h>
17#include <qpainter.h>
18#include <qmath.h>
19#include <qalgorithms.h>
20#if QT_VERSION >= 0x040400
21#include <qthread.h>
22#include <qfuture.h>
23#include <qtconcurrentrun.h>
24#endif
25
26#define DEBUG_RENDER 0
27
28#if DEBUG_RENDER
29#include <QElapsedTimer>
30#endif
31
32class QwtPlotSpectrogram::PrivateData
33{
34public:
35 PrivateData():
36 data( NULL )
37 {
38 colorMap = new QwtLinearColorMap();
39 displayMode = ImageMode;
40
41 conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel;
42#if 0
43 conrecFlags |= QwtRasterData::IgnoreOutOfRange;
44#endif
45 }
46 ~PrivateData()
47 {
48 delete data;
49 delete colorMap;
50 }
51
52 QwtRasterData *data;
53 QwtColorMap *colorMap;
54 DisplayModes displayMode;
55
56 QList<double> contourLevels;
57 QPen defaultContourPen;
58 QwtRasterData::ConrecFlags conrecFlags;
59};
60
61/*!
62 Sets the following item attributes:
63 - QwtPlotItem::AutoScale: true
64 - QwtPlotItem::Legend: false
65
66 The z value is initialized by 8.0.
67
68 \param title Title
69
70 \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
71*/
72QwtPlotSpectrogram::QwtPlotSpectrogram( const QString &title ):
73 QwtPlotRasterItem( title )
74{
75 d_data = new PrivateData();
76
77 setItemAttribute( QwtPlotItem::AutoScale, true );
78 setItemAttribute( QwtPlotItem::Legend, false );
79
80 setZ( 8.0 );
81}
82
83//! Destructor
84QwtPlotSpectrogram::~QwtPlotSpectrogram()
85{
86 delete d_data;
87}
88
89//! \return QwtPlotItem::Rtti_PlotSpectrogram
90int QwtPlotSpectrogram::rtti() const
91{
92 return QwtPlotItem::Rtti_PlotSpectrogram;
93}
94
95/*!
96 The display mode controls how the raster data will be represented.
97
98 \param mode Display mode
99 \param on On/Off
100
101 The default setting enables ImageMode.
102
103 \sa DisplayMode, displayMode()
104*/
105void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on )
106{
107 if ( on != bool( mode & d_data->displayMode ) )
108 {
109 if ( on )
110 d_data->displayMode |= mode;
111 else
112 d_data->displayMode &= ~mode;
113 }
114
115 legendChanged();
116 itemChanged();
117}
118
119/*!
120 The display mode controls how the raster data will be represented.
121
122 \param mode Display mode
123 \return true if mode is enabled
124*/
125bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const
126{
127 return ( d_data->displayMode & mode );
128}
129
130/*!
131 Change the color map
132
133 Often it is useful to display the mapping between intensities and
134 colors as an additional plot axis, showing a color bar.
135
136 \param colorMap Color Map
137
138 \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
139 QwtScaleWidget::setColorMap()
140*/
141void QwtPlotSpectrogram::setColorMap( QwtColorMap *colorMap )
142{
143 if ( d_data->colorMap != colorMap )
144 {
145 delete d_data->colorMap;
146 d_data->colorMap = colorMap;
147 }
148
149 invalidateCache();
150
151 legendChanged();
152 itemChanged();
153}
154
155/*!
156 \return Color Map used for mapping the intensity values to colors
157 \sa setColorMap()
158*/
159const QwtColorMap *QwtPlotSpectrogram::colorMap() const
160{
161 return d_data->colorMap;
162}
163
164/*!
165 Build and assign the default pen for the contour lines
166
167 In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
168 non cosmetic ( see QPen::isCosmetic() ). This method has been introduced
169 to hide this incompatibility.
170
171 \param color Pen color
172 \param width Pen width
173 \param style Pen style
174
175 \sa pen(), brush()
176 */
177void QwtPlotSpectrogram::setDefaultContourPen(
178 const QColor &color, qreal width, Qt::PenStyle style )
179{
180 setDefaultContourPen( QPen( color, width, style ) );
181}
182
183/*!
184 \brief Set the default pen for the contour lines
185
186 If the spectrogram has a valid default contour pen
187 a contour line is painted using the default contour pen.
188 Otherwise (pen.style() == Qt::NoPen) the pen is calculated
189 for each contour level using contourPen().
190
191 \sa defaultContourPen(), contourPen()
192*/
193void QwtPlotSpectrogram::setDefaultContourPen( const QPen &pen )
194{
195 if ( pen != d_data->defaultContourPen )
196 {
197 d_data->defaultContourPen = pen;
198
199 legendChanged();
200 itemChanged();
201 }
202}
203
204/*!
205 \return Default contour pen
206 \sa setDefaultContourPen()
207*/
208QPen QwtPlotSpectrogram::defaultContourPen() const
209{
210 return d_data->defaultContourPen;
211}
212
213/*!
214 \brief Calculate the pen for a contour line
215
216 The color of the pen is the color for level calculated by the color map
217
218 \param level Contour level
219 \return Pen for the contour line
220 \note contourPen is only used if defaultContourPen().style() == Qt::NoPen
221
222 \sa setDefaultContourPen(), setColorMap(), setContourLevels()
223*/
224QPen QwtPlotSpectrogram::contourPen( double level ) const
225{
226 if ( d_data->data == NULL || d_data->colorMap == NULL )
227 return QPen();
228
229 const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis);
230 const QColor c( d_data->colorMap->rgb( intensityRange, level ) );
231
232 return QPen( c );
233}
234
235/*!
236 Modify an attribute of the CONREC algorithm, used to calculate
237 the contour lines.
238
239 \param flag CONREC flag
240 \param on On/Off
241
242 \sa testConrecFlag(), renderContourLines(),
243 QwtRasterData::contourLines()
244*/
245void QwtPlotSpectrogram::setConrecFlag(
246 QwtRasterData::ConrecFlag flag, bool on )
247{
248 if ( bool( d_data->conrecFlags & flag ) == on )
249 return;
250
251 if ( on )
252 d_data->conrecFlags |= flag;
253 else
254 d_data->conrecFlags &= ~flag;
255
256 itemChanged();
257}
258
259/*!
260 Test an attribute of the CONREC algorithm, used to calculate
261 the contour lines.
262
263 \param flag CONREC flag
264 \return true, is enabled
265
266 The default setting enables QwtRasterData::IgnoreAllVerticesOnLevel
267
268 \sa setConrecClag(), renderContourLines(),
269 QwtRasterData::contourLines()
270*/
271bool QwtPlotSpectrogram::testConrecFlag(
272 QwtRasterData::ConrecFlag flag ) const
273{
274 return d_data->conrecFlags & flag;
275}
276
277/*!
278 Set the levels of the contour lines
279
280 \param levels Values of the contour levels
281 \sa contourLevels(), renderContourLines(),
282 QwtRasterData::contourLines()
283
284 \note contourLevels returns the same levels but sorted.
285*/
286void QwtPlotSpectrogram::setContourLevels( const QList<double> &levels )
287{
288 d_data->contourLevels = levels;
289 qSort( d_data->contourLevels );
290
291 legendChanged();
292 itemChanged();
293}
294
295/*!
296 \return Levels of the contour lines.
297
298 The levels are sorted in increasing order.
299
300 \sa contourLevels(), renderContourLines(),
301 QwtRasterData::contourLines()
302*/
303QList<double> QwtPlotSpectrogram::contourLevels() const
304{
305 return d_data->contourLevels;
306}
307
308/*!
309 Set the data to be displayed
310
311 \param data Spectrogram Data
312 \sa data()
313*/
314void QwtPlotSpectrogram::setData( QwtRasterData *data )
315{
316 if ( data != d_data->data )
317 {
318 delete d_data->data;
319 d_data->data = data;
320
321 invalidateCache();
322 itemChanged();
323 }
324}
325
326/*!
327 \return Spectrogram data
328 \sa setData()
329*/
330const QwtRasterData *QwtPlotSpectrogram::data() const
331{
332 return d_data->data;
333}
334
335/*!
336 \return Spectrogram data
337 \sa setData()
338*/
339QwtRasterData *QwtPlotSpectrogram::data()
340{
341 return d_data->data;
342}
343
344/*!
345 \return Bounding interval for an axis
346
347 The default implementation returns the interval of the
348 associated raster data object.
349
350 \param axis X, Y, or Z axis
351 \sa QwtRasterData::interval()
352*/
353QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const
354{
355 if ( d_data->data == NULL )
356 return QwtInterval();
357
358 return d_data->data->interval( axis );
359}
360
361/*!
362 \brief Pixel hint
363
364 The geometry of a pixel is used to calculated the resolution and
365 alignment of the rendered image.
366
367 The default implementation returns data()->pixelHint( rect );
368
369 \param area In most implementations the resolution of the data doesn't
370 depend on the requested area.
371
372 \return Bounding rectangle of a pixel
373
374 \sa QwtPlotRasterItem::pixelHint(), QwtRasterData::pixelHint(),
375 render(), renderImage()
376*/
377QRectF QwtPlotSpectrogram::pixelHint( const QRectF &area ) const
378{
379 if ( d_data->data == NULL )
380 return QRectF();
381
382 return d_data->data->pixelHint( area );
383}
384
385/*!
386 \brief Render an image from data and color map.
387
388 For each pixel of area the value is mapped into a color.
389
390 \param xMap X-Scale Map
391 \param yMap Y-Scale Map
392 \param area Requested area for the image in scale coordinates
393 \param imageSize Size of the requested image
394
395 \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending
396 on the color map.
397
398 \sa QwtRasterData::value(), QwtColorMap::rgb(),
399 QwtColorMap::colorIndex()
400*/
401QImage QwtPlotSpectrogram::renderImage(
402 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
403 const QRectF &area, const QSize &imageSize ) const
404{
405 if ( imageSize.isEmpty() || d_data->data == NULL
406 || d_data->colorMap == NULL )
407 {
408 return QImage();
409 }
410
411 const QwtInterval intensityRange = d_data->data->interval( Qt::ZAxis );
412 if ( !intensityRange.isValid() )
413 return QImage();
414
415 QImage::Format format = ( d_data->colorMap->format() == QwtColorMap::RGB )
416 ? QImage::Format_ARGB32 : QImage::Format_Indexed8;
417
418 QImage image( imageSize, format );
419
420 if ( d_data->colorMap->format() == QwtColorMap::Indexed )
421 image.setColorTable( d_data->colorMap->colorTable( intensityRange ) );
422
423 d_data->data->initRaster( area, image.size() );
424
425#if DEBUG_RENDER
426 QElapsedTimer time;
427 time.start();
428#endif
429
430#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
431 uint numThreads = renderThreadCount();
432
433 if ( numThreads <= 0 )
434 numThreads = QThread::idealThreadCount();
435
436 if ( numThreads <= 0 )
437 numThreads = 1;
438
439 const int numRows = imageSize.height() / numThreads;
440
441 QList< QFuture<void> > futures;
442 for ( uint i = 0; i < numThreads; i++ )
443 {
444 QRect tile( 0, i * numRows, image.width(), numRows );
445 if ( i == numThreads - 1 )
446 {
447 tile.setHeight( image.height() - i * numRows );
448 renderTile( xMap, yMap, tile, &image );
449 }
450 else
451 {
452 futures += QtConcurrent::run(
453 this, &QwtPlotSpectrogram::renderTile,
454 xMap, yMap, tile, &image );
455 }
456 }
457 for ( int i = 0; i < futures.size(); i++ )
458 futures[i].waitForFinished();
459
460#else // QT_VERSION < 0x040400
461 const QRect tile( 0, 0, image.width(), image.height() );
462 renderTile( xMap, yMap, tile, &image );
463#endif
464
465#if DEBUG_RENDER
466 const qint64 elapsed = time.elapsed();
467 qDebug() << "renderImage" << imageSize << elapsed;
468#endif
469
470 d_data->data->discardRaster();
471
472 return image;
473}
474
475/*!
476 \brief Render a tile of an image.
477
478 Rendering in tiles can be used to composite an image in parallel
479 threads.
480
481 \param xMap X-Scale Map
482 \param yMap Y-Scale Map
483 \param tile Geometry of the tile in image coordinates
484 \param image Image to be rendered
485*/
486void QwtPlotSpectrogram::renderTile(
487 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
488 const QRect &tile, QImage *image ) const
489{
490 const QwtInterval range = d_data->data->interval( Qt::ZAxis );
491 if ( !range.isValid() )
492 return;
493
494 if ( d_data->colorMap->format() == QwtColorMap::RGB )
495 {
496 for ( int y = tile.top(); y <= tile.bottom(); y++ )
497 {
498 const double ty = yMap.invTransform( y );
499
500 QRgb *line = reinterpret_cast<QRgb *>( image->scanLine( y ) );
501 line += tile.left();
502
503 for ( int x = tile.left(); x <= tile.right(); x++ )
504 {
505 const double tx = xMap.invTransform( x );
506
507 *line++ = d_data->colorMap->rgb( range,
508 d_data->data->value( tx, ty ) );
509 }
510 }
511 }
512 else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
513 {
514 for ( int y = tile.top(); y <= tile.bottom(); y++ )
515 {
516 const double ty = yMap.invTransform( y );
517
518 unsigned char *line = image->scanLine( y );
519 line += tile.left();
520
521 for ( int x = tile.left(); x <= tile.right(); x++ )
522 {
523 const double tx = xMap.invTransform( x );
524
525 *line++ = d_data->colorMap->colorIndex( range,
526 d_data->data->value( tx, ty ) );
527 }
528 }
529 }
530}
531
532/*!
533 \brief Return the raster to be used by the CONREC contour algorithm.
534
535 A larger size will improve the precision of the CONREC algorithm,
536 but will slow down the time that is needed to calculate the lines.
537
538 The default implementation returns rect.size() / 2 bounded to
539 the resolution depending on pixelSize().
540
541 \param area Rectangle, where to calculate the contour lines
542 \param rect Rectangle in pixel coordinates, where to paint the contour lines
543 \return Raster to be used by the CONREC contour algorithm.
544
545 \note The size will be bounded to rect.size().
546
547 \sa drawContourLines(), QwtRasterData::contourLines()
548*/
549QSize QwtPlotSpectrogram::contourRasterSize(
550 const QRectF &area, const QRect &rect ) const
551{
552 QSize raster = rect.size() / 2;
553
554 const QRectF pixelRect = pixelHint( area );
555 if ( !pixelRect.isEmpty() )
556 {
557 const QSize res( qCeil( rect.width() / pixelRect.width() ),
558 qCeil( rect.height() / pixelRect.height() ) );
559 raster = raster.boundedTo( res );
560 }
561
562 return raster;
563}
564
565/*!
566 Calculate contour lines
567
568 \param rect Rectangle, where to calculate the contour lines
569 \param raster Raster, used by the CONREC algorithm
570 \return Calculated contour lines
571
572 \sa contourLevels(), setConrecFlag(),
573 QwtRasterData::contourLines()
574*/
575QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
576 const QRectF &rect, const QSize &raster ) const
577{
578 if ( d_data->data == NULL )
579 return QwtRasterData::ContourLines();
580
581 return d_data->data->contourLines( rect, raster,
582 d_data->contourLevels, d_data->conrecFlags );
583}
584
585/*!
586 Paint the contour lines
587
588 \param painter Painter
589 \param xMap Maps x-values into pixel coordinates.
590 \param yMap Maps y-values into pixel coordinates.
591 \param contourLines Contour lines
592
593 \sa renderContourLines(), defaultContourPen(), contourPen()
594*/
595void QwtPlotSpectrogram::drawContourLines( QPainter *painter,
596 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
597 const QwtRasterData::ContourLines &contourLines ) const
598{
599 if ( d_data->data == NULL )
600 return;
601
602 const int numLevels = d_data->contourLevels.size();
603 for ( int l = 0; l < numLevels; l++ )
604 {
605 const double level = d_data->contourLevels[l];
606
607 QPen pen = defaultContourPen();
608 if ( pen.style() == Qt::NoPen )
609 pen = contourPen( level );
610
611 if ( pen.style() == Qt::NoPen )
612 continue;
613
614 painter->setPen( pen );
615
616 const QPolygonF &lines = contourLines[level];
617 for ( int i = 0; i < lines.size(); i += 2 )
618 {
619 const QPointF p1( xMap.transform( lines[i].x() ),
620 yMap.transform( lines[i].y() ) );
621 const QPointF p2( xMap.transform( lines[i+1].x() ),
622 yMap.transform( lines[i+1].y() ) );
623
624 QwtPainter::drawLine( painter, p1, p2 );
625 }
626 }
627}
628
629/*!
630 \brief Draw the spectrogram
631
632 \param painter Painter
633 \param xMap Maps x-values into pixel coordinates.
634 \param yMap Maps y-values into pixel coordinates.
635 \param canvasRect Contents rectangle of the canvas in painter coordinates
636
637 \sa setDisplayMode(), renderImage(),
638 QwtPlotRasterItem::draw(), drawContourLines()
639*/
640void QwtPlotSpectrogram::draw( QPainter *painter,
641 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
642 const QRectF &canvasRect ) const
643{
644 if ( d_data->displayMode & ImageMode )
645 QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect );
646
647 if ( d_data->displayMode & ContourMode )
648 {
649 // Add some pixels at the borders
650 const int margin = 2;
651 QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin,
652 canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin );
653
654 QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect );
655
656 const QRectF br = boundingRect();
657 if ( br.isValid() )
658 {
659 area &= br;
660 if ( area.isEmpty() )
661 return;
662
663 rasterRect = QwtScaleMap::transform( xMap, yMap, area );
664 }
665
666 QSize raster = contourRasterSize( area, rasterRect.toRect() );
667 raster = raster.boundedTo( rasterRect.toRect().size() );
668 if ( raster.isValid() )
669 {
670 const QwtRasterData::ContourLines lines =
671 renderContourLines( area, raster );
672
673 drawContourLines( painter, xMap, yMap, lines );
674 }
675 }
676}
Note: See TracBrowser for help on using the repository browser.