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

Last change on this file since 10573 was 9383, checked in by stoecker, 4 years ago

update to qwt verion 6.1.1 to fix build with newer Qt5

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 if ( image.format() == QImage::Format_Indexed8 )
163 expanded.setColorTable( image.colorTable() );
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
195 const quint32 *line1 =
196 reinterpret_cast<const quint32 *>( image.scanLine( y1 ) );
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 {
227 quint32 *line2 = reinterpret_cast<quint32 *>(
228 expanded.scanLine( y2 ) );
229
230 for ( int x2 = xx1; x2 < xx2; x2++ )
231 line2[x2] = rgb;
232 }
233 }
234 }
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;
245 }
246 else
247 {
248 yy1 = qRound( y1 * ph - py0 );
249 if ( yy1 < 0 )
250 yy1 = 0;
251 }
252
253 int yy2;
254 if ( y1 == h - 1 )
255 {
256 yy2 = sz.height();
257 }
258 else
259 {
260 yy2 = qRound( ( y1 + 1 ) * ph - py0 );
261 if ( yy2 > sz.height() )
262 yy2 = sz.height();
263 }
264
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 );
297 }
298 }
299 }
300 break;
301 }
302 default:
303 expanded = image;
304 }
305
306 return expanded;
307}
308
309static QRectF qwtExpandToPixels(const QRectF &rect, const QRectF &pixelRect)
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
328static void qwtTransformMaps( const QTransform &tr,
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
342static void qwtAdjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
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
363static bool qwtUseCache( QwtPlotRasterItem::CachePolicy policy,
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
389static void qwtToRgba( const QImage* from, QImage* to,
390 const QRect& tile, int alpha )
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
396 const int y0 = tile.top();
397 const int y1 = tile.bottom();
398 const int x0 = tile.left();
399 const int x1 = tile.right();
400
401 if ( from->depth() == 8 )
402 {
403 for ( int y = y0; y <= y1; y++ )
404 {
405 QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
406 const unsigned char *line = from->scanLine( y );
407
408 for ( int x = x0; x <= x1; x++ )
409 *alphaLine++ = ( from->color( *line++ ) & mask2 ) | mask1;
410 }
411 }
412 else if ( from->depth() == 32 )
413 {
414 for ( int y = y0; y <= y1; y++ )
415 {
416 QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
417 const QRgb *line = reinterpret_cast<const QRgb *>( from->scanLine( y ) );
418
419 for ( int x = x0; x <= x1; x++ )
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/*!
477 \return True, when attribute is enabled
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
577 alignment of the rendered image.
578
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
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
586 a QImage from the raster data.
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
610 \param canvasRect Contents rectangle of the plot canvas
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
619 const bool doCache = qwtUseCache( d_data->cache.policy, painter );
620
621 const QwtInterval xInterval = interval( Qt::XAxis );
622 const QwtInterval yInterval = interval( Qt::YAxis );
623
624 /*
625 Scaling an image always results in a loss of
626 precision/quality. So we always render the image in
627 paint device resolution.
628 */
629
630 QwtScaleMap xxMap, yyMap;
631 qwtTransformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
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 {
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 ) );
655
656 if ( dx > pixelRect.width() && dy > pixelRect.height() )
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 }
665 else
666 {
667 /*
668 If only one dimension is of the data pixel is higher
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 }
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);
688 qwtAdjustMaps(xxMap, yyMap, area, paintRect);
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
694 image = compose(xxMap, yyMap,
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
702 imageRect = qwtStripRect(paintRect, area,
703 xxMap, yyMap, xInterval, yInterval);
704
705 if ( imageRect != paintRect )
706 {
707 const QRect r(
708 qRound( imageRect.x() - paintRect.x()),
709 qRound( imageRect.y() - paintRect.y() ),
710 qRound( imageRect.width() ),
711 qRound( imageRect.height() ) );
712
713 image = image.copy(r);
714 }
715 }
716 else
717 {
718 if ( QwtPainter::roundingAlignment( painter ) )
719 paintRect = qwtAlignRect(paintRect);
720
721 // align the area to the data pixels
722 QRectF imageArea = qwtExpandToPixels(area, pixelRect);
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() ) );
738
739 image = compose(xxMap, yyMap,
740 imageArea, paintRect, imageSize, doCache );
741
742 if ( image.isNull() )
743 return;
744
745 imageRect = qwtStripRect(paintRect, area,
746 xxMap, yyMap, xInterval, yInterval);
747
748 if ( ( image.width() > 1 || image.height() > 1 ) &&
749 testPaintAttribute( PaintInDeviceResolution ) )
750 {
751 // Because of rounding errors the pixels
752 // need to be expanded manually to rectangles of
753 // different sizes
754
755 image = qwtExpandImage(image, xxMap, yyMap,
756 imageArea, area, paintRect, xInterval, yInterval );
757 }
758 }
759
760 painter->save();
761 painter->setWorldTransform( QTransform() );
762
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.
773
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/*!
783 \return Bounding rectangle of the data
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
821QImage QwtPlotRasterItem::compose(
822 const QwtScaleMap &xMap, const QwtScaleMap &yMap,
823 const QRectF &imageArea, const QRectF &paintRect,
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
846 const QwtScaleMap xxMap =
847 imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
848
849 double dy = 0.0;
850 if ( paintRect.toRect().height() > imageSize.height() )
851 dy = imageArea.height() / imageSize.height();
852
853 const QwtScaleMap yyMap =
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 )
867 {
868 QImage alphaImage( image.size(), QImage::Format_ARGB32 );
869
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
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
916
917 \return Calculated scale map
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
941 if ( pixelSize > 0.0 || p2 == 1.0 )
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.