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

Last change on this file since 9029 was 8127, checked in by stoecker, 8 years ago

update qwt and qwtpolar, many QT5 fixes (unfinished)

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