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

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