彻底改版2.0
This commit is contained in:
961
third/3rd_qwt/qwt_plot_rasteritem.cpp
Normal file
961
third/3rd_qwt/qwt_plot_rasteritem.cpp
Normal file
@@ -0,0 +1,961 @@
|
||||
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
||||
* Qwt Widget Library
|
||||
* Copyright (C) 1997 Josef Wilgen
|
||||
* Copyright (C) 2002 Uwe Rathmann
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the Qwt License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "qwt_plot_rasteritem.h"
|
||||
#include "qwt_scale_map.h"
|
||||
#include "qwt_painter.h"
|
||||
#include <qapplication.h>
|
||||
#include <qdesktopwidget.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpaintengine.h>
|
||||
#include <qmath.h>
|
||||
#if QT_VERSION >= 0x040400
|
||||
#include <qthread.h>
|
||||
#include <qfuture.h>
|
||||
#include <qtconcurrentrun.h>
|
||||
#endif
|
||||
#include <float.h>
|
||||
|
||||
class QwtPlotRasterItem::PrivateData
|
||||
{
|
||||
public:
|
||||
PrivateData():
|
||||
alpha( -1 ),
|
||||
paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution )
|
||||
{
|
||||
cache.policy = QwtPlotRasterItem::NoCache;
|
||||
}
|
||||
|
||||
int alpha;
|
||||
|
||||
QwtPlotRasterItem::PaintAttributes paintAttributes;
|
||||
|
||||
struct ImageCache
|
||||
{
|
||||
QwtPlotRasterItem::CachePolicy policy;
|
||||
QRectF area;
|
||||
QSizeF size;
|
||||
QImage image;
|
||||
} cache;
|
||||
};
|
||||
|
||||
|
||||
static QRectF qwtAlignRect(const QRectF &rect)
|
||||
{
|
||||
QRectF r;
|
||||
r.setLeft( qRound( rect.left() ) );
|
||||
r.setRight( qRound( rect.right() ) );
|
||||
r.setTop( qRound( rect.top() ) );
|
||||
r.setBottom( qRound( rect.bottom() ) );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static QRectF qwtStripRect(const QRectF &rect, const QRectF &area,
|
||||
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
||||
const QwtInterval &xInterval, const QwtInterval &yInterval)
|
||||
{
|
||||
QRectF r = rect;
|
||||
if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum )
|
||||
{
|
||||
if ( area.left() <= xInterval.minValue() )
|
||||
{
|
||||
if ( xMap.isInverting() )
|
||||
r.adjust(0, 0, -1, 0);
|
||||
else
|
||||
r.adjust(1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum )
|
||||
{
|
||||
if ( area.right() >= xInterval.maxValue() )
|
||||
{
|
||||
if ( xMap.isInverting() )
|
||||
r.adjust(1, 0, 0, 0);
|
||||
else
|
||||
r.adjust(0, 0, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum )
|
||||
{
|
||||
if ( area.top() <= yInterval.minValue() )
|
||||
{
|
||||
if ( yMap.isInverting() )
|
||||
r.adjust(0, 0, 0, -1);
|
||||
else
|
||||
r.adjust(0, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum )
|
||||
{
|
||||
if ( area.bottom() >= yInterval.maxValue() )
|
||||
{
|
||||
if ( yMap.isInverting() )
|
||||
r.adjust(0, 1, 0, 0);
|
||||
else
|
||||
r.adjust(0, 0, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static QImage qwtExpandImage(const QImage &image,
|
||||
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
||||
const QRectF &area, const QRectF &area2, const QRectF &paintRect,
|
||||
const QwtInterval &xInterval, const QwtInterval &yInterval )
|
||||
{
|
||||
const QRectF strippedRect = qwtStripRect(paintRect, area2,
|
||||
xMap, yMap, xInterval, yInterval);
|
||||
const QSize sz = strippedRect.toRect().size();
|
||||
|
||||
const int w = image.width();
|
||||
const int h = image.height();
|
||||
|
||||
const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized();
|
||||
const double pw = ( r.width() - 1) / w;
|
||||
const double ph = ( r.height() - 1) / h;
|
||||
|
||||
double px0, py0;
|
||||
if ( !xMap.isInverting() )
|
||||
{
|
||||
px0 = xMap.transform( area2.left() );
|
||||
px0 = qRound( px0 );
|
||||
px0 = px0 - xMap.transform( area.left() );
|
||||
}
|
||||
else
|
||||
{
|
||||
px0 = xMap.transform( area2.right() );
|
||||
px0 = qRound( px0 );
|
||||
px0 -= xMap.transform( area.right() );
|
||||
|
||||
px0 -= 1.0;
|
||||
}
|
||||
px0 += strippedRect.left() - paintRect.left();
|
||||
|
||||
if ( !yMap.isInverting() )
|
||||
{
|
||||
py0 = yMap.transform( area2.top() );
|
||||
py0 = qRound( py0 );
|
||||
py0 -= yMap.transform( area.top() );
|
||||
}
|
||||
else
|
||||
{
|
||||
py0 = yMap.transform( area2.bottom() );
|
||||
py0 = qRound( py0 );
|
||||
py0 -= yMap.transform( area.bottom() );
|
||||
|
||||
py0 -= 1.0;
|
||||
}
|
||||
py0 += strippedRect.top() - paintRect.top();
|
||||
|
||||
QImage expanded(sz, image.format());
|
||||
|
||||
switch( image.depth() )
|
||||
{
|
||||
case 32:
|
||||
{
|
||||
for ( int y1 = 0; y1 < h; y1++ )
|
||||
{
|
||||
int yy1;
|
||||
if ( y1 == 0 )
|
||||
{
|
||||
yy1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yy1 = qRound( y1 * ph - py0 );
|
||||
if ( yy1 < 0 )
|
||||
yy1 = 0;
|
||||
}
|
||||
|
||||
int yy2;
|
||||
if ( y1 == h - 1 )
|
||||
{
|
||||
yy2 = sz.height();
|
||||
}
|
||||
else
|
||||
{
|
||||
yy2 = qRound( ( y1 + 1 ) * ph - py0 );
|
||||
if ( yy2 > sz.height() )
|
||||
yy2 = sz.height();
|
||||
}
|
||||
|
||||
const quint32 *line1 =
|
||||
reinterpret_cast<const quint32 *>( image.scanLine( y1 ) );
|
||||
|
||||
for ( int x1 = 0; x1 < w; x1++ )
|
||||
{
|
||||
int xx1;
|
||||
if ( x1 == 0 )
|
||||
{
|
||||
xx1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
xx1 = qRound( x1 * pw - px0 );
|
||||
if ( xx1 < 0 )
|
||||
xx1 = 0;
|
||||
}
|
||||
|
||||
int xx2;
|
||||
if ( x1 == w - 1 )
|
||||
{
|
||||
xx2 = sz.width();
|
||||
}
|
||||
else
|
||||
{
|
||||
xx2 = qRound( ( x1 + 1 ) * pw - px0 );
|
||||
if ( xx2 > sz.width() )
|
||||
xx2 = sz.width();
|
||||
}
|
||||
|
||||
const quint32 rgb( line1[x1] );
|
||||
for ( int y2 = yy1; y2 < yy2; y2++ )
|
||||
{
|
||||
quint32 *line2 = reinterpret_cast<quint32 *>(
|
||||
expanded.scanLine( y2 ) );
|
||||
|
||||
for ( int x2 = xx1; x2 < xx2; x2++ )
|
||||
line2[x2] = rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
for ( int y1 = 0; y1 < h; y1++ )
|
||||
{
|
||||
int yy1;
|
||||
if ( y1 == 0 )
|
||||
{
|
||||
yy1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yy1 = qRound( y1 * ph - py0 );
|
||||
if ( yy1 < 0 )
|
||||
yy1 = 0;
|
||||
}
|
||||
|
||||
int yy2;
|
||||
if ( y1 == h - 1 )
|
||||
{
|
||||
yy2 = sz.height();
|
||||
}
|
||||
else
|
||||
{
|
||||
yy2 = qRound( ( y1 + 1 ) * ph - py0 );
|
||||
if ( yy2 > sz.height() )
|
||||
yy2 = sz.height();
|
||||
}
|
||||
|
||||
const uchar *line1 = image.scanLine( y1 );
|
||||
|
||||
for ( int x1 = 0; x1 < w; x1++ )
|
||||
{
|
||||
int xx1;
|
||||
if ( x1 == 0 )
|
||||
{
|
||||
xx1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
xx1 = qRound( x1 * pw - px0 );
|
||||
if ( xx1 < 0 )
|
||||
xx1 = 0;
|
||||
}
|
||||
|
||||
int xx2;
|
||||
if ( x1 == w - 1 )
|
||||
{
|
||||
xx2 = sz.width();
|
||||
}
|
||||
else
|
||||
{
|
||||
xx2 = qRound( ( x1 + 1 ) * pw - px0 );
|
||||
if ( xx2 > sz.width() )
|
||||
xx2 = sz.width();
|
||||
}
|
||||
|
||||
for ( int y2 = yy1; y2 < yy2; y2++ )
|
||||
{
|
||||
uchar *line2 = expanded.scanLine( y2 );
|
||||
memset( line2 + xx1, line1[x1], xx2 - xx1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
expanded = image;
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
static QRectF qwtExpandToPixels(const QRectF &rect, const QRectF &pixelRect)
|
||||
{
|
||||
const double pw = pixelRect.width();
|
||||
const double ph = pixelRect.height();
|
||||
|
||||
const double dx1 = pixelRect.left() - rect.left();
|
||||
const double dx2 = pixelRect.right() - rect.right();
|
||||
const double dy1 = pixelRect.top() - rect.top();
|
||||
const double dy2 = pixelRect.bottom() - rect.bottom();
|
||||
|
||||
QRectF r;
|
||||
r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw );
|
||||
r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph );
|
||||
r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw );
|
||||
r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void qwtTransformMaps( const QTransform &tr,
|
||||
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
||||
QwtScaleMap &xxMap, QwtScaleMap &yyMap )
|
||||
{
|
||||
const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) );
|
||||
const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) );
|
||||
|
||||
xxMap = xMap;
|
||||
xxMap.setPaintInterval( p1.x(), p2.x() );
|
||||
|
||||
yyMap = yMap;
|
||||
yyMap.setPaintInterval( p1.y(), p2.y() );
|
||||
}
|
||||
|
||||
static void qwtAdjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap,
|
||||
const QRectF &area, const QRectF &paintRect)
|
||||
{
|
||||
double sx1 = area.left();
|
||||
double sx2 = area.right();
|
||||
if ( xMap.isInverting() )
|
||||
qSwap(sx1, sx2);
|
||||
|
||||
double sy1 = area.top();
|
||||
double sy2 = area.bottom();
|
||||
|
||||
if ( yMap.isInverting() )
|
||||
qSwap(sy1, sy2);
|
||||
|
||||
xMap.setPaintInterval(paintRect.left(), paintRect.right());
|
||||
xMap.setScaleInterval(sx1, sx2);
|
||||
|
||||
yMap.setPaintInterval(paintRect.top(), paintRect.bottom());
|
||||
yMap.setScaleInterval(sy1, sy2);
|
||||
}
|
||||
|
||||
static bool qwtUseCache( QwtPlotRasterItem::CachePolicy policy,
|
||||
const QPainter *painter )
|
||||
{
|
||||
bool doCache = false;
|
||||
|
||||
if ( policy == QwtPlotRasterItem::PaintCache )
|
||||
{
|
||||
// Caching doesn't make sense, when the item is
|
||||
// not painted to screen
|
||||
|
||||
switch ( painter->paintEngine()->type() )
|
||||
{
|
||||
case QPaintEngine::SVG:
|
||||
case QPaintEngine::Pdf:
|
||||
case QPaintEngine::PostScript:
|
||||
case QPaintEngine::MacPrinter:
|
||||
case QPaintEngine::Picture:
|
||||
break;
|
||||
default:;
|
||||
doCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
return doCache;
|
||||
}
|
||||
|
||||
static void qwtToRgba( const QImage* from, QImage* to,
|
||||
const QRect& tile, int alpha )
|
||||
{
|
||||
const QRgb mask1 = qRgba( 0, 0, 0, alpha );
|
||||
const QRgb mask2 = qRgba( 255, 255, 255, 0 );
|
||||
const QRgb mask3 = qRgba( 0, 0, 0, 255 );
|
||||
|
||||
const int y0 = tile.top();
|
||||
const int y1 = tile.bottom();
|
||||
const int x0 = tile.left();
|
||||
const int x1 = tile.right();
|
||||
|
||||
if ( from->depth() == 8 )
|
||||
{
|
||||
for ( int y = y0; y <= y1; y++ )
|
||||
{
|
||||
QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
|
||||
const unsigned char *line = from->scanLine( y );
|
||||
|
||||
for ( int x = x0; x <= x1; x++ )
|
||||
*alphaLine++ = ( from->color( *line++ ) & mask2 ) | mask1;
|
||||
}
|
||||
}
|
||||
else if ( from->depth() == 32 )
|
||||
{
|
||||
for ( int y = y0; y <= y1; y++ )
|
||||
{
|
||||
QRgb *alphaLine = reinterpret_cast<QRgb *>( to->scanLine( y ) );
|
||||
const QRgb *line = reinterpret_cast<const QRgb *>( from->scanLine( y ) );
|
||||
|
||||
for ( int x = x0; x <= x1; x++ )
|
||||
{
|
||||
const QRgb rgb = *line++;
|
||||
if ( rgb & mask3 ) // alpha != 0
|
||||
*alphaLine++ = ( rgb & mask2 ) | mask1;
|
||||
else
|
||||
*alphaLine++ = rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Constructor
|
||||
QwtPlotRasterItem::QwtPlotRasterItem( const QString& title ):
|
||||
QwtPlotItem( QwtText( title ) )
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//! Constructor
|
||||
QwtPlotRasterItem::QwtPlotRasterItem( const QwtText& title ):
|
||||
QwtPlotItem( title )
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
QwtPlotRasterItem::~QwtPlotRasterItem()
|
||||
{
|
||||
delete d_data;
|
||||
}
|
||||
|
||||
void QwtPlotRasterItem::init()
|
||||
{
|
||||
d_data = new PrivateData();
|
||||
|
||||
setItemAttribute( QwtPlotItem::AutoScale, true );
|
||||
setItemAttribute( QwtPlotItem::Legend, false );
|
||||
|
||||
setZ( 8.0 );
|
||||
}
|
||||
|
||||
/*!
|
||||
Specify an attribute how to draw the raster item
|
||||
|
||||
\param attribute Paint attribute
|
||||
\param on On/Off
|
||||
/sa PaintAttribute, testPaintAttribute()
|
||||
*/
|
||||
void QwtPlotRasterItem::setPaintAttribute( PaintAttribute attribute, bool on )
|
||||
{
|
||||
if ( on )
|
||||
d_data->paintAttributes |= attribute;
|
||||
else
|
||||
d_data->paintAttributes &= ~attribute;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return True, when attribute is enabled
|
||||
\sa PaintAttribute, setPaintAttribute()
|
||||
*/
|
||||
bool QwtPlotRasterItem::testPaintAttribute( PaintAttribute attribute ) const
|
||||
{
|
||||
return ( d_data->paintAttributes & attribute );
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Set an alpha value for the raster data
|
||||
|
||||
Often a plot has several types of raster data organized in layers.
|
||||
( f.e a geographical map, with weather statistics ).
|
||||
Using setAlpha() raster items can be stacked easily.
|
||||
|
||||
The alpha value is a value [0, 255] to
|
||||
control the transparency of the image. 0 represents a fully
|
||||
transparent color, while 255 represents a fully opaque color.
|
||||
|
||||
\param alpha Alpha value
|
||||
|
||||
- alpha >= 0\n
|
||||
All alpha values of the pixels returned by renderImage() will be set to
|
||||
alpha, beside those with an alpha value of 0 (invalid pixels).
|
||||
- alpha < 0
|
||||
The alpha values returned by renderImage() are not changed.
|
||||
|
||||
The default alpha value is -1.
|
||||
|
||||
\sa alpha()
|
||||
*/
|
||||
void QwtPlotRasterItem::setAlpha( int alpha )
|
||||
{
|
||||
if ( alpha < 0 )
|
||||
alpha = -1;
|
||||
|
||||
if ( alpha > 255 )
|
||||
alpha = 255;
|
||||
|
||||
if ( alpha != d_data->alpha )
|
||||
{
|
||||
d_data->alpha = alpha;
|
||||
|
||||
itemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\return Alpha value of the raster item
|
||||
\sa setAlpha()
|
||||
*/
|
||||
int QwtPlotRasterItem::alpha() const
|
||||
{
|
||||
return d_data->alpha;
|
||||
}
|
||||
|
||||
/*!
|
||||
Change the cache policy
|
||||
|
||||
The default policy is NoCache
|
||||
|
||||
\param policy Cache policy
|
||||
\sa CachePolicy, cachePolicy()
|
||||
*/
|
||||
void QwtPlotRasterItem::setCachePolicy(
|
||||
QwtPlotRasterItem::CachePolicy policy )
|
||||
{
|
||||
if ( d_data->cache.policy != policy )
|
||||
{
|
||||
d_data->cache.policy = policy;
|
||||
|
||||
invalidateCache();
|
||||
itemChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\return Cache policy
|
||||
\sa CachePolicy, setCachePolicy()
|
||||
*/
|
||||
QwtPlotRasterItem::CachePolicy QwtPlotRasterItem::cachePolicy() const
|
||||
{
|
||||
return d_data->cache.policy;
|
||||
}
|
||||
|
||||
/*!
|
||||
Invalidate the paint cache
|
||||
\sa setCachePolicy()
|
||||
*/
|
||||
void QwtPlotRasterItem::invalidateCache()
|
||||
{
|
||||
d_data->cache.image = QImage();
|
||||
d_data->cache.area = QRect();
|
||||
d_data->cache.size = QSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Pixel hint
|
||||
|
||||
The geometry of a pixel is used to calculated the resolution and
|
||||
alignment of the rendered image.
|
||||
|
||||
Width and height of the hint need to be the horizontal
|
||||
and vertical distances between 2 neighbored points.
|
||||
The center of the hint has to be the position of any point
|
||||
( it doesn't matter which one ).
|
||||
|
||||
Limiting the resolution of the image might significantly improve
|
||||
the performance and heavily reduce the amount of memory when rendering
|
||||
a QImage from the raster data.
|
||||
|
||||
The default implementation returns an empty rectangle (QRectF()),
|
||||
meaning, that the image will be rendered in target device ( f.e screen )
|
||||
resolution.
|
||||
|
||||
\param area In most implementations the resolution of the data doesn't
|
||||
depend on the requested area.
|
||||
|
||||
\return Bounding rectangle of a pixel
|
||||
|
||||
\sa render(), renderImage()
|
||||
*/
|
||||
QRectF QwtPlotRasterItem::pixelHint( const QRectF &area ) const
|
||||
{
|
||||
Q_UNUSED( area );
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Draw the raster data
|
||||
\param painter Painter
|
||||
\param xMap X-Scale Map
|
||||
\param yMap Y-Scale Map
|
||||
\param canvasRect Contents rectangle of the plot canvas
|
||||
*/
|
||||
void QwtPlotRasterItem::draw( QPainter *painter,
|
||||
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
||||
const QRectF &canvasRect ) const
|
||||
{
|
||||
if ( canvasRect.isEmpty() || d_data->alpha == 0 )
|
||||
return;
|
||||
|
||||
const bool doCache = qwtUseCache( d_data->cache.policy, painter );
|
||||
|
||||
const QwtInterval xInterval = interval( Qt::XAxis );
|
||||
const QwtInterval yInterval = interval( Qt::YAxis );
|
||||
|
||||
/*
|
||||
Scaling an image always results in a loss of
|
||||
precision/quality. So we always render the image in
|
||||
paint device resolution.
|
||||
*/
|
||||
|
||||
QwtScaleMap xxMap, yyMap;
|
||||
qwtTransformMaps( painter->transform(), xMap, yMap, xxMap, yyMap );
|
||||
|
||||
QRectF paintRect = painter->transform().mapRect( canvasRect );
|
||||
QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect );
|
||||
|
||||
const QRectF br = boundingRect();
|
||||
if ( br.isValid() && !br.contains( area ) )
|
||||
{
|
||||
area &= br;
|
||||
if ( !area.isValid() )
|
||||
return;
|
||||
|
||||
paintRect = QwtScaleMap::transform( xxMap, yyMap, area );
|
||||
}
|
||||
|
||||
QRectF imageRect;
|
||||
QImage image;
|
||||
|
||||
QRectF pixelRect = pixelHint(area);
|
||||
if ( !pixelRect.isEmpty() )
|
||||
{
|
||||
// one pixel of the target device in plot coordinates
|
||||
const double dx = qAbs( xxMap.invTransform( 1 ) - xxMap.invTransform( 0 ) );
|
||||
const double dy = qAbs( yyMap.invTransform( 1 ) - yyMap.invTransform( 0 ) );
|
||||
|
||||
if ( dx > pixelRect.width() && dy > pixelRect.height() )
|
||||
{
|
||||
/*
|
||||
When the resolution of the data pixels is higher than
|
||||
the resolution of the target device we render in
|
||||
target device resolution.
|
||||
*/
|
||||
pixelRect = QRectF();
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
If only one dimension is of the data pixel is higher
|
||||
we expand the pixel rect to the resolution of the target device.
|
||||
*/
|
||||
|
||||
if ( dx > pixelRect.width() )
|
||||
pixelRect.setWidth( dx );
|
||||
|
||||
if ( dy > pixelRect.height() )
|
||||
pixelRect.setHeight( dy );
|
||||
}
|
||||
}
|
||||
|
||||
if ( pixelRect.isEmpty() )
|
||||
{
|
||||
if ( QwtPainter::roundingAlignment( painter ) )
|
||||
{
|
||||
// we want to have maps, where the boundaries of
|
||||
// the aligned paint rectangle exactly match the area
|
||||
|
||||
paintRect = qwtAlignRect(paintRect);
|
||||
qwtAdjustMaps(xxMap, yyMap, area, paintRect);
|
||||
}
|
||||
|
||||
// When we have no information about position and size of
|
||||
// data pixels we render in resolution of the paint device.
|
||||
|
||||
image = compose(xxMap, yyMap,
|
||||
area, paintRect, paintRect.size().toSize(), doCache);
|
||||
if ( image.isNull() )
|
||||
return;
|
||||
|
||||
// Remove pixels at the boundaries, when explicitly
|
||||
// excluded in the intervals
|
||||
|
||||
imageRect = qwtStripRect(paintRect, area,
|
||||
xxMap, yyMap, xInterval, yInterval);
|
||||
|
||||
if ( imageRect != paintRect )
|
||||
{
|
||||
const QRect r(
|
||||
qRound( imageRect.x() - paintRect.x()),
|
||||
qRound( imageRect.y() - paintRect.y() ),
|
||||
qRound( imageRect.width() ),
|
||||
qRound( imageRect.height() ) );
|
||||
|
||||
image = image.copy(r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( QwtPainter::roundingAlignment( painter ) )
|
||||
paintRect = qwtAlignRect(paintRect);
|
||||
|
||||
// align the area to the data pixels
|
||||
QRectF imageArea = qwtExpandToPixels(area, pixelRect);
|
||||
|
||||
if ( imageArea.right() == xInterval.maxValue() &&
|
||||
!( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
|
||||
{
|
||||
imageArea.adjust(0, 0, pixelRect.width(), 0);
|
||||
}
|
||||
if ( imageArea.bottom() == yInterval.maxValue() &&
|
||||
!( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) )
|
||||
{
|
||||
imageArea.adjust(0, 0, 0, pixelRect.height() );
|
||||
}
|
||||
|
||||
QSize imageSize;
|
||||
imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) );
|
||||
imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) );
|
||||
|
||||
image = compose(xxMap, yyMap,
|
||||
imageArea, paintRect, imageSize, doCache );
|
||||
|
||||
if ( image.isNull() )
|
||||
return;
|
||||
|
||||
imageRect = qwtStripRect(paintRect, area,
|
||||
xxMap, yyMap, xInterval, yInterval);
|
||||
|
||||
if ( ( image.width() > 1 || image.height() > 1 ) &&
|
||||
testPaintAttribute( PaintInDeviceResolution ) )
|
||||
{
|
||||
// Because of rounding errors the pixels
|
||||
// need to be expanded manually to rectangles of
|
||||
// different sizes
|
||||
|
||||
image = qwtExpandImage(image, xxMap, yyMap,
|
||||
imageArea, area, paintRect, xInterval, yInterval );
|
||||
}
|
||||
}
|
||||
|
||||
painter->save();
|
||||
painter->setWorldTransform( QTransform() );
|
||||
|
||||
QwtPainter::drawImage( painter, imageRect, image );
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
/*!
|
||||
\return Bounding interval for an axis
|
||||
|
||||
This method is intended to be reimplemented by derived classes.
|
||||
The default implementation returns an invalid interval.
|
||||
|
||||
\param axis X, Y, or Z axis
|
||||
*/
|
||||
QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const
|
||||
{
|
||||
Q_UNUSED( axis );
|
||||
return QwtInterval();
|
||||
}
|
||||
|
||||
/*!
|
||||
\return Bounding rectangle of the data
|
||||
\sa QwtPlotRasterItem::interval()
|
||||
*/
|
||||
QRectF QwtPlotRasterItem::boundingRect() const
|
||||
{
|
||||
const QwtInterval intervalX = interval( Qt::XAxis );
|
||||
const QwtInterval intervalY = interval( Qt::YAxis );
|
||||
|
||||
if ( !intervalX.isValid() && !intervalY.isValid() )
|
||||
return QRectF(); // no bounding rect
|
||||
|
||||
QRectF r;
|
||||
|
||||
if ( intervalX.isValid() )
|
||||
{
|
||||
r.setLeft( intervalX.minValue() );
|
||||
r.setRight( intervalX.maxValue() );
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setLeft(-0.5 * FLT_MAX);
|
||||
r.setWidth(FLT_MAX);
|
||||
}
|
||||
|
||||
if ( intervalY.isValid() )
|
||||
{
|
||||
r.setTop( intervalY.minValue() );
|
||||
r.setBottom( intervalY.maxValue() );
|
||||
}
|
||||
else
|
||||
{
|
||||
r.setTop(-0.5 * FLT_MAX);
|
||||
r.setHeight(FLT_MAX);
|
||||
}
|
||||
|
||||
return r.normalized();
|
||||
}
|
||||
|
||||
QImage QwtPlotRasterItem::compose(
|
||||
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
||||
const QRectF &imageArea, const QRectF &paintRect,
|
||||
const QSize &imageSize, bool doCache) const
|
||||
{
|
||||
QImage image;
|
||||
if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() )
|
||||
return image;
|
||||
|
||||
if ( doCache )
|
||||
{
|
||||
if ( !d_data->cache.image.isNull()
|
||||
&& d_data->cache.area == imageArea
|
||||
&& d_data->cache.size == paintRect.size() )
|
||||
{
|
||||
image = d_data->cache.image;
|
||||
}
|
||||
}
|
||||
|
||||
if ( image.isNull() )
|
||||
{
|
||||
double dx = 0.0;
|
||||
if ( paintRect.toRect().width() > imageSize.width() )
|
||||
dx = imageArea.width() / imageSize.width();
|
||||
|
||||
const QwtScaleMap xxMap =
|
||||
imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx);
|
||||
|
||||
double dy = 0.0;
|
||||
if ( paintRect.toRect().height() > imageSize.height() )
|
||||
dy = imageArea.height() / imageSize.height();
|
||||
|
||||
const QwtScaleMap yyMap =
|
||||
imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy);
|
||||
|
||||
image = renderImage( xxMap, yyMap, imageArea, imageSize );
|
||||
|
||||
if ( doCache )
|
||||
{
|
||||
d_data->cache.area = imageArea;
|
||||
d_data->cache.size = paintRect.size();
|
||||
d_data->cache.image = image;
|
||||
}
|
||||
}
|
||||
|
||||
if ( d_data->alpha >= 0 && d_data->alpha < 255 )
|
||||
{
|
||||
QImage alphaImage( image.size(), QImage::Format_ARGB32 );
|
||||
|
||||
#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE)
|
||||
uint numThreads = renderThreadCount();
|
||||
|
||||
if ( numThreads <= 0 )
|
||||
numThreads = QThread::idealThreadCount();
|
||||
|
||||
if ( numThreads <= 0 )
|
||||
numThreads = 1;
|
||||
|
||||
const int numRows = image.height() / numThreads;
|
||||
|
||||
QList< QFuture<void> > futures;
|
||||
for ( uint i = 0; i < numThreads; i++ )
|
||||
{
|
||||
QRect tile( 0, i * numRows, image.width(), numRows );
|
||||
if ( i == numThreads - 1 )
|
||||
{
|
||||
tile.setHeight( image.height() - i * numRows );
|
||||
qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
|
||||
}
|
||||
else
|
||||
{
|
||||
futures += QtConcurrent::run(
|
||||
&qwtToRgba, &image, &alphaImage, tile, d_data->alpha );
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < futures.size(); i++ )
|
||||
futures[i].waitForFinished();
|
||||
#else
|
||||
const QRect tile( 0, 0, image.width(), image.height() );
|
||||
qwtToRgba( &image, &alphaImage, tile, d_data->alpha );
|
||||
#endif
|
||||
image = alphaImage;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Calculate a scale map for painting to an image
|
||||
|
||||
\param orientation Orientation, Qt::Horizontal means a X axis
|
||||
\param map Scale map for rendering the plot item
|
||||
\param area Area to be painted on the image
|
||||
\param imageSize Image size
|
||||
\param pixelSize Width/Height of a data pixel
|
||||
|
||||
\return Calculated scale map
|
||||
*/
|
||||
QwtScaleMap QwtPlotRasterItem::imageMap(
|
||||
Qt::Orientation orientation,
|
||||
const QwtScaleMap &map, const QRectF &area,
|
||||
const QSize &imageSize, double pixelSize) const
|
||||
{
|
||||
double p1, p2, s1, s2;
|
||||
|
||||
if ( orientation == Qt::Horizontal )
|
||||
{
|
||||
p1 = 0.0;
|
||||
p2 = imageSize.width();
|
||||
s1 = area.left();
|
||||
s2 = area.right();
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = 0.0;
|
||||
p2 = imageSize.height();
|
||||
s1 = area.top();
|
||||
s2 = area.bottom();
|
||||
}
|
||||
|
||||
if ( pixelSize > 0.0 )
|
||||
{
|
||||
double off = 0.5 * pixelSize;
|
||||
if ( map.isInverting() )
|
||||
off = -off;
|
||||
|
||||
s1 += off;
|
||||
s2 += off;
|
||||
}
|
||||
else
|
||||
{
|
||||
p2--;
|
||||
}
|
||||
|
||||
if ( map.isInverting() && ( s1 < s2 ) )
|
||||
qSwap( s1, s2 );
|
||||
|
||||
QwtScaleMap newMap = map;
|
||||
newMap.setPaintInterval( p1, p2 );
|
||||
newMap.setScaleInterval( s1, s2 );
|
||||
|
||||
return newMap;
|
||||
}
|
||||
Reference in New Issue
Block a user