7
7
* modify it under the terms of the Qwt License, Version 1.0
8
8
*****************************************************************************/
10
#include "qwt_plot_rasteritem.h"
11
#include "qwt_legend.h"
12
#include "qwt_legend_item.h"
13
#include "qwt_scale_map.h"
14
#include "qwt_painter.h"
10
15
#include <qapplication.h>
11
16
#include <qdesktopwidget.h>
12
#include <qpaintdevice.h>
13
17
#include <qpainter.h>
14
#include "qwt_legend.h"
15
#include "qwt_legend_item.h"
16
#include "qwt_scale_map.h"
17
#include "qwt_plot_rasteritem.h"
18
#include <qpaintengine.h>
19
21
class QwtPlotRasterItem::PrivateData
26
paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution )
25
28
cache.policy = QwtPlotRasterItem::NoCache;
32
QwtPlotRasterItem::PaintAttributes paintAttributes;
32
36
QwtPlotRasterItem::CachePolicy policy;
39
static QImage toRgba(const QImage& image, int alpha)
41
if ( alpha < 0 || alpha >= 255 )
44
static QRectF qwtAlignRect(const QRectF &rect)
47
r.setLeft( qRound( rect.left() ) );
48
r.setRight( qRound( rect.right() ) );
49
r.setTop( qRound( rect.top() ) );
50
r.setBottom( qRound( rect.bottom() ) );
55
static QRectF qwtStripRect(const QRectF &rect, const QRectF &area,
56
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
57
const QwtInterval &xInterval, const QwtInterval &yInterval)
60
if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum )
62
if ( area.left() <= xInterval.minValue() )
64
if ( xMap.isInverting() )
65
r.adjust(0, 0, -1, 0);
71
if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum )
73
if ( area.right() >= xInterval.maxValue() )
75
if ( xMap.isInverting() )
78
r.adjust(0, 0, -1, 0);
82
if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum )
84
if ( area.top() <= yInterval.minValue() )
86
if ( yMap.isInverting() )
87
r.adjust(0, 0, 0, -1);
93
if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum )
95
if ( area.bottom() >= yInterval.maxValue() )
97
if ( yMap.isInverting() )
100
r.adjust(0, 0, 0, -1);
107
static QImage qwtExpandImage(const QImage &image,
108
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
109
const QRectF &area, const QRectF &area2, const QRectF &paintRect,
110
const QwtInterval &xInterval, const QwtInterval &yInterval )
112
const QRectF strippedRect = qwtStripRect(paintRect, area2,
113
xMap, yMap, xInterval, yInterval);
114
const QSize sz = strippedRect.toRect().size();
116
const int w = image.width();
117
const int h = image.height();
119
const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized();
120
const double pw = ( r.width() - 1) / w;
121
const double ph = ( r.height() - 1) / h;
124
if ( !xMap.isInverting() )
126
px0 = xMap.transform( area2.left() );
128
px0 = px0 - xMap.transform( area.left() );
132
px0 = xMap.transform( area2.right() );
134
px0 -= xMap.transform( area.right() );
138
px0 += strippedRect.left() - paintRect.left();
140
if ( !yMap.isInverting() )
142
py0 = yMap.transform( area2.top() );
144
py0 -= yMap.transform( area.top() );
148
py0 = yMap.transform( area2.bottom() );
150
py0 -= yMap.transform( area.bottom() );
154
py0 += strippedRect.top() - paintRect.top();
156
QImage expanded(sz, image.format());
158
switch( image.depth() )
162
for ( int y1 = 0; y1 < h; y1++ )
171
yy1 = qRound( y1 * ph - py0 );
183
yy2 = qRound( ( y1 + 1 ) * ph - py0 );
184
if ( yy2 > sz.height() )
188
const quint32 *line1 = (const quint32 *) image.scanLine( y1 );
190
for ( int x1 = 0; x1 < w; x1++ )
199
xx1 = qRound( x1 * pw - px0 );
211
xx2 = qRound( ( x1 + 1 ) * pw - px0 );
212
if ( xx2 > sz.width() )
216
const quint32 rgb( line1[x1] );
217
for ( int y2 = yy1; y2 < yy2; y2++ )
219
quint32 *line2 = ( quint32 *) expanded.scanLine( y2 );
220
for ( int x2 = xx1; x2 < xx2; x2++ )
229
for ( int y1 = 0; y1 < h; y1++ )
238
yy1 = qRound( y1 * ph - py0 );
250
yy2 = qRound( ( y1 + 1 ) * ph - py0 );
251
if ( yy2 > sz.height() )
255
const uchar *line1 = image.scanLine( y1 );
257
for ( int x1 = 0; x1 < w; x1++ )
266
xx1 = qRound( x1 * pw - px0 );
278
xx2 = qRound( ( x1 + 1 ) * pw - px0 );
279
if ( xx2 > sz.width() )
283
for ( int y2 = yy1; y2 < yy2; y2++ )
285
uchar *line2 = expanded.scanLine( y2 );
286
memset( line2 + xx1, line1[x1], xx2 - xx1 );
299
static QRectF expandToPixels(const QRectF &rect, const QRectF &pixelRect)
301
const double pw = pixelRect.width();
302
const double ph = pixelRect.height();
304
const double dx1 = pixelRect.left() - rect.left();
305
const double dx2 = pixelRect.right() - rect.right();
306
const double dy1 = pixelRect.top() - rect.top();
307
const double dy2 = pixelRect.bottom() - rect.bottom();
310
r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw );
311
r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph );
312
r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw );
313
r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph );
318
static void transformMaps( const QTransform &tr,
319
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
320
QwtScaleMap &xxMap, QwtScaleMap &yyMap )
322
const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) );
323
const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) );
326
xxMap.setPaintInterval( p1.x(), p2.x() );
329
yyMap.setPaintInterval( p1.y(), p2.y() );
332
static void adjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
333
const QRectF &area, const QRectF &paintRect)
335
double sx1 = area.left();
336
double sx2 = area.right();
337
if ( xMap.isInverting() )
340
double sy1 = area.top();
341
double sy2 = area.bottom();
343
if ( yMap.isInverting() )
346
xMap.setPaintInterval(paintRect.left(), paintRect.right());
347
xMap.setScaleInterval(sx1, sx2);
349
yMap.setPaintInterval(paintRect.top(), paintRect.bottom());
350
yMap.setScaleInterval(sy1, sy2);
353
static bool useCache( QwtPlotRasterItem::CachePolicy policy,
354
const QPainter *painter )
356
bool doCache = false;
358
if ( policy == QwtPlotRasterItem::PaintCache )
360
// Caching doesn't make sense, when the item is
361
// not painted to screen
363
switch ( painter->paintEngine()->type() )
365
case QPaintEngine::SVG:
366
case QPaintEngine::Pdf:
367
case QPaintEngine::PostScript:
368
case QPaintEngine::MacPrinter:
369
case QPaintEngine::Picture:
379
static QImage toRgba( const QImage& image, int alpha )
381
if ( alpha < 0 || alpha >= 255 )
44
#if QT_VERSION < 0x040000
45
QImage alphaImage(image.size(), 32);
46
alphaImage.setAlphaBuffer(true);
48
QImage alphaImage(image.size(), QImage::Format_ARGB32);
384
QImage alphaImage( image.size(), QImage::Format_ARGB32 );
51
const QRgb mask1 = qRgba(0, 0, 0, alpha);
52
const QRgb mask2 = qRgba(255, 255, 255, 0);
53
const QRgb mask3 = qRgba(0, 0, 0, 255);
386
const QRgb mask1 = qRgba( 0, 0, 0, alpha );
387
const QRgb mask2 = qRgba( 255, 255, 255, 0 );
388
const QRgb mask3 = qRgba( 0, 0, 0, 255 );
55
390
const int w = image.size().width();
56
391
const int h = image.size().height();
226
603
\param yMap Y-Scale Map
227
604
\param canvasRect Contents rect of the plot canvas
229
void QwtPlotRasterItem::draw(QPainter *painter,
606
void QwtPlotRasterItem::draw( QPainter *painter,
230
607
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
231
const QRect &canvasRect) const
608
const QRectF &canvasRect ) const
233
610
if ( canvasRect.isEmpty() || d_data->alpha == 0 )
236
QwtDoubleRect area = invTransform(xMap, yMap, canvasRect);
237
if ( boundingRect().isValid() )
238
area &= boundingRect();
240
const QRect paintRect = transform(xMap, yMap, area);
241
if ( !paintRect.isValid() )
247
if ( painter->device()->devType() == QInternal::Printer
248
|| painter->device()->devType() == QInternal::Picture )
253
if ( !doCache || d_data->cache.policy == NoCache )
255
image = renderImage(xMap, yMap, area);
256
if ( d_data->alpha >= 0 && d_data->alpha < 255 )
257
image = toRgba(image, d_data->alpha);
260
else if ( d_data->cache.policy == PaintCache )
262
if ( d_data->cache.image.isNull() || d_data->cache.rect != area
263
|| d_data->cache.size != paintRect.size() )
265
d_data->cache.image = renderImage(xMap, yMap, area);
266
d_data->cache.rect = area;
613
const bool doCache = useCache( d_data->cache.policy, painter );
615
const QwtInterval xInterval = interval( Qt::XAxis );
616
const QwtInterval yInterval = interval( Qt::YAxis );
619
Scaling a rastered image always results in a loss of
620
precision/quality. So we always render the image in
621
paint device resolution.
624
QwtScaleMap xxMap, yyMap;
625
transformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
627
QRectF paintRect = painter->transform().mapRect( canvasRect );
628
QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect );
630
const QRectF br = boundingRect();
631
if ( br.isValid() && !br.contains( area ) )
634
if ( !area.isValid() )
637
paintRect = QwtScaleMap::transform( xxMap, yyMap, area );
643
QRectF pixelRect = pixelHint(area);
644
if ( !pixelRect.isEmpty() )
646
const QRectF r = QwtScaleMap::invTransform(
647
xxMap, yyMap, QRectF(0, 0, 1, 1) ).normalized();
649
if ( r.width() > pixelRect.width() &&
650
r.height() > pixelRect.height() )
653
When the resolution of the data pixels is higher than
654
the resolution of the target device we render in
655
target device resolution.
657
pixelRect = QRectF();
661
if ( pixelRect.isEmpty() )
663
if ( QwtPainter::roundingAlignment( painter ) )
665
// we want to have maps, where the boundaries of
666
// the aligned paint rectangle exactly match the area
668
paintRect = qwtAlignRect(paintRect);
669
adjustMaps(xxMap, yyMap, area, paintRect);
672
// When we have no information about position and size of
673
// data pixels we render in resolution of the paint device.
675
image = compose(xxMap, yyMap,
676
area, paintRect, paintRect.size().toSize(), doCache);
677
if ( image.isNull() )
680
// Remove pixels at the boundaries, when explicitly
681
// excluded in the intervals
683
imageRect = qwtStripRect(paintRect, area,
684
xxMap, yyMap, xInterval, yInterval);
686
if ( imageRect != paintRect )
689
qRound( imageRect.x() - paintRect.x()),
690
qRound( imageRect.y() - paintRect.y() ),
691
qRound( imageRect.width() ),
692
qRound( imageRect.height() ) );
694
image = image.copy(r);
699
if ( QwtPainter::roundingAlignment( painter ) )
700
paintRect = qwtAlignRect(paintRect);
702
// align the area to the data pixels
703
QRectF imageArea = expandToPixels(area, pixelRect);
705
if ( imageArea.right() == xInterval.maxValue() &&
706
!( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
708
imageArea.adjust(0, 0, pixelRect.width(), 0);
710
if ( imageArea.bottom() == yInterval.maxValue() &&
711
!( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
713
imageArea.adjust(0, 0, 0, pixelRect.height() );
717
imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) );
718
imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) );
719
image = compose(xxMap, yyMap,
720
imageArea, paintRect, imageSize, doCache );
721
if ( image.isNull() )
724
imageRect = qwtStripRect(paintRect, area,
725
xxMap, yyMap, xInterval, yInterval);
727
if ( ( image.width() > 1 || image.height() > 1 ) &&
728
testPaintAttribute( PaintInDeviceResolution ) )
730
// Because of rounding errors the pixels
731
// need to be expanded manually to rectangles of
734
image = qwtExpandImage(image, xxMap, yyMap,
735
imageArea, area, paintRect, xInterval, yInterval );
740
painter->setWorldTransform( QTransform() );
742
QwtPainter::drawImage( painter, imageRect, image );
748
\return Bounding interval for an axis
750
This method is intended to be reimplemented by derived classes.
751
The default implementation returns an invalid interval.
753
\param axis X, Y, or Z axis
755
QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const
758
return QwtInterval();
762
\return Bounding rect of the data
763
\sa QwtPlotRasterItem::interval()
765
QRectF QwtPlotRasterItem::boundingRect() const
767
const QwtInterval intervalX = interval( Qt::XAxis );
768
const QwtInterval intervalY = interval( Qt::YAxis );
770
if ( !intervalX.isValid() && !intervalY.isValid() )
771
return QRectF(); // no bounding rect
775
if ( intervalX.isValid() )
777
r.setLeft( intervalX.minValue() );
778
r.setRight( intervalX.maxValue() );
782
r.setLeft(-0.5 * FLT_MAX);
786
if ( intervalY.isValid() )
788
r.setTop( intervalY.minValue() );
789
r.setBottom( intervalY.maxValue() );
793
r.setTop(-0.5 * FLT_MAX);
794
r.setHeight(FLT_MAX);
797
return r.normalized();
800
QImage QwtPlotRasterItem::compose(
801
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
802
const QRectF &imageArea, const QRectF &paintRect,
803
const QSize &imageSize, bool doCache) const
806
if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() )
811
if ( !d_data->cache.image.isNull()
812
&& d_data->cache.area == imageArea
813
&& d_data->cache.size == paintRect.size() )
815
image = d_data->cache.image;
819
if ( image.isNull() )
822
if ( paintRect.toRect().width() > imageSize.width() )
823
dx = imageArea.width() / imageSize.width();
825
const QwtScaleMap xxMap =
826
imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
829
if ( paintRect.toRect().height() > imageSize.height() )
830
dy = imageArea.height() / imageSize.height();
832
const QwtScaleMap yyMap =
833
imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy);
835
image = renderImage( xxMap, yyMap, imageArea, imageSize );
839
d_data->cache.area = imageArea;
267
840
d_data->cache.size = paintRect.size();
270
image = d_data->cache.image;
271
if ( d_data->alpha >= 0 && d_data->alpha < 255 )
272
image = toRgba(image, d_data->alpha);
274
else if ( d_data->cache.policy == ScreenCache )
276
const QSize screenSize =
277
QApplication::desktop()->screenGeometry().size();
279
if ( paintRect.width() > screenSize.width() ||
280
paintRect.height() > screenSize.height() )
282
image = renderImage(xMap, yMap, area);
286
if ( d_data->cache.image.isNull() || d_data->cache.rect != area )
288
QwtScaleMap cacheXMap = xMap;
289
cacheXMap.setPaintInterval( 0, screenSize.width());
291
QwtScaleMap cacheYMap = yMap;
292
cacheYMap.setPaintInterval(screenSize.height(), 0);
294
d_data->cache.image = renderImage(
295
cacheXMap, cacheYMap, area);
296
d_data->cache.rect = area;
297
d_data->cache.size = paintRect.size();
300
image = d_data->cache.image;
302
image = toRgba(image, d_data->alpha);
305
painter->drawImage(paintRect, image);
841
d_data->cache.image = image;
845
if ( d_data->alpha >= 0 && d_data->alpha < 255 )
846
image = toRgba( image, d_data->alpha );
852
\brief Calculate a scale map for painting to an image
854
\param orientation Orientation, Qt::Horizontal means a X axis
855
\param map Scale map for rendering the plot item
856
\param area Area to be painted on the image
857
\param imageSize Image size
858
\param pixelSize Width/Height of a data pixel
860
QwtScaleMap QwtPlotRasterItem::imageMap(
861
Qt::Orientation orientation,
862
const QwtScaleMap &map, const QRectF &area,
863
const QSize &imageSize, double pixelSize) const
865
double p1, p2, s1, s2;
867
if ( orientation == Qt::Horizontal )
870
p2 = imageSize.width();
877
p2 = imageSize.height();
882
if ( pixelSize > 0.0 )
884
double off = 0.5 * pixelSize;
885
if ( map.isInverting() )
896
if ( map.isInverting() && ( s1 < s2 ) )
899
QwtScaleMap newMap = map;
900
newMap.setPaintInterval( p1, p2 );
901
newMap.setScaleInterval( s1, s2 );