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 

Generated on Mon Feb 26 21:22:38 2007 for Qwt User's Guide by  doxygen 1.4.6