Imported Upstream version 3.2.2
[debian/gnuradio] / gr-qtgui / src / lib / plot_waterfall.cc
1 #include <qimage.h>
2 #include <qpen.h>
3 #include <qpainter.h>
4 #include "qwt_painter.h"
5 #include "qwt_double_interval.h"
6 #include "qwt_scale_map.h"
7 #include "qwt_color_map.h"
8 #include "plot_waterfall.h"
9
10 #if QT_VERSION < 0x040000
11 typedef Q3ValueVector<QRgb> QwtColorTable;
12 #else
13 typedef QVector<QRgb> QwtColorTable;
14 #endif
15
16
17 class PlotWaterfallImage: public QImage
18 {
19     // This class hides some Qt3/Qt4 API differences
20 public:
21     PlotWaterfallImage(const QSize &size, QwtColorMap::Format format):
22 #if QT_VERSION < 0x040000
23         QImage(size, format == QwtColorMap::RGB ? 32 : 8)
24 #else
25         QImage(size, format == QwtColorMap::RGB
26                ? QImage::Format_ARGB32 : QImage::Format_Indexed8 )
27 #endif
28         {
29         }
30
31     PlotWaterfallImage(const QImage &other):
32         QImage(other)
33         {
34         }
35
36     void initColorTable(const QImage& other)
37         {
38 #if QT_VERSION < 0x040000
39             const unsigned int numColors = other.numColors();
40
41             setNumColors(numColors);
42             for ( unsigned int i = 0; i < numColors; i++ )
43                 setColor(i, other.color(i));
44 #else
45             setColorTable(other.colorTable());
46 #endif
47         }
48
49 #if QT_VERSION < 0x040000
50
51     void setColorTable(const QwtColorTable &colorTable)
52         {
53             setNumColors(colorTable.size());
54             for ( unsigned int i = 0; i < colorTable.size(); i++ )
55                 setColor(i, colorTable[i]);
56         }
57
58     QwtColorTable colorTable() const
59         {
60             QwtColorTable table(numColors());
61             for ( int i = 0; i < numColors(); i++ )
62                 table[i] = color(i);
63
64             return table;
65         }
66 #endif
67 };
68
69 class PlotWaterfall::PrivateData
70 {
71 public:
72     PrivateData()
73         {
74             data = NULL;
75             colorMap = new QwtLinearColorMap();
76         }
77     ~PrivateData()
78         {
79             delete colorMap;
80         }
81
82     WaterfallData *data;
83     QwtColorMap *colorMap;
84 };
85
86 /*!
87   Sets the following item attributes:
88   - QwtPlotItem::AutoScale: true
89   - QwtPlotItem::Legend:    false
90
91   The z value is initialized by 8.0.
92    
93   \param title Title
94
95   \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ()
96 */
97 PlotWaterfall::PlotWaterfall(WaterfallData* data, const QString &title):
98     QwtPlotRasterItem(title)
99 {
100     d_data = new PrivateData();
101     d_data->data = data;
102
103 //    setCachePolicy(QwtPlotRasterItem::PaintCache);
104
105     setItemAttribute(QwtPlotItem::AutoScale, true);
106     setItemAttribute(QwtPlotItem::Legend, false);
107
108     setZ(8.0);
109 }
110
111 //! Destructor
112 PlotWaterfall::~PlotWaterfall()
113 {
114     delete d_data;
115 }
116
117 const WaterfallData* PlotWaterfall::data()const{
118     return d_data->data;
119 }
120
121 //! \return QwtPlotItem::Rtti_PlotSpectrogram
122 int PlotWaterfall::rtti() const
123 {
124     return QwtPlotItem::Rtti_PlotSpectrogram;
125 }
126
127 /*!
128   Change the color map
129
130   Often it is useful to display the mapping between intensities and
131   colors as an additional plot axis, showing a color bar.
132
133   \param colorMap Color Map
134
135   \sa colorMap(), QwtScaleWidget::setColorBarEnabled(),
136   QwtScaleWidget::setColorMap()
137 */
138 void PlotWaterfall::setColorMap(const QwtColorMap &colorMap)
139 {
140     delete d_data->colorMap;
141     d_data->colorMap = colorMap.copy();
142
143     invalidateCache();
144     itemChanged();
145 }
146
147 /*!
148   \return Color Map used for mapping the intensity values to colors
149   \sa setColorMap()
150 */
151 const QwtColorMap &PlotWaterfall::colorMap() const
152 {
153     return *d_data->colorMap;
154 }
155 /*!
156   \return Bounding rect of the data
157   \sa QwtRasterData::boundingRect
158 */
159 QwtDoubleRect PlotWaterfall::boundingRect() const
160 {
161     return d_data->data->boundingRect();
162 }
163
164 /*!
165   \brief Returns the recommended raster for a given rect.
166
167   F.e the raster hint is used to limit the resolution of
168   the image that is rendered.
169
170   \param rect Rect for the raster hint
171   \return data().rasterHint(rect)
172 */
173 QSize PlotWaterfall::rasterHint(const QwtDoubleRect &rect) const
174 {
175     return d_data->data->rasterHint(rect);
176 }
177
178 /*!
179   \brief Render an image from the data and color map.
180
181   The area is translated into a rect of the paint device. 
182   For each pixel of this rect the intensity is mapped
183   into a color.
184
185   \param xMap X-Scale Map
186   \param yMap Y-Scale Map
187   \param area Area that should be rendered in scale coordinates.
188
189   \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending 
190   on the color map.
191
192   \sa QwtRasterData::intensity(), QwtColorMap::rgb(),
193   QwtColorMap::colorIndex()
194 */
195 QImage PlotWaterfall::renderImage(
196     const QwtScaleMap &xMap, const QwtScaleMap &yMap, 
197     const QwtDoubleRect &area) const
198 {
199     if ( area.isEmpty() )
200         return QImage();
201
202     QRect rect = transform(xMap, yMap, area);
203
204     QwtScaleMap xxMap = xMap;
205     QwtScaleMap yyMap = yMap;
206
207     const QSize res = d_data->data->rasterHint(area);
208     if ( res.isValid() )
209     {
210         /*
211           It is useless to render an image with a higher resolution
212           than the data offers. Of course someone will have to
213           scale this image later into the size of the given rect, but f.e.
214           in case of postscript this will done on the printer.
215         */
216         rect.setSize(rect.size().boundedTo(res));
217
218         int px1 = rect.x();
219         int px2 = rect.x() + rect.width();
220         if ( xMap.p1() > xMap.p2() )
221             qSwap(px1, px2);
222
223         double sx1 = area.x();
224         double sx2 = area.x() + area.width();
225         if ( xMap.s1() > xMap.s2() )
226             qSwap(sx1, sx2);
227
228         int py1 = rect.y();
229         int py2 = rect.y() + rect.height();
230         if ( yMap.p1() > yMap.p2() )
231             qSwap(py1, py2);
232
233         double sy1 = area.y();
234         double sy2 = area.y() + area.height();
235         if ( yMap.s1() > yMap.s2() )
236             qSwap(sy1, sy2);
237
238         xxMap.setPaintInterval(px1, px2);
239         xxMap.setScaleInterval(sx1, sx2);
240         yyMap.setPaintInterval(py1, py2);
241         yyMap.setScaleInterval(sy1, sy2); 
242     }
243
244     PlotWaterfallImage image(rect.size(), d_data->colorMap->format());
245
246     const QwtDoubleInterval intensityRange = d_data->data->range();
247     if ( !intensityRange.isValid() )
248         return image;
249
250     d_data->data->initRaster(area, rect.size());
251
252     if ( d_data->colorMap->format() == QwtColorMap::RGB )
253     {
254         for ( int y = rect.top(); y <= rect.bottom(); y++ )
255         {
256             const double ty = yyMap.invTransform(y);
257
258             QRgb *line = (QRgb *)image.scanLine(y - rect.top());
259             for ( int x = rect.left(); x <= rect.right(); x++ )
260             {
261                 const double tx = xxMap.invTransform(x);
262
263                 *line++ = d_data->colorMap->rgb(intensityRange,
264                                                 d_data->data->value(tx, ty));
265             }
266         }
267     }
268     else if ( d_data->colorMap->format() == QwtColorMap::Indexed )
269     {
270         image.setColorTable(d_data->colorMap->colorTable(intensityRange));
271
272         for ( int y = rect.top(); y <= rect.bottom(); y++ )
273         {
274             const double ty = yyMap.invTransform(y);
275
276             unsigned char *line = image.scanLine(y - rect.top());
277             for ( int x = rect.left(); x <= rect.right(); x++ )
278             {
279                 const double tx = xxMap.invTransform(x);
280
281                 *line++ = d_data->colorMap->colorIndex(intensityRange,
282                                                        d_data->data->value(tx, ty));
283             }
284         }
285     }
286
287     d_data->data->discardRaster();
288
289     // Mirror the image in case of inverted maps
290
291     const bool hInvert = xxMap.p1() > xxMap.p2();
292     const bool vInvert = yyMap.p1() < yyMap.p2();
293     if ( hInvert || vInvert )
294     {
295 #ifdef __GNUC__
296 #endif
297 #if QT_VERSION < 0x040000
298         image = image.mirror(hInvert, vInvert);
299 #else
300         image = image.mirrored(hInvert, vInvert);
301 #endif
302     }
303
304     return image;
305 }
306
307 /*!
308   \brief Draw the spectrogram
309
310   \param painter Painter
311   \param xMap Maps x-values into pixel coordinates.
312   \param yMap Maps y-values into pixel coordinates.
313   \param canvasRect Contents rect of the canvas in painter coordinates 
314
315   \sa setDisplayMode, renderImage, 
316   QwtPlotRasterItem::draw, drawContourLines
317 */
318
319 void PlotWaterfall::draw(QPainter *painter,
320                          const QwtScaleMap &xMap, const QwtScaleMap &yMap,
321                          const QRect &canvasRect) const
322 {
323     QwtPlotRasterItem::draw(painter, xMap, yMap, canvasRect);
324 }
325