source: ntrip/trunk/BNC/qwt/qwt_plot_rasteritem.cpp

Last change on this file was 9383, checked in by stoecker, 3 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

File size: 25.4 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_rasteritem.h"
11#include "qwt_scale_map.h"
12#include "qwt_painter.h"
13#include <qapplication.h>
14#include <qdesktopwidget.h>
15#include <qpainter.h>
16#include <qpaintengine.h>
[8127]17#include <qmath.h>
18#if QT_VERSION >= 0x040400
19#include <qthread.h>
20#include <qfuture.h>
21#include <qtconcurrentrun.h>
22#endif
[4271]23#include <float.h>
24
25class QwtPlotRasterItem::PrivateData
26{
27public:
28 PrivateData():
29 alpha( -1 ),
30 paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution )
31 {
32 cache.policy = QwtPlotRasterItem::NoCache;
33 }
34
35 int alpha;
[8127]36
[4271]37 QwtPlotRasterItem::PaintAttributes paintAttributes;
38
39 struct ImageCache
40 {
41 QwtPlotRasterItem::CachePolicy policy;
42 QRectF area;
43 QSizeF size;
44 QImage image;
45 } cache;
46};
47
48
49static QRectF qwtAlignRect(const QRectF &rect)
50{
51 QRectF r;
52 r.setLeft( qRound( rect.left() ) );
53 r.setRight( qRound( rect.right() ) );
54 r.setTop( qRound( rect.top() ) );
55 r.setBottom( qRound( rect.bottom() ) );
56
57 return r;
58}
59
60static QRectF qwtStripRect(const QRectF &rect, const QRectF &area,
61 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
62 const QwtInterval &xInterval, const QwtInterval &yInterval)
63{
64 QRectF r = rect;
65 if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum )
66 {
67 if ( area.left() <= xInterval.minValue() )
68 {
69 if ( xMap.isInverting() )
70 r.adjust(0, 0, -1, 0);
71 else
72 r.adjust(1, 0, 0, 0);
73 }
74 }
75
76 if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum )
77 {
78 if ( area.right() >= xInterval.maxValue() )
79 {
80 if ( xMap.isInverting() )
81 r.adjust(1, 0, 0, 0);
82 else
83 r.adjust(0, 0, -1, 0);
84 }
85 }
86
87 if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum )
88 {
89 if ( area.top() <= yInterval.minValue() )
90 {
91 if ( yMap.isInverting() )
92 r.adjust(0, 0, 0, -1);
93 else
94 r.adjust(0, 1, 0, 0);
95 }
96 }
97
98 if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum )
99 {
100 if ( area.bottom() >= yInterval.maxValue() )
101 {
102 if ( yMap.isInverting() )
103 r.adjust(0, 1, 0, 0);
104 else
105 r.adjust(0, 0, 0, -1);
106 }
107 }
108
109 return r;
110}
111
112static QImage qwtExpandImage(const QImage &image,
113 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
114 const QRectF &area, const QRectF &area2, const QRectF &paintRect,
115 const QwtInterval &xInterval, const QwtInterval &yInterval )
116{
117 const QRectF strippedRect = qwtStripRect(paintRect, area2,
118 xMap, yMap, xInterval, yInterval);
119 const QSize sz = strippedRect.toRect().size();
120
121 const int w = image.width();
122 const int h = image.height();
123
124 const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized();
125 const double pw = ( r.width() - 1) / w;
126 const double ph = ( r.height() - 1) / h;
127
128 double px0, py0;
129 if ( !xMap.isInverting() )
130 {
131 px0 = xMap.transform( area2.left() );
132 px0 = qRound( px0 );
133 px0 = px0 - xMap.transform( area.left() );
134 }
135 else
136 {
137 px0 = xMap.transform( area2.right() );
138 px0 = qRound( px0 );
139 px0 -= xMap.transform( area.right() );
140
141 px0 -= 1.0;
142 }
143 px0 += strippedRect.left() - paintRect.left();
144
145 if ( !yMap.isInverting() )
146 {
147 py0 = yMap.transform( area2.top() );
148 py0 = qRound( py0 );
149 py0 -= yMap.transform( area.top() );
150 }
151 else
152 {
153 py0 = yMap.transform( area2.bottom() );
154 py0 = qRound( py0 );
155 py0 -= yMap.transform( area.bottom() );
156
157 py0 -= 1.0;
158 }
159 py0 += strippedRect.top() - paintRect.top();
160
[9383]161 QImage expanded( sz, image.format() );
162 if ( image.format() == QImage::Format_Indexed8 )
163 expanded.setColorTable( image.colorTable() );
[4271]164
165 switch( image.depth() )
166 {
167 case 32:
168 {
169 for ( int y1 = 0; y1 < h; y1++ )
170 {
171 int yy1;
172 if ( y1 == 0 )
173 {
174 yy1 = 0;
175 }
176 else
177 {
178 yy1 = qRound( y1 * ph - py0 );
179 if ( yy1 < 0 )
180 yy1 = 0;
181 }
182
183 int yy2;
184 if ( y1 == h - 1 )
185 {
186 yy2 = sz.height();
187 }
188 else
189 {
190 yy2 = qRound( ( y1 + 1 ) * ph - py0 );
191 if ( yy2 > sz.height() )
192 yy2 = sz.height();
193 }
194
[9383]195 const quint32 *line1 =
[8127]196 reinterpret_cast<const quint32 *>( image.scanLine( y1 ) );
[4271]197
198 for ( int x1 = 0; x1 < w; x1++ )
199 {
200 int xx1;
201 if ( x1 == 0 )
202 {
203 xx1 = 0;
204 }
205 else
206 {
207 xx1 = qRound( x1 * pw - px0 );
208 if ( xx1 < 0 )
209 xx1 = 0;
210 }
211
212 int xx2;
213 if ( x1 == w - 1 )
214 {
215 xx2 = sz.width();
216 }
217 else
218 {
219 xx2 = qRound( ( x1 + 1 ) * pw - px0 );
220 if ( xx2 > sz.width() )
221 xx2 = sz.width();
222 }
223
224 const quint32 rgb( line1[x1] );
225 for ( int y2 = yy1; y2 < yy2; y2++ )
226 {
[9383]227 quint32 *line2 = reinterpret_cast<quint32 *>(
[8127]228 expanded.scanLine( y2 ) );
229
[9383]230 for ( int x2 = xx1; x2 < xx2; x2++ )
[4271]231 line2[x2] = rgb;
[9383]232 }
233 }
234 }
[4271]235 break;
236 }
237 case 8:
238 {
239 for ( int y1 = 0; y1 < h; y1++ )
240 {
241 int yy1;
242 if ( y1 == 0 )
243 {
244 yy1 = 0;
[9383]245 }
[4271]246 else
247 {
248 yy1 = qRound( y1 * ph - py0 );
249 if ( yy1 < 0 )
[9383]250 yy1 = 0;
251 }
252
[4271]253 int yy2;
254 if ( y1 == h - 1 )
255 {
256 yy2 = sz.height();
[9383]257 }
[4271]258 else
259 {
260 yy2 = qRound( ( y1 + 1 ) * ph - py0 );
261 if ( yy2 > sz.height() )
262 yy2 = sz.height();
263 }
[9383]264
[4271]265 const uchar *line1 = image.scanLine( y1 );
266
267 for ( int x1 = 0; x1 < w; x1++ )
268 {
269 int xx1;
270 if ( x1 == 0 )
271 {
272 xx1 = 0;
273 }
274 else
275 {
276 xx1 = qRound( x1 * pw - px0 );
277 if ( xx1 < 0 )
278 xx1 = 0;
279 }
280
281 int xx2;
282 if ( x1 == w - 1 )
283 {
284 xx2 = sz.width();
285 }
286 else
287 {
288 xx2 = qRound( ( x1 + 1 ) * pw - px0 );
289 if ( xx2 > sz.width() )
290 xx2 = sz.width();
291 }
292
293 for ( int y2 = yy1; y2 < yy2; y2++ )
294 {
295 uchar *line2 = expanded.scanLine( y2 );
296 memset( line2 + xx1, line1[x1], xx2 - xx1 );
[9383]297 }
298 }
[4271]299 }
300 break;
301 }
302 default:
303 expanded = image;
304 }
[9383]305
[4271]306 return expanded;
[9383]307}
[4271]308
[8127]309static QRectF qwtExpandToPixels(const QRectF &rect, const QRectF &pixelRect)
[4271]310{
311 const double pw = pixelRect.width();
312 const double ph = pixelRect.height();
313
314 const double dx1 = pixelRect.left() - rect.left();
315 const double dx2 = pixelRect.right() - rect.right();
316 const double dy1 = pixelRect.top() - rect.top();
317 const double dy2 = pixelRect.bottom() - rect.bottom();
318
319 QRectF r;
320 r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw );
321 r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph );
322 r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw );
323 r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph );
324
325 return r;
326}
327
[8127]328static void qwtTransformMaps( const QTransform &tr,
[4271]329 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
330 QwtScaleMap &xxMap, QwtScaleMap &yyMap )
331{
332 const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) );
333 const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) );
334
335 xxMap = xMap;
336 xxMap.setPaintInterval( p1.x(), p2.x() );
337
338 yyMap = yMap;
339 yyMap.setPaintInterval( p1.y(), p2.y() );
340}
341
[8127]342static void qwtAdjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
[4271]343 const QRectF &area, const QRectF &paintRect)
344{
345 double sx1 = area.left();
346 double sx2 = area.right();
347 if ( xMap.isInverting() )
348 qSwap(sx1, sx2);
349
350 double sy1 = area.top();
351 double sy2 = area.bottom();
352
353 if ( yMap.isInverting() )
354 qSwap(sy1, sy2);
355
356 xMap.setPaintInterval(paintRect.left(), paintRect.right());
357 xMap.setScaleInterval(sx1, sx2);
358
359 yMap.setPaintInterval(paintRect.top(), paintRect.bottom());
360 yMap.setScaleInterval(sy1, sy2);
361}
362
[8127]363static bool qwtUseCache( QwtPlotRasterItem::CachePolicy policy,
[4271]364 const QPainter *painter )
365{
366 bool doCache = false;
367
368 if ( policy == QwtPlotRasterItem::PaintCache )
369 {
370 // Caching doesn't make sense, when the item is
371 // not painted to screen
372
373 switch ( painter->paintEngine()->type() )
374 {
375 case QPaintEngine::SVG:
376 case QPaintEngine::Pdf:
377 case QPaintEngine::PostScript:
378 case QPaintEngine::MacPrinter:
379 case QPaintEngine::Picture:
380 break;
381 default:;
382 doCache = true;
383 }
384 }
385
386 return doCache;
387}
388
[9383]389static void qwtToRgba( const QImage* from, QImage* to,
[8127]390 const QRect& tile, int alpha )
[4271]391{
392 const QRgb mask1 = qRgba( 0, 0, 0, alpha );
393 const QRgb mask2 = qRgba( 255, 255, 255, 0 );
394 const QRgb mask3 = qRgba( 0, 0, 0, 255 );
395
[8127]396 const int y0 = tile.top();
397 const int y1 = tile.bottom();
398 const int x0 = tile.left();
399 const int x1 = tile.right();
[4271]400
[8127]401 if ( from->depth() == 8 )
[4271]402 {
[8127]403 for ( int y = y0; y <= y1; y++ )
[4271]404 {
[8127]405 QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
406 const unsigned char *line = from->scanLine( y );
[4271]407
[8127]408 for ( int x = x0; x <= x1; x++ )
409 *alphaLine++ = ( from->color( *line++ ) & mask2 ) | mask1;
[4271]410 }
411 }
[8127]412 else if ( from->depth() == 32 )
[4271]413 {
[8127]414 for ( int y = y0; y <= y1; y++ )
[4271]415 {
[8127]416 QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
417 const QRgb *line = reinterpret_cast<const QRgb *>( from->scanLine( y ) );
[4271]418
[8127]419 for ( int x = x0; x <= x1; x++ )
[4271]420 {
421 const QRgb rgb = *line++;
422 if ( rgb & mask3 ) // alpha != 0
423 *alphaLine++ = ( rgb & mask2 ) | mask1;
424 else
425 *alphaLine++ = rgb;
426 }
427 }
428 }
429}
430
431//! Constructor
432QwtPlotRasterItem::QwtPlotRasterItem( const QString& title ):
433 QwtPlotItem( QwtText( title ) )
434{
435 init();
436}
437
438//! Constructor
439QwtPlotRasterItem::QwtPlotRasterItem( const QwtText& title ):
440 QwtPlotItem( title )
441{
442 init();
443}
444
445//! Destructor
446QwtPlotRasterItem::~QwtPlotRasterItem()
447{
448 delete d_data;
449}
450
451void QwtPlotRasterItem::init()
452{
453 d_data = new PrivateData();
454
455 setItemAttribute( QwtPlotItem::AutoScale, true );
456 setItemAttribute( QwtPlotItem::Legend, false );
457
458 setZ( 8.0 );
459}
460
461/*!
462 Specify an attribute how to draw the raster item
463
464 \param attribute Paint attribute
465 \param on On/Off
466 /sa PaintAttribute, testPaintAttribute()
467*/
468void QwtPlotRasterItem::setPaintAttribute( PaintAttribute attribute, bool on )
469{
470 if ( on )
471 d_data->paintAttributes |= attribute;
472 else
473 d_data->paintAttributes &= ~attribute;
474}
475
476/*!
[8127]477 \return True, when attribute is enabled
[4271]478 \sa PaintAttribute, setPaintAttribute()
479*/
480bool QwtPlotRasterItem::testPaintAttribute( PaintAttribute attribute ) const
481{
482 return ( d_data->paintAttributes & attribute );
483}
484
485/*!
486 \brief Set an alpha value for the raster data
487
488 Often a plot has several types of raster data organized in layers.
489 ( f.e a geographical map, with weather statistics ).
490 Using setAlpha() raster items can be stacked easily.
491
492 The alpha value is a value [0, 255] to
493 control the transparency of the image. 0 represents a fully
494 transparent color, while 255 represents a fully opaque color.
495
496 \param alpha Alpha value
497
498 - alpha >= 0\n
499 All alpha values of the pixels returned by renderImage() will be set to
500 alpha, beside those with an alpha value of 0 (invalid pixels).
501 - alpha < 0
502 The alpha values returned by renderImage() are not changed.
503
504 The default alpha value is -1.
505
506 \sa alpha()
507*/
508void QwtPlotRasterItem::setAlpha( int alpha )
509{
510 if ( alpha < 0 )
511 alpha = -1;
512
513 if ( alpha > 255 )
514 alpha = 255;
515
516 if ( alpha != d_data->alpha )
517 {
518 d_data->alpha = alpha;
519
520 itemChanged();
521 }
522}
523
524/*!
525 \return Alpha value of the raster item
526 \sa setAlpha()
527*/
528int QwtPlotRasterItem::alpha() const
529{
530 return d_data->alpha;
531}
532
533/*!
534 Change the cache policy
535
536 The default policy is NoCache
537
538 \param policy Cache policy
539 \sa CachePolicy, cachePolicy()
540*/
541void QwtPlotRasterItem::setCachePolicy(
542 QwtPlotRasterItem::CachePolicy policy )
543{
544 if ( d_data->cache.policy != policy )
545 {
546 d_data->cache.policy = policy;
547
548 invalidateCache();
549 itemChanged();
550 }
551}
552
553/*!
554 \return Cache policy
555 \sa CachePolicy, setCachePolicy()
556*/
557QwtPlotRasterItem::CachePolicy QwtPlotRasterItem::cachePolicy() const
558{
559 return d_data->cache.policy;
560}
561
562/*!
563 Invalidate the paint cache
564 \sa setCachePolicy()
565*/
566void QwtPlotRasterItem::invalidateCache()
567{
568 d_data->cache.image = QImage();
569 d_data->cache.area = QRect();
570 d_data->cache.size = QSize();
571}
572
573/*!
574 \brief Pixel hint
575
576 The geometry of a pixel is used to calculated the resolution and
[9383]577 alignment of the rendered image.
[4271]578
[9383]579 Width and height of the hint need to be the horizontal
580 and vertical distances between 2 neighbored points.
581 The center of the hint has to be the position of any point
[4271]582 ( it doesn't matter which one ).
583
584 Limiting the resolution of the image might significantly improve
585 the performance and heavily reduce the amount of memory when rendering
[9383]586 a QImage from the raster data.
[4271]587
588 The default implementation returns an empty rectangle (QRectF()),
589 meaning, that the image will be rendered in target device ( f.e screen )
590 resolution.
591
592 \param area In most implementations the resolution of the data doesn't
593 depend on the requested area.
594
595 \return Bounding rectangle of a pixel
596
597 \sa render(), renderImage()
598*/
599QRectF QwtPlotRasterItem::pixelHint( const QRectF &area ) const
600{
601 Q_UNUSED( area );
602 return QRectF();
603}
604
605/*!
606 \brief Draw the raster data
607 \param painter Painter
608 \param xMap X-Scale Map
609 \param yMap Y-Scale Map
[8127]610 \param canvasRect Contents rectangle of the plot canvas
[4271]611*/
612void QwtPlotRasterItem::draw( QPainter *painter,
613 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
614 const QRectF &canvasRect ) const
615{
616 if ( canvasRect.isEmpty() || d_data->alpha == 0 )
617 return;
618
[8127]619 const bool doCache = qwtUseCache( d_data->cache.policy, painter );
[4271]620
621 const QwtInterval xInterval = interval( Qt::XAxis );
622 const QwtInterval yInterval = interval( Qt::YAxis );
623
624 /*
[8127]625 Scaling an image always results in a loss of
[4271]626 precision/quality. So we always render the image in
627 paint device resolution.
628 */
629
630 QwtScaleMap xxMap, yyMap;
[8127]631 qwtTransformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
[4271]632
633 QRectF paintRect = painter->transform().mapRect( canvasRect );
634 QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect );
635
636 const QRectF br = boundingRect();
637 if ( br.isValid() && !br.contains( area ) )
638 {
639 area &= br;
640 if ( !area.isValid() )
641 return;
642
643 paintRect = QwtScaleMap::transform( xxMap, yyMap, area );
644 }
645
646 QRectF imageRect;
647 QImage image;
648
649 QRectF pixelRect = pixelHint(area);
650 if ( !pixelRect.isEmpty() )
651 {
[8127]652 // one pixel of the target device in plot coordinates
653 const double dx = qAbs( xxMap.invTransform( 1 ) - xxMap.invTransform( 0 ) );
654 const double dy = qAbs( yyMap.invTransform( 1 ) - yyMap.invTransform( 0 ) );
[4271]655
[8127]656 if ( dx > pixelRect.width() && dy > pixelRect.height() )
[4271]657 {
658 /*
659 When the resolution of the data pixels is higher than
660 the resolution of the target device we render in
661 target device resolution.
662 */
663 pixelRect = QRectF();
664 }
[8127]665 else
666 {
667 /*
[9383]668 If only one dimension is of the data pixel is higher
[8127]669 we expand the pixel rect to the resolution of the target device.
670 */
671
672 if ( dx > pixelRect.width() )
673 pixelRect.setWidth( dx );
674
675 if ( dy > pixelRect.height() )
676 pixelRect.setHeight( dy );
677 }
[4271]678 }
679
680 if ( pixelRect.isEmpty() )
681 {
682 if ( QwtPainter::roundingAlignment( painter ) )
683 {
684 // we want to have maps, where the boundaries of
685 // the aligned paint rectangle exactly match the area
686
687 paintRect = qwtAlignRect(paintRect);
[8127]688 qwtAdjustMaps(xxMap, yyMap, area, paintRect);
[4271]689 }
690
691 // When we have no information about position and size of
692 // data pixels we render in resolution of the paint device.
693
[9383]694 image = compose(xxMap, yyMap,
[4271]695 area, paintRect, paintRect.size().toSize(), doCache);
696 if ( image.isNull() )
697 return;
698
699 // Remove pixels at the boundaries, when explicitly
700 // excluded in the intervals
701
[9383]702 imageRect = qwtStripRect(paintRect, area,
[4271]703 xxMap, yyMap, xInterval, yInterval);
704
705 if ( imageRect != paintRect )
706 {
[9383]707 const QRect r(
[4271]708 qRound( imageRect.x() - paintRect.x()),
709 qRound( imageRect.y() - paintRect.y() ),
710 qRound( imageRect.width() ),
711 qRound( imageRect.height() ) );
[9383]712
[4271]713 image = image.copy(r);
[9383]714 }
[4271]715 }
716 else
717 {
718 if ( QwtPainter::roundingAlignment( painter ) )
719 paintRect = qwtAlignRect(paintRect);
720
721 // align the area to the data pixels
[8127]722 QRectF imageArea = qwtExpandToPixels(area, pixelRect);
[4271]723
724 if ( imageArea.right() == xInterval.maxValue() &&
725 !( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
726 {
727 imageArea.adjust(0, 0, pixelRect.width(), 0);
728 }
729 if ( imageArea.bottom() == yInterval.maxValue() &&
730 !( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
731 {
732 imageArea.adjust(0, 0, 0, pixelRect.height() );
733 }
734
735 QSize imageSize;
736 imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) );
737 imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) );
[8127]738
[9383]739 image = compose(xxMap, yyMap,
[4271]740 imageArea, paintRect, imageSize, doCache );
[8127]741
[4271]742 if ( image.isNull() )
743 return;
744
[9383]745 imageRect = qwtStripRect(paintRect, area,
[4271]746 xxMap, yyMap, xInterval, yInterval);
747
748 if ( ( image.width() > 1 || image.height() > 1 ) &&
749 testPaintAttribute( PaintInDeviceResolution ) )
750 {
[9383]751 // Because of rounding errors the pixels
752 // need to be expanded manually to rectangles of
[4271]753 // different sizes
754
[9383]755 image = qwtExpandImage(image, xxMap, yyMap,
[4271]756 imageArea, area, paintRect, xInterval, yInterval );
757 }
758 }
759
760 painter->save();
761 painter->setWorldTransform( QTransform() );
[9383]762
[4271]763 QwtPainter::drawImage( painter, imageRect, image );
764
765 painter->restore();
766}
767
768/*!
769 \return Bounding interval for an axis
770
771 This method is intended to be reimplemented by derived classes.
772 The default implementation returns an invalid interval.
[9383]773
[4271]774 \param axis X, Y, or Z axis
775*/
776QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const
777{
778 Q_UNUSED( axis );
779 return QwtInterval();
780}
781
782/*!
[8127]783 \return Bounding rectangle of the data
[4271]784 \sa QwtPlotRasterItem::interval()
785*/
786QRectF QwtPlotRasterItem::boundingRect() const
787{
788 const QwtInterval intervalX = interval( Qt::XAxis );
789 const QwtInterval intervalY = interval( Qt::YAxis );
790
791 if ( !intervalX.isValid() && !intervalY.isValid() )
792 return QRectF(); // no bounding rect
793
794 QRectF r;
795
796 if ( intervalX.isValid() )
797 {
798 r.setLeft( intervalX.minValue() );
799 r.setRight( intervalX.maxValue() );
800 }
801 else
802 {
803 r.setLeft(-0.5 * FLT_MAX);
804 r.setWidth(FLT_MAX);
805 }
806
807 if ( intervalY.isValid() )
808 {
809 r.setTop( intervalY.minValue() );
810 r.setBottom( intervalY.maxValue() );
811 }
812 else
813 {
814 r.setTop(-0.5 * FLT_MAX);
815 r.setHeight(FLT_MAX);
816 }
817
818 return r.normalized();
819}
820
[9383]821QImage QwtPlotRasterItem::compose(
[4271]822 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
[9383]823 const QRectF &imageArea, const QRectF &paintRect,
[4271]824 const QSize &imageSize, bool doCache) const
825{
826 QImage image;
827 if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() )
828 return image;
829
830 if ( doCache )
831 {
832 if ( !d_data->cache.image.isNull()
833 && d_data->cache.area == imageArea
834 && d_data->cache.size == paintRect.size() )
835 {
836 image = d_data->cache.image;
837 }
838 }
839
840 if ( image.isNull() )
841 {
842 double dx = 0.0;
843 if ( paintRect.toRect().width() > imageSize.width() )
844 dx = imageArea.width() / imageSize.width();
845
[9383]846 const QwtScaleMap xxMap =
[4271]847 imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
[9383]848
[4271]849 double dy = 0.0;
850 if ( paintRect.toRect().height() > imageSize.height() )
851 dy = imageArea.height() / imageSize.height();
852
[9383]853 const QwtScaleMap yyMap =
[4271]854 imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy);
855
856 image = renderImage( xxMap, yyMap, imageArea, imageSize );
857
858 if ( doCache )
859 {
860 d_data->cache.area = imageArea;
861 d_data->cache.size = paintRect.size();
862 d_data->cache.image = image;
863 }
864 }
865
866 if ( d_data->alpha >= 0 && d_data->alpha < 255 )
[8127]867 {
868 QImage alphaImage( image.size(), QImage::Format_ARGB32 );
[4271]869
[8127]870#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
871 uint numThreads = renderThreadCount();
872
873 if ( numThreads <= 0 )
874 numThreads = QThread::idealThreadCount();
875
876 if ( numThreads <= 0 )
877 numThreads = 1;
878
879 const int numRows = image.height() / numThreads;
880
881 QList< QFuture<void> > futures;
882 for ( uint i = 0; i < numThreads; i++ )
883 {
884 QRect tile( 0, i * numRows, image.width(), numRows );
885 if ( i == numThreads - 1 )
886 {
887 tile.setHeight( image.height() - i * numRows );
888 qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
889 }
890 else
891 {
892 futures += QtConcurrent::run(
893 &qwtToRgba, &image, &alphaImage, tile, d_data->alpha );
894 }
895 }
896 for ( int i = 0; i < futures.size(); i++ )
897 futures[i].waitForFinished();
898#else
899 const QRect tile( 0, 0, image.width(), image.height() );
900 qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
901#endif
902 image = alphaImage;
903 }
904
[4271]905 return image;
906}
907
908/*!
909 \brief Calculate a scale map for painting to an image
910
911 \param orientation Orientation, Qt::Horizontal means a X axis
912 \param map Scale map for rendering the plot item
913 \param area Area to be painted on the image
914 \param imageSize Image size
915 \param pixelSize Width/Height of a data pixel
[8127]916
917 \return Calculated scale map
[4271]918*/
919QwtScaleMap QwtPlotRasterItem::imageMap(
920 Qt::Orientation orientation,
921 const QwtScaleMap &map, const QRectF &area,
922 const QSize &imageSize, double pixelSize) const
923{
924 double p1, p2, s1, s2;
925
926 if ( orientation == Qt::Horizontal )
927 {
928 p1 = 0.0;
929 p2 = imageSize.width();
930 s1 = area.left();
931 s2 = area.right();
932 }
933 else
934 {
935 p1 = 0.0;
936 p2 = imageSize.height();
937 s1 = area.top();
938 s2 = area.bottom();
939 }
940
[9383]941 if ( pixelSize > 0.0 || p2 == 1.0 )
[4271]942 {
943 double off = 0.5 * pixelSize;
944 if ( map.isInverting() )
945 off = -off;
946
947 s1 += off;
948 s2 += off;
949 }
950 else
951 {
952 p2--;
953 }
954
955 if ( map.isInverting() && ( s1 < s2 ) )
956 qSwap( s1, s2 );
957
958 QwtScaleMap newMap = map;
959 newMap.setPaintInterval( p1, p2 );
960 newMap.setScaleInterval( s1, s2 );
961
962 return newMap;
963}
Note: See TracBrowser for help on using the repository browser.