| 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 |
|
|---|
| 32 | static 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 |
|
|---|
| 48 | class 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 | */
|
|---|
| 108 | QwtPlotSpectrogram::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
|
|---|
| 120 | QwtPlotSpectrogram::~QwtPlotSpectrogram()
|
|---|
| 121 | {
|
|---|
| 122 | delete m_data;
|
|---|
| 123 | }
|
|---|
| 124 |
|
|---|
| 125 | //! \return QwtPlotItem::Rtti_PlotSpectrogram
|
|---|
| 126 | int 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 | */
|
|---|
| 141 | void 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 | */
|
|---|
| 161 | bool 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 | */
|
|---|
| 177 | void 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 | */
|
|---|
| 200 | const 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 | */
|
|---|
| 224 | void 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 | */
|
|---|
| 238 | int 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 | */
|
|---|
| 256 | void 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 | */
|
|---|
| 272 | void 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 | */
|
|---|
| 287 | QPen 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 | */
|
|---|
| 303 | QPen 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 | */
|
|---|
| 324 | void 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 | */
|
|---|
| 350 | bool 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 | */
|
|---|
| 365 | void 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 | */
|
|---|
| 382 | QList< 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 | */
|
|---|
| 393 | void 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 | */
|
|---|
| 409 | const QwtRasterData* QwtPlotSpectrogram::data() const
|
|---|
| 410 | {
|
|---|
| 411 | return m_data->data;
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|
| 414 | /*!
|
|---|
| 415 | \return Spectrogram data
|
|---|
| 416 | \sa setData()
|
|---|
| 417 | */
|
|---|
| 418 | QwtRasterData* 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 | */
|
|---|
| 432 | QwtInterval 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 | */
|
|---|
| 456 | QRectF 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 | */
|
|---|
| 480 | QImage 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 | */
|
|---|
| 572 | void 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 | */
|
|---|
| 663 | QSize 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 | */
|
|---|
| 689 | QwtRasterData::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 | */
|
|---|
| 709 | void 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 | */
|
|---|
| 754 | void 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 | }
|
|---|