# qwt_plot_spectrogram.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 #include <qimage.h>
    00011 #include <qpen.h>
    00012 #include <qpainter.h>
    00013 #include "qwt_painter.h"
    00014 #include "qwt_double_interval.h"
    00015 #include "qwt_scale_map.h"
    00016 #include "qwt_color_map.h"
    00017 #include "qwt_plot_spectrogram.h"
    00018
    00019 #if QT_VERSION < 0x040000
    00020 typedef QValueVector<QRgb> QwtColorTable;
    00021 #else
    00022 typedef QVector<QRgb> QwtColorTable;
    00023 #endif
    00024
    00025 class QwtPlotSpectrogramImage: public QImage
    00026 {
    00027   // This class hides some Qt3/Qt4 API differences
    00028 public:
    00029     QwtPlotSpectrogramImage(const QSize &size, QwtColorMap::Format format):
    00030 #if QT_VERSION < 0x040000
    00031         QImage(size, format == QwtColorMap::RGB ? 32 : 8)
    00032 #else
    00033         QImage(size, format == QwtColorMap::RGB
    00034             ? QImage::Format_ARGB32 : QImage::Format_Indexed8 )
    00035 #endif
    00036     {
    00037     }
    00038
    00039     QwtPlotSpectrogramImage(const QImage &other):
    00040         QImage(other)
    00041     {
    00042     }
    00043
    00044     void initColorTable(const QImage& other)
    00045     {
    00046 #if QT_VERSION < 0x040000
    00047         const unsigned int numColors = other.numColors();
    00048
    00049         setNumColors(numColors);
    00050         for ( unsigned int i = 0; i < numColors; i++ )
    00051             setColor(i, other.color(i));
    00052 #else
    00053         setColorTable(other.colorTable());
    00054 #endif
    00055     }
    00056
    00057 #if QT_VERSION < 0x040000
    00058
    00059     void setColorTable(const QwtColorTable &colorTable)
    00060     {
    00061         setNumColors(colorTable.size());
    00062         for ( unsigned int i = 0; i < colorTable.size(); i++ )
    00063             setColor(i, colorTable[i]);
    00064     }
    00065
    00066     QwtColorTable colorTable() const
    00067     {
    00068         QwtColorTable table(numColors());
    00069         for ( int i = 0; i < numColors(); i++ )
    00070             table[i] = color(i);
    00071
    00072         return table;
    00073     }
    00074 #endif
    00075 };
    00076
    00077 class QwtPlotSpectrogram::PrivateData
    00078 {
    00079 public:
    00080     class DummyData: public QwtRasterData
    00081     {
    00082     public:
    00083         virtual QwtRasterData *copy() const
    00084         {
    00085             return new DummyData();
    00086         }
    00087
    00088         virtual double value(double, double) const
    00089         {
    00090             return 0.0;
    00091         }
    00092
    00093         virtual QwtDoubleInterval range() const
    00094         {
    00095             return QwtDoubleInterval(0.0, 1.0);
    00096         }
    00097     };
    00098
    00099     PrivateData()
    00100     {
    00101         data = new DummyData();
    00102         colorMap = new QwtLinearColorMap();
    00103         displayMode = ImageMode;
    00104
    00105         conrecAttributes = QwtRasterData::IgnoreAllVerticesOnLevel;
    00106         conrecAttributes |= QwtRasterData::IgnoreOutOfRange;
    00107     }
    00108     ~PrivateData()
    00109     {
    00110         delete data;
    00111         delete colorMap;
    00112     }
    00113
    00114     QwtRasterData *data;
    00115     QwtColorMap *colorMap;
    00116     int displayMode;
    00117
    00118     QwtValueList contourLevels;
    00119     QPen defaultContourPen;
    00120     int conrecAttributes;
    00121 };
    00122
    00134 QwtPlotSpectrogram::QwtPlotSpectrogram(const QString &title):
    00135     QwtPlotRasterItem(title)
    00136 {
    00137     d_data = new PrivateData();
    00138
    00139     setItemAttribute(QwtPlotItem::AutoScale, true);
    00140     setItemAttribute(QwtPlotItem::Legend, false);
    00141
    00142     setZ(8.0);
    00143 }
    00144
    00146 QwtPlotSpectrogram::~QwtPlotSpectrogram()
    00147 {
    00148     delete d_data;
    00149 }
    00150
    00152 int QwtPlotSpectrogram::rtti() const
    00153 {
    00154     return QwtPlotItem::Rtti_PlotSpectrogram;
    00155 }
    00156
    00167 void QwtPlotSpectrogram::setDisplayMode(DisplayMode mode, bool on)
    00168 {
    00169     if ( on != bool(mode & d_data->displayMode) )
    00170     {
    00171         if ( on )
    00172             d_data->displayMode |= mode;
    00173         else
    00174             d_data->displayMode &= ~mode;
    00175     }
    00176
    00177     itemChanged();
    00178 }
    00179
    00186 bool QwtPlotSpectrogram::testDisplayMode(DisplayMode mode) const
    00187 {
    00188     return (d_data->displayMode & mode);
    00189 }
    00190
    00202 void QwtPlotSpectrogram::setColorMap(const QwtColorMap &colorMap)
    00203 {
    00204     delete d_data->colorMap;
    00205     d_data->colorMap = colorMap.copy();
    00206
    00207     invalidateCache();
    00208     itemChanged();
    00209 }
    00210
    00215 const QwtColorMap &QwtPlotSpectrogram::colorMap() const
    00216 {
    00217     return *d_data->colorMap;
    00218 }
    00219
    00230 void QwtPlotSpectrogram::setDefaultContourPen(const QPen &pen)
    00231 {
    00232     if ( pen != d_data->defaultContourPen )
    00233     {
    00234         d_data->defaultContourPen = pen;
    00235         itemChanged();
    00236     }
    00237 }
    00238
    00243 QPen QwtPlotSpectrogram::defaultContourPen() const
    00244 {
    00245     return d_data->defaultContourPen;
    00246 }
    00247
    00259 QPen QwtPlotSpectrogram::contourPen(double level) const
    00260 {
    00261     const QwtDoubleInterval intensityRange = d_data->data->range();
    00262     const QColor c(d_data->colorMap->rgb(intensityRange, level));
    00263
    00264     return QPen(c);
    00265 }
    00266
    00276 void QwtPlotSpectrogram::setConrecAttribute(
    00277     QwtRasterData::ConrecAttribute attribute, bool on)
    00278 {
    00279     if ( bool(d_data->conrecAttributes & attribute) == on )
    00280         return;
    00281
    00282     if ( on )
    00283         d_data->conrecAttributes |= attribute;
    00284     else
    00285         d_data->conrecAttributes &= ~attribute;
    00286
    00287     itemChanged();
    00288 }
    00289
    00299 bool QwtPlotSpectrogram::testConrecAttribute(
    00300     QwtRasterData::ConrecAttribute attribute) const
    00301 {
    00302     return d_data->conrecAttributes & attribute;
    00303 }
    00304
    00313 void QwtPlotSpectrogram::setContourLevels(const QwtValueList &levels)
    00314 {
    00315     d_data->contourLevels = levels;
    00316 #if QT_VERSION >= 0x040000
    00317     qSort(d_data->contourLevels);
    00318 #else
    00319     qHeapSort(d_data->contourLevels);
    00320 #endif
    00321     itemChanged();
    00322 }
    00323
    00331 QwtValueList QwtPlotSpectrogram::contourLevels() const
    00332 {
    00333     return d_data->contourLevels;
    00334 }
    00335
    00342 void QwtPlotSpectrogram::setData(const QwtRasterData &data)
    00343 {
    00344     delete d_data->data;
    00345     d_data->data = data.copy();
    00346
    00347     invalidateCache();
    00348     itemChanged();
    00349 }
    00350
    00355 const QwtRasterData &QwtPlotSpectrogram::data() const
    00356 {
    00357     return *d_data->data;
    00358 }
    00359
    00364 QwtDoubleRect QwtPlotSpectrogram::boundingRect() const
    00365 {
    00366     return d_data->data->boundingRect();
    00367 }
    00368
    00378 QSize QwtPlotSpectrogram::rasterHint(const QwtDoubleRect &rect) const
    00379 {
    00380     return d_data->data->rasterHint(rect);
    00381 }
    00382
    00400 QImage QwtPlotSpectrogram::renderImage(
    00401     const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    00402     const QwtDoubleRect &area) const
    00403 {
    00404     if ( area.isEmpty() )
    00405         return QImage();
    00406
    00407     QRect rect = transform(xMap, yMap, area);
    00408
    00409     QwtScaleMap xxMap = xMap;
    00410     QwtScaleMap yyMap = yMap;
    00411
    00412     const QSize res = d_data->data->rasterHint(area);
    00413     if ( res.isValid() )
    00414     {
    00415         /*
    00416           It is useless to render an image with a higher resolution
    00417           than the data offers. Of course someone will have to
    00418           scale this image later into the size of the given rect, but f.e.
    00419           in case of postscript this will done on the printer.
    00420          */
    00421         rect.setSize(rect.size().boundedTo(res));
    00422
    00423         int px1 = rect.x();
    00424         int px2 = rect.x() + rect.width();
    00425         if ( xMap.p1() > xMap.p2() )
    00426             qSwap(px1, px2);
    00427
    00428         double sx1 = area.x();
    00429         double sx2 = area.x() + area.width();
    00430         if ( xMap.s1() > xMap.s2() )
    00431             qSwap(sx1, sx2);
    00432
    00433         int py1 = rect.y();
    00434         int py2 = rect.y() + rect.height();
    00435         if ( yMap.p1() > yMap.p2() )
    00436             qSwap(py1, py2);
    00437
    00438         double sy1 = area.y();
    00439         double sy2 = area.y() + area.height();
    00440         if ( yMap.s1() > yMap.s2() )
    00441             qSwap(sy1, sy2);
    00442
    00443         xxMap.setPaintInterval(px1, px2);
    00444         xxMap.setScaleInterval(sx1, sx2);
    00445         yyMap.setPaintInterval(py1, py2);
    00446         yyMap.setScaleInterval(sy1, sy2);
    00447     }
    00448
    00449     QwtPlotSpectrogramImage image(rect.size(), d_data->colorMap->format());
    00450
    00451     const QwtDoubleInterval intensityRange = d_data->data->range();
    00452     if ( !intensityRange.isValid() )
    00453         return image;
    00454
    00455     d_data->data->initRaster(area, rect.size());
    00456
    00457     if ( d_data->colorMap->format() == QwtColorMap::RGB )
    00458     {
    00459         for ( int y = rect.top(); y <= rect.bottom(); y++ )
    00460         {
    00461             const double ty = yyMap.invTransform(y);
    00462
    00463             QRgb *line = (QRgb *)image.scanLine(y - rect.top());
    00464             for ( int x = rect.left(); x <= rect.right(); x++ )
    00465             {
    00466                 const double tx = xxMap.invTransform(x);
    00467
    00468                 *line++ = d_data->colorMap->rgb(intensityRange,
    00469                     d_data->data->value(tx, ty));
    00470             }
    00471         }
    00472     }
    00473     else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
    00474     {
    00475         image.setColorTable(d_data->colorMap->colorTable(intensityRange));
    00476
    00477         for ( int y = rect.top(); y <= rect.bottom(); y++ )
    00478         {
    00479             const double ty = yyMap.invTransform(y);
    00480
    00481             unsigned char *line = image.scanLine(y - rect.top());
    00482             for ( int x = rect.left(); x <= rect.right(); x++ )
    00483             {
    00484                 const double tx = xxMap.invTransform(x);
    00485
    00486                 *line++ = d_data->colorMap->colorIndex(intensityRange,
    00487                     d_data->data->value(tx, ty));
    00488             }
    00489         }
    00490     }
    00491
    00492     d_data->data->discardRaster();
    00493
    00494     // Mirror the image in case of inverted maps
    00495
    00496     const bool hInvert = xxMap.p1() > xxMap.p2();
    00497     const bool vInvert = yyMap.p1() < yyMap.p2();
    00498     if ( hInvert || vInvert )
    00499     {
    00500 #ifdef __GNUC__
    00501 #endif
    00502 #if QT_VERSION < 0x040000
    00503         image = image.mirror(hInvert, vInvert);
    00504 #else
    00505         image = image.mirrored(hInvert, vInvert);
    00506 #endif
    00507     }
    00508
    00509     return image;
    00510 }
    00511
    00529 QSize QwtPlotSpectrogram::contourRasterSize(const QwtDoubleRect &area,
    00530     const QRect &rect) const
    00531 {
    00532     QSize raster = rect.size() / 2;
    00533
    00534     const QSize rasterHint = d_data->data->rasterHint(area);
    00535     if ( rasterHint.isValid() )
    00536         raster = raster.boundedTo(rasterHint);
    00537
    00538     return raster;
    00539 }
    00540
    00549 QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines(
    00550     const QwtDoubleRect &rect, const QSize &raster) const
    00551 {
    00552     return d_data->data->contourLines(rect, raster,
    00553         d_data->contourLevels, d_data->conrecAttributes );
    00554 }
    00555
    00566 void QwtPlotSpectrogram::drawContourLines(QPainter *painter,
    00567         const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    00568         const QwtRasterData::ContourLines &contourLines) const
    00569 {
    00570     const QwtDoubleInterval intensityRange = d_data->data->range();
    00571
    00572     const int numLevels = (int)d_data->contourLevels.size();
    00573     for (int l = 0; l < numLevels; l++)
    00574     {
    00575         const double level = d_data->contourLevels[l];
    00576
    00577         QPen pen = defaultContourPen();
    00578         if ( pen.style() == Qt::NoPen )
    00579             pen = contourPen(level);
    00580
    00581         if ( pen.style() == Qt::NoPen )
    00582             continue;
    00583
    00584         painter->setPen(pen);
    00585
    00586 #if QT_VERSION >= 0x040000
    00587         const QPolygonF &lines = contourLines[level];
    00588 #else
    00589         const QwtArray<QwtDoublePoint> &lines = contourLines[level];
    00590 #endif
    00591         for ( int i = 0; i < (int)lines.size(); i += 2 )
    00592         {
    00593             const QPoint p1( xMap.transform(lines[i].x()),
    00594                 yMap.transform(lines[i].y()) );
    00595             const QPoint p2( xMap.transform(lines[i+1].x()),
    00596                 yMap.transform(lines[i+1].y()) );
    00597
    00598             QwtPainter::drawLine(painter, p1, p2);
    00599         }
    00600     }
    00601 }
    00602
    00615 void QwtPlotSpectrogram::draw(QPainter *painter,
    00616     const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    00617     const QRect &canvasRect) const
    00618 {
    00619     if ( d_data->displayMode & ImageMode )
    00620         QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect);
    00621
    00622     if ( d_data->displayMode & ContourMode )
    00623     {
    00624         // Add some pixels at the borders, so that
    00625         const int margin = 2;
    00626         QRect rasterRect(canvasRect.x() - margin, canvasRect.y() - margin,
    00627             canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin);
    00628
    00629         QwtDoubleRect area = invTransform(xMap, yMap, rasterRect);
    00630
    00631         const QwtDoubleRect br = boundingRect();
    00632         if ( br.isValid() && br.contains(area) )
    00633         {
    00634             area &= br;
    00635             rasterRect = transform(xMap, yMap, area);
    00636         }
    00637
    00638         QSize raster = contourRasterSize(area, rasterRect);
    00639         raster = raster.boundedTo(rasterRect.size());
    00640         if ( raster.isValid() )
    00641         {
    00642             const QwtRasterData::ContourLines lines =
    00643                 renderContourLines(area, raster);
    00644
    00645             drawContourLines(painter, xMap, yMap, lines);
    00646         }
    00647     }
    00648 }
    00649