Threading - WIP

This commit is contained in:
Martin Dobias 2013-09-30 05:26:23 +02:00
parent 247e6c762d
commit b04f81a12b
18 changed files with 1452 additions and 274 deletions

View File

@ -33,8 +33,6 @@ class QgsMapCanvasMap : QGraphicsRectItem
void enableAntiAliasing( bool flag );
void useImageToRender( bool flag );
//! renders map using QgsMapRenderer to mPixmap
void render();

View File

@ -462,6 +462,11 @@ ELSE (ANDROID)
ADD_EXECUTABLE(${QGIS_APP_NAME} MACOSX_BUNDLE WIN32 main.cpp ${QGIS_APP_SRCS} ${QGIS_APP_MOC_SRCS} ${IMAGE_RCC_SRCS} ${TEST_RCC_SRCS})
ENDIF (ANDROID)
QT4_WRAP_CPP(tst_moc_srcs maprenderertest.h)
ADD_EXECUTABLE(maprenderertest maprenderertest.cpp ${tst_moc_srcs})
TARGET_LINK_LIBRARIES(maprenderertest qgis_core qgis_gui)
IF (WIN32)
IF (MSVC)
ADD_DEFINITIONS("-DAPP_EXPORT=${DLLEXPORT}")

View File

@ -0,0 +1,61 @@
#include "maprenderertest.h"
#include <QApplication>
#include <QPainter>
#include <QPaintEvent>
#include "qgsapplication.h"
#include "qgsvectorlayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsmapcanvas.h"
#include "qgsmaptoolpan.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QgsApplication::setPrefixPath("/home/martin/qgis/git-master/creator/output", true);
QgsApplication::initQgis();
QString uri = "dbname='/data/gis/praha.osm.db' table=\"praha_polygons\" (geometry) sql=";
QString uri2 = "dbname='/data/gis/praha.osm.db' table=\"praha_polylines\" (geometry) sql=";
//QString uri = "/data/gis/cr-shp-wgs84/plochy/kraje_pseudo.shp";
QgsVectorLayer* layer = new QgsVectorLayer(uri, "praha", "spatialite");
if (!layer->isValid())
{
qDebug("invalid layer");
return 1;
}
QgsMapLayerRegistry::instance()->addMapLayer(layer);
QgsVectorLayer* layer2 = new QgsVectorLayer(uri2, "praha", "spatialite");
if (!layer2->isValid())
{
qDebug("invalid layer");
return 1;
}
QgsMapLayerRegistry::instance()->addMapLayer(layer2);
// open a window and do the rendering!
/*TestWidget l(layer);
l.resize(360,360);
l.show();*/
QgsMapCanvas canvas;
canvas.setCanvasColor(Qt::white);
canvas.setExtent(layer->extent());
QList<QgsMapCanvasLayer> layers;
layers.append(QgsMapCanvasLayer(layer2));
layers.append(QgsMapCanvasLayer(layer));
canvas.setLayerSet(layers);
QgsMapTool* pan = new QgsMapToolPan(&canvas);
canvas.setMapTool(pan);
canvas.show();
return app.exec();
}

102
src/app/maprenderertest.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef MAPRENDERERTEST_H
#define MAPRENDERERTEST_H
#include <QLabel>
#include <QPainter>
#include <QTimer>
#include <QMouseEvent>
#include "qgsmaprendererv2.h"
#include "qgsmaplayer.h"
class TestWidget : public QLabel
{
Q_OBJECT
public:
TestWidget(QgsMapLayer* layer)
{
//p = QPixmap(200,200);
//p.fill(Qt::red);
i = QImage(size(), QImage::Format_ARGB32_Premultiplied);
i.fill(Qt::gray);
// init renderer
rend.setLayers(QStringList(layer->id()));
rend.setExtent(layer->extent());
rend.setOutputSize(i.size());
rend.setOutputDpi(120);
rend.updateDerived();
if (rend.hasValidSettings())
qDebug("map renderer settings valid");
connect(&rend, SIGNAL(finished()), SLOT(f()));
setPixmap(QPixmap::fromImage(i));
connect(&timer, SIGNAL(timeout()), SLOT(onMapUpdateTimeout()));
timer.setInterval(100);
}
void mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::RightButton)
{
qDebug("cancelling!");
rend.cancel();
}
else
{
qDebug("starting!");
if (rend.isRendering())
{
qDebug("need to cancel first!");
rend.cancel();
// TODO: need to ensure that finished slot has been called
}
i.fill(Qt::gray);
painter = new QPainter(&i);
rend.startWithCustomPainter(painter);
timer.start();
}
}
protected slots:
void f()
{
qDebug("finished!");
painter->end();
delete painter;
timer.stop();
//update();
setPixmap(QPixmap::fromImage(i));
}
void onMapUpdateTimeout()
{
qDebug("update timer!");
setPixmap(QPixmap::fromImage(i));
}
protected:
//QPixmap p;
QImage i;
QPainter* painter;
QgsMapRendererV2 rend;
QTimer timer;
};
#endif // MAPRENDERERTEST_H

View File

@ -3,6 +3,9 @@
SET(QGIS_CORE_SRCS
qgsmaprendererv2.cpp
qgsmaprendererjob.cpp
gps/qgsgpsconnection.cpp
gps/qgsgpsconnectionregistry.cpp
gps/qgsnmeaconnection.cpp
@ -293,6 +296,10 @@ ADD_FLEX_FILES(QGIS_CORE_SRCS qgsexpressionlexer.ll)
ADD_BISON_FILES(QGIS_CORE_SRCS qgsexpressionparser.yy)
SET(QGIS_CORE_MOC_HDRS
qgsmaprendererv2.h
qgsmaprendererjob.h
qgsapplication.h
qgsbrowsermodel.h
qgscontexthelp.h

View File

@ -73,16 +73,11 @@ QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
mMinScale = 0;
mMaxScale = 100000000;
mScaleBasedVisibility = false;
mpCacheImage = 0;
}
QgsMapLayer::~QgsMapLayer()
{
delete mCRS;
if ( mpCacheImage )
{
delete mpCacheImage;
}
}
QgsMapLayer::LayerType QgsMapLayer::type() const
@ -1334,20 +1329,6 @@ void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & do
layerNode.appendChild( propsElement );
}
void QgsMapLayer::setCacheImage( QImage * thepImage )
{
QgsDebugMsg( "cache Image set!" );
if ( mpCacheImage == thepImage )
return;
if ( mpCacheImage )
{
onCacheImageDelete();
delete mpCacheImage;
}
mpCacheImage = thepImage;
}
bool QgsMapLayer::isEditable() const
{
return false;

View File

@ -360,17 +360,11 @@ class CORE_EXPORT QgsMapLayer : public QObject
/** Return pointer to layer's undo stack */
QUndoStack* undoStack();
/** Get the QImage used for caching render operations
* @note This method was added in QGIS 1.4 **/
QImage *cacheImage() { return mpCacheImage; }
/** Set the QImage used for caching render operations
* @note This method was added in QGIS 1.4 **/
void setCacheImage( QImage * thepImage );
/**
* @brief Is called when the cache image is being deleted. Overwrite and use to clean up.
* @note added in 2.0
*/
/** \note Deprecated from 2.1 - returns NULL */
QImage *cacheImage() { return 0; }
/** \note Deprecated from 2.1 - does nothing */
void setCacheImage( QImage * ) {}
/** @note Deprecated from 2.1 - does nothing */
virtual void onCacheImageDelete() {}
public slots:
@ -421,8 +415,7 @@ class CORE_EXPORT QgsMapLayer : public QObject
*/
void repaintRequested();
/**The layer emits this signal when a screen update is requested.
This signal should be connected with the slot QgsMapCanvas::updateMap()*/
//! \note Deprecated in 2.1 and not emitted anymore
void screenUpdateRequested();
/** This is used to send a request that any mapcanvas using this layer update its extents */
@ -544,10 +537,6 @@ class CORE_EXPORT QgsMapLayer : public QObject
QMap<QString, QVariant> mCustomProperties;
/**QImage for caching of rendering operations
* @note This property was added in QGIS 1.4 **/
QImage * mpCacheImage;
};
#endif

View File

@ -0,0 +1,556 @@
#include "qgsmaprendererjob.h"
#include <QPainter>
#include "qgslogger.h"
#include "qgsrendercontext.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"
QgsMapRendererSequentialJob::QgsMapRendererSequentialJob(const QgsMapRendererSettings& settings)
: QgsMapRendererJob(SequentialJob, settings)
, mInternalJob(0)
{
}
void QgsMapRendererSequentialJob::start()
{
mImage.fill(Qt::blue);
// 1. create an image where we will output all rendering
mPainter = new QPainter(&mImage);
// 2. start rendering the layers in a thread (using custom painter)
mInternalJob = new QgsMapRendererCustomPainterJob(mSettings, mPainter);
mInternalJob->start();
connect(mInternalJob, SIGNAL(finished()), SLOT(internalFinished()));
}
void QgsMapRendererSequentialJob::cancel()
{
mInternalJob->cancel();
}
QImage QgsMapRendererSequentialJob::renderedImage()
{
return mImage;
}
void QgsMapRendererSequentialJob::internalFinished()
{
mPainter->end();
delete mPainter;
mPainter = 0;
delete mInternalJob;
mInternalJob = 0;
}
////////
QgsMapRendererCustomPainterJob::QgsMapRendererCustomPainterJob(const QgsMapRendererSettings& settings, QPainter* painter)
: QgsMapRendererJob(CustomPainterJob, settings)
, mPainter(painter)
{
connect(&mFutureWatcher, SIGNAL(finished()), SLOT(futureFinished()));
}
void QgsMapRendererCustomPainterJob::start()
{
qDebug("run!");
mFuture = QtConcurrent::run(staticRender, this);
mFutureWatcher.setFuture(mFuture);
}
#include <QApplication>
void QgsMapRendererCustomPainterJob::cancel()
{
mRenderContext.setRenderingStopped(true);
mFutureWatcher.waitForFinished();
qApp->processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers ); // TODO: necessary?
}
QImage QgsMapRendererCustomPainterJob::renderedImage()
{
return QImage();
}
void QgsMapRendererCustomPainterJob::futureFinished()
{
qDebug("futureFinished");
emit finished();
}
void QgsMapRendererCustomPainterJob::staticRender(QgsMapRendererCustomPainterJob* self)
{
qDebug("staticRender");
self->startRender();
}
void QgsMapRendererCustomPainterJob::startRender()
{
qDebug("startRender");
/*
for (int i = 0; i < 50; ++i)
{
if (mStopped)
return;
mPainter->drawLine(rand() % 360, rand() % 360, rand() % 360, rand() % 360);
//QThread::msleep(100);
int x = 0;
for (int i = 0; i < 1000; i++)
{
for (int j = 0; j < 10000; j++)
x *= x+5;
}
qDebug("drawn line");
}*/
#ifdef QGISDEBUG
QgsDebugMsg( "Starting to render layer stack." );
QTime renderTime;
renderTime.start();
#endif
mRenderContext.setMapToPixel( QgsMapToPixel( mSettings.mapUnitsPerPixel, mSettings.size.height(), mSettings.visibleExtent.yMinimum(), mSettings.visibleExtent.xMinimum() ) );
mRenderContext.setExtent( mSettings.visibleExtent );
mRenderContext.setDrawEditingInformation( false );
mRenderContext.setPainter( mPainter );
mRenderContext.setCoordinateTransform( 0 );
//this flag is only for stopping during the current rendering progress,
//so must be false at every new render operation
mRenderContext.setRenderingStopped( false );
// set selection color
/* TODO QgsProject* prj = QgsProject::instance();
int myRed = prj->readNumEntry( "Gui", "/SelectionColorRedPart", 255 );
int myGreen = prj->readNumEntry( "Gui", "/SelectionColorGreenPart", 255 );
int myBlue = prj->readNumEntry( "Gui", "/SelectionColorBluePart", 0 );
int myAlpha = prj->readNumEntry( "Gui", "/SelectionColorAlphaPart", 255 );*/
mRenderContext.setSelectionColor( Qt::red ); // TODO QColor( myRed, myGreen, myBlue, myAlpha ) );
//calculate scale factor
//use the specified dpi and not those from the paint device
//because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene)
/* TODO double sceneDpi = mScaleCalculator->dpi();
double scaleFactor = 1.0;
if ( mOutputUnits == QgsMapRenderer::Millimeters )
{
if ( forceWidthScale )
{
scaleFactor = *forceWidthScale;
}
else
{
scaleFactor = sceneDpi / 25.4;
}
}
double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
{
mRenderContext.setRasterScaleFactor( rasterScaleFactor );
mySameAsLastFlag = false;
}
if ( mRenderContext.scaleFactor() != scaleFactor )
{
mRenderContext.setScaleFactor( scaleFactor );
mySameAsLastFlag = false;
}
if ( mRenderContext.rendererScale() != mScale )
{
//add map scale to render context
mRenderContext.setRendererScale( mScale );
mySameAsLastFlag = false;
}
if ( mLastExtent != mExtent )
{
mLastExtent = mExtent;
mySameAsLastFlag = false;
}
mRenderContext.setLabelingEngine( mLabelingEngine );
if ( mLabelingEngine )
mLabelingEngine->init( this );*/
// know we know if this render is just a repeat of the last time, we
// can clear caches if it has changed
/*if ( !mySameAsLastFlag )
{
//clear the cache pixmap if we changed resolution / extent
QSettings mySettings;
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
{
QgsMapLayerRegistry::instance()->clearAllLayerCaches();
}
}*/
// render all layers in the stack, starting at the base
QListIterator<QString> li( mSettings.layers );
li.toBack();
QgsRectangle r1, r2;
while ( li.hasPrevious() )
{
if ( mRenderContext.renderingStopped() )
{
break;
}
// Store the painter in case we need to swap it out for the
// cache painter
QPainter * mypContextPainter = mRenderContext.painter();
// Flattened image for drawing when a blending mode is set
QImage * mypFlattenedImage = 0;
QString layerId = li.previous();
QgsDebugMsg( "Rendering at layer item " + layerId );
// This call is supposed to cause the progress bar to
// advance. However, it seems that updating the progress bar is
// incompatible with having a QPainter active (the one that is
// passed into this function), as Qt produces a number of errors
// when try to do so. I'm (Gavin) not sure how to fix this, but
// added these comments and debug statement to help others...
QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" );
//emit drawingProgress(myRenderCounter++, mLayerSet.size());
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
if ( !ml )
{
QgsDebugMsg( "Layer not found in registry!" );
continue;
}
QgsDebugMsg( QString( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 extent:%5 blendmode:%6" )
.arg( ml->name() )
.arg( ml->minimumScale() )
.arg( ml->maximumScale() )
.arg( ml->hasScaleBasedVisibility() )
.arg( ml->extent().toString() )
.arg( ml->blendMode() )
);
if ( mRenderContext.useAdvancedEffects() )
{
// Set the QPainter composition mode so that this layer is rendered using
// the desired blending mode
mypContextPainter->setCompositionMode( ml->blendMode() );
}
if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() <= mSettings.scale && mSettings.scale < ml->maximumScale() ) ) //|| mOverview )
{
connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
//
// Now do the call to the layer that actually does
// the rendering work!
//
bool split = false;
/*if ( hasCrsTransformEnabled() )
{
r1 = mExtent;
split = splitLayersExtent( ml, r1, r2 );
ct = QgsCoordinateTransformCache::instance()->transform( ml->crs().authid(), mDestCRS->authid() );
mRenderContext.setExtent( r1 );
QgsDebugMsg( " extent 1: " + r1.toString() );
QgsDebugMsg( " extent 2: " + r2.toString() );
if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer
{
continue;
}
}
else
{
ct = NULL;
}*/
mRenderContext.setCoordinateTransform( 0 );
//decide if we have to scale the raster
//this is necessary in case QGraphicsScene is used
/*bool scaleRaster = false;
QgsMapToPixel rasterMapToPixel;
QgsMapToPixel bk_mapToPixel;
if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 )
{
scaleRaster = true;
}*/
// Force render of layers that are being edited
// or if there's a labeling engine that needs the layer to register features
/*if ( ml->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
if ( vl->isEditable() ||
( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) )
{
ml->setCacheImage( 0 );
}
}*/
/*QSettings mySettings;
bool useRenderCaching = false;
if ( ! split )//render caching does not yet cater for split extents
{
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
{
useRenderCaching = true;
if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
{
QgsDebugMsg( "Caching enabled but layer redraw forced by extent change or empty cache" );
QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(),
mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
if ( mypImage->isNull() )
{
QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) );
emit drawError( ml );
painter->end(); // drawError is not caught by anyone, so we end painting to notify caller
return;
}
mypImage->fill( 0 );
ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
QPainter * mypPainter = new QPainter( ml->cacheImage() );
// Changed to enable anti aliasing by default in QGIS 1.7
if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
{
mypPainter->setRenderHint( QPainter::Antialiasing );
}
mRenderContext.setPainter( mypPainter );
}
else if ( mySameAsLastFlag )
{
//draw from cached image
QgsDebugMsg( "Caching enabled --- drawing layer from cached image" );
mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
//short circuit as there is nothing else to do...
continue;
}
}
}*/
// If we are drawing with an alternative blending mode then we need to render to a separate image
// before compositing this on the map. This effectively flattens the layer and prevents
// blending occuring between objects on the layer
// (this is not required for raster layers or when layer caching is enabled, since that has the same effect)
/*bool flattenedLayer = false;
if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
if (( !useRenderCaching )
&& (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
|| ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
|| ( vl->layerTransparency() != 0 ) ) )
{
flattenedLayer = true;
mypFlattenedImage = new QImage( mRenderContext.painter()->device()->width(),
mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
if ( mypFlattenedImage->isNull() )
{
QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) );
emit drawError( ml );
painter->end(); // drawError is not caught by anyone, so we end painting to notify caller
return;
}
mypFlattenedImage->fill( 0 );
QPainter * mypPainter = new QPainter( mypFlattenedImage );
if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
{
mypPainter->setRenderHint( QPainter::Antialiasing );
}
mypPainter->scale( rasterScaleFactor, rasterScaleFactor );
mRenderContext.setPainter( mypPainter );
}
}*/
// Per feature blending mode
/*if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
if ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
{
// set the painter to the feature blend mode, so that features drawn
// on this layer will interact and blend with each other
mRenderContext.painter()->setCompositionMode( vl->featureBlendMode() );
}
}*/
/*if ( scaleRaster )
{
bk_mapToPixel = mRenderContext.mapToPixel();
rasterMapToPixel = mRenderContext.mapToPixel();
rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
mRenderContext.setMapToPixel( rasterMapToPixel );
mRenderContext.painter()->save();
mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
}*/
if ( !ml->draw( mRenderContext ) )
{
// TODO emit drawError( ml );
}
else
{
QgsDebugMsg( "Layer rendered without issues" );
}
if ( split )
{
mRenderContext.setExtent( r2 );
if ( !ml->draw( mRenderContext ) )
{
// TODO emit drawError( ml );
}
}
/*if ( scaleRaster )
{
mRenderContext.setMapToPixel( bk_mapToPixel );
mRenderContext.painter()->restore();
}*/
//apply layer transparency for vector layers
/*if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
if ( vl->layerTransparency() != 0 )
{
// a layer transparency has been set, so update the alpha for the flattened layer
// by combining it with the layer transparency
QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * vl->layerTransparency() / 100 ) );
// use destination in composition mode to merge source's alpha with destination
mRenderContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn );
mRenderContext.painter()->fillRect( 0, 0, mRenderContext.painter()->device()->width(),
mRenderContext.painter()->device()->height(), transparentFillColor );
}
}*/
/*if ( useRenderCaching )
{
// composite the cached image into our view and then clean up from caching
// by reinstating the painter as it was swapped out for caching renders
delete mRenderContext.painter();
mRenderContext.setPainter( mypContextPainter );
//draw from cached image that we created further up
if ( ml->cacheImage() )
mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
}
else if ( flattenedLayer )
{
// If we flattened this layer for alternate blend modes, composite it now
delete mRenderContext.painter();
mRenderContext.setPainter( mypContextPainter );
mypContextPainter->save();
mypContextPainter->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
mypContextPainter->drawImage( 0, 0, *( mypFlattenedImage ) );
mypContextPainter->restore();
delete mypFlattenedImage;
mypFlattenedImage = 0;
}*/
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
}
else // layer not visible due to scale
{
QgsDebugMsg( "Layer not rendered because it is not within the defined "
"visibility scale range" );
}
} // while (li.hasPrevious())
QgsDebugMsg( "Done rendering map layers" );
// Reset the composition mode before rendering the labels
mRenderContext.painter()->setCompositionMode( QPainter::CompositionMode_SourceOver );
/*if ( !mOverview )
{
// render all labels for vector layers in the stack, starting at the base
li.toBack();
while ( li.hasPrevious() )
{
if ( mRenderContext.renderingStopped() )
{
break;
}
QString layerId = li.previous();
// TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
{
// only make labels if the layer is visible
// after scale dep viewing settings are checked
if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) )
{
bool split = false;
if ( hasCrsTransformEnabled() )
{
QgsRectangle r1 = mExtent;
split = splitLayersExtent( ml, r1, r2 );
ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
mRenderContext.setExtent( r1 );
}
else
{
ct = NULL;
}
mRenderContext.setCoordinateTransform( ct );
ml->drawLabels( mRenderContext );
if ( split )
{
mRenderContext.setExtent( r2 );
ml->drawLabels( mRenderContext );
}
}
}
}
} // if (!mOverview)*/
// make sure progress bar arrives at 100%!
// TODO emit drawingProgress( 1, 1 );
/*if ( mLabelingEngine )
{
// set correct extent
mRenderContext.setExtent( mExtent );
mRenderContext.setCoordinateTransform( NULL );
mLabelingEngine->drawLabeling( mRenderContext );
mLabelingEngine->exit();
}*/
QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
}

View File

@ -0,0 +1,119 @@
#ifndef QGSMAPRENDERERJOB_H
#define QGSMAPRENDERERJOB_H
#include <QObject>
#include <QImage>
#include "qgsrendercontext.h"
#include "qgsmaprendererv2.h"
/** abstract base class renderer jobs that asynchronously start map rendering */
class QgsMapRendererJob : public QObject
{
Q_OBJECT
public:
enum Type
{
SequentialJob,
CustomPainterJob
//ParallelJob
};
QgsMapRendererJob(Type type, const QgsMapRendererSettings& settings) : mType(type), mSettings(settings) { }
virtual ~QgsMapRendererJob() {}
Type type() const { return mType; }
//! Start the rendering job and immediately return.
virtual void start() = 0;
//! Stop the rendering job - does not return until the job has terminated.
virtual void cancel() = 0;
//! Get a preview/resulting image - in case QPainter has not been provided.
//! With QPainter specified, it will return invalid QImage (there's no way to provide it).
virtual QImage renderedImage() = 0;
signals:
//! emitted when asynchronous rendering is finished (or canceled).
void finished();
protected:
Type mType;
QgsMapRendererSettings mSettings;
};
class QgsMapRendererCustomPainterJob;
/** job implementation that renders everything sequentially in one thread */
class QgsMapRendererSequentialJob : public QgsMapRendererJob
{
Q_OBJECT
public:
QgsMapRendererSequentialJob(const QgsMapRendererSettings& settings);
virtual void start();
virtual void cancel();
virtual QImage renderedImage();
public slots:
void internalFinished();
protected:
QgsMapRendererCustomPainterJob* mInternalJob;
QImage mImage;
QPainter* mPainter;
};
/** job implementation that renders all layers in parallel - the implication is that rendering is done to QImage */
//class QgsMapRendererParallelJob : public QgsMapRendererJob
//{
//};
#include <QtConcurrentRun>
#include <QFutureWatcher>
/** job implementation that renders everything sequentially using a custom painter.
* The returned image is always invalid (because there is none available).
*/
class QgsMapRendererCustomPainterJob : public QgsMapRendererJob
{
Q_OBJECT
public:
QgsMapRendererCustomPainterJob(const QgsMapRendererSettings& settings, QPainter* painter);
virtual void start();
virtual void cancel();
virtual QImage renderedImage();
protected slots:
void futureFinished();
protected:
static void staticRender(QgsMapRendererCustomPainterJob* self); // function to be used within the thread
void startRender();
private:
QPainter* mPainter;
QFuture<void> mFuture;
QFutureWatcher<void> mFutureWatcher;
QgsRenderContext mRenderContext;
};
#endif // QGSMAPRENDERERJOB_H

View File

@ -0,0 +1,252 @@
#include "qgsmaprendererv2.h"
#include "qgsscalecalculator.h"
#include "qgsmaprendererjob.h"
#include "qgsmaptopixel.h"
#include "qgslogger.h"
/*
usage in QgsMapCanvas - upon pan / zoom - in QgsMapCanvasMap:
- stop rendering if active
- update QgsMapRendererV2 settings
- start rendering
- start update timer
- [on timeout/finished] show rendered image
usage in QgsComposer
- create QgsMapRendererV2
- setup, start with QPainter
- wait until it finishes
*/
QgsMapRendererV2::QgsMapRendererV2()
: mActiveJob(0)
, mScaleCalculator(new QgsScaleCalculator())
{
}
QgsMapRendererV2::~QgsMapRendererV2()
{
// make sure there is no job running
cancel();
}
bool QgsMapRendererV2::start(bool parallel)
{
if (mActiveJob)
return false;
if (parallel)
{
Q_ASSERT(false && "parallel job not implemented yet");
return false;
}
else
{
mActiveJob = new QgsMapRendererSequentialJob(mSettings);
}
connect(mActiveJob, SIGNAL(finished()), this, SLOT(onJobFinished()));
mActiveJob->start();
return true;
}
bool QgsMapRendererV2::startWithCustomPainter(QPainter *painter)
{
if (mActiveJob)
return false;
mActiveJob = new QgsMapRendererCustomPainterJob(mSettings, painter);
connect(mActiveJob, SIGNAL(finished()), this, SLOT(onJobFinished()));
mActiveJob->start();
return true;
}
bool QgsMapRendererV2::cancel()
{
if (!mActiveJob)
return false;
mActiveJob->cancel();
return true;
}
void QgsMapRendererV2::onJobFinished()
{
qDebug("onJobFinished");
Q_ASSERT(mActiveJob);
emit finished();
mActiveJob->deleteLater();
mActiveJob = 0;
}
QgsRectangle QgsMapRendererV2::extent() const
{
return mSettings.extent;
}
void QgsMapRendererV2::setExtent(const QgsRectangle& extent)
{
mSettings.extent = extent;
}
void QgsMapRendererV2::updateDerived()
{
QgsRectangle extent = mSettings.extent;
if ( extent.isEmpty() )
{
mSettings.valid = false;
return;
}
// Don't allow zooms where the current extent is so small that it
// can't be accurately represented using a double (which is what
// currentExtent uses). Excluding 0 avoids a divide by zero and an
// infinite loop when rendering to a new canvas. Excluding extents
// greater than 1 avoids doing unnecessary calculations.
// The scheme is to compare the width against the mean x coordinate
// (and height against mean y coordinate) and only allow zooms where
// the ratio indicates that there is more than about 12 significant
// figures (there are about 16 significant figures in a double).
if ( extent.width() > 0 &&
extent.height() > 0 &&
extent.width() < 1 &&
extent.height() < 1 )
{
// Use abs() on the extent to avoid the case where the extent is
// symmetrical about 0.
double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5;
double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5;
double xRange = extent.width() / xMean;
double yRange = extent.height() / yMean;
static const double minProportion = 1e-12;
if ( xRange < minProportion || yRange < minProportion )
{
mSettings.valid = false;
return;
}
}
double myHeight = mSettings.size.height();
double myWidth = mSettings.size.width();
if ( !myWidth || !myHeight )
{
mSettings.valid = false;
return;
}
// calculate the translation and scaling parameters
double mapUnitsPerPixelY = mSettings.extent.height() / myHeight;
double mapUnitsPerPixelX = mSettings.extent.width() / myWidth;
mSettings.mapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
// calculate the actual extent of the mapCanvas
double dxmin = mSettings.extent.xMinimum(), dxmax = mSettings.extent.xMaximum(),
dymin = mSettings.extent.yMinimum(), dymax = mSettings.extent.yMaximum(), whitespace;
if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
{
whitespace = (( myWidth * mSettings.mapUnitsPerPixel ) - mSettings.extent.width() ) * 0.5;
dxmin -= whitespace;
dxmax += whitespace;
}
else
{
whitespace = (( myHeight * mSettings.mapUnitsPerPixel ) - mSettings.extent.height() ) * 0.5;
dymin -= whitespace;
dymax += whitespace;
}
mSettings.visibleExtent.set( dxmin, dymin, dxmax, dymax );
// update the scale
mSettings.scale = mScaleCalculator->calculate( mSettings.extent, mSettings.size.width() );
QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ) ).arg( qgsDoubleToString( mapUnitsPerPixelY ) ) );
QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( myWidth ) ).arg( qgsDoubleToString( myHeight ) ) );
QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.extent.width() ) ).arg( qgsDoubleToString( mSettings.extent.height() ) ) );
QgsDebugMsg( mSettings.extent.toString() );
QgsDebugMsg( QString( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.visibleExtent.width() / myWidth ) ).arg( qgsDoubleToString( mSettings.visibleExtent.height() / myHeight ) ) );
QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.visibleExtent.width() / mSettings.mapUnitsPerPixel ) ).arg( qgsDoubleToString( mSettings.visibleExtent.height() / mSettings.mapUnitsPerPixel ) ) );
QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mSettings.scale ) ) );
}
QSize QgsMapRendererV2::outputSize() const
{
return mSettings.size;
}
void QgsMapRendererV2::setOutputSize(const QSize& size)
{
mSettings.size = size;
}
double QgsMapRendererV2::outputDpi() const
{
return mSettings.dpi;
}
void QgsMapRendererV2::setOutputDpi(double dpi)
{
mSettings.dpi = dpi;
}
QStringList QgsMapRendererV2::layers() const
{
return mSettings.layers;
}
void QgsMapRendererV2::setLayers(const QStringList& layers)
{
mSettings.layers = layers;
}
bool QgsMapRendererV2::hasValidSettings() const
{
return mSettings.valid;
}
QgsRectangle QgsMapRendererV2::visibleExtent() const
{
return mSettings.visibleExtent;
}
double QgsMapRendererV2::mapUnitsPerPixel() const
{
return mSettings.mapUnitsPerPixel;
}
double QgsMapRendererV2::scale() const
{
return mSettings.scale;
}

121
src/core/qgsmaprendererv2.h Normal file
View File

@ -0,0 +1,121 @@
#ifndef QGSMAPRENDERERV2_H
#define QGSMAPRENDERERV2_H
#include <QSize>
#include <QStringList>
#include "qgscoordinatereferencesystem.h"
#include "qgsrectangle.h"
class QPainter;
class QgsScaleCalculator;
class QgsMapRendererJob;
struct QgsMapRendererSettings
{
// TODO
double dpi;
QSize size;
QgsRectangle extent;
QStringList layers;
bool projectionsEnabled;
QgsCoordinateReferenceSystem destCRS;
// derived properties
bool valid; //!< whether the actual settings are valid (set in updateDerived())
QgsRectangle visibleExtent; //!< extent with some additional white space that matches the output aspect ratio
double mapUnitsPerPixel;
double scale;
// TODO: utility functions
};
class QgsMapRendererV2 : public QObject
{
Q_OBJECT
public:
QgsMapRendererV2();
~QgsMapRendererV2();
//
// getters/setters for rendering settings
//
QgsRectangle extent() const;
void setExtent(const QgsRectangle& rect);
QSize outputSize() const;
void setOutputSize(const QSize& size);
double outputDpi() const;
void setOutputDpi(double dpi);
QStringList layers() const;
void setLayers(const QStringList& layers);
void updateDerived(); // TODO: should be protected, called automatically
bool hasValidSettings() const;
QgsRectangle visibleExtent() const;
double mapUnitsPerPixel() const;
double scale() const;
//! Access all map renderer settings at once
const QgsMapRendererSettings& settings() const { return mSettings; }
//
// rendering control
//
//! start rendering to a QImage with the current settings
bool start(bool parallel = false);
//! start rendering with a custom painter (
bool startWithCustomPainter(QPainter* painter);
//! cancel the rendering job and wait until it stops
bool cancel();
//! block until the rendering is done
void waitForFinished();
bool isRendering() const { return mActiveJob != 0; }
const QgsMapRendererJob* activeJob() const { return mActiveJob; }
signals:
void finished();
protected slots:
void onJobFinished();
protected:
void updateScale();
void adjustExtentToSize();
protected:
QgsMapRendererSettings mSettings;
QgsScaleCalculator* mScaleCalculator;
//! currently running renderer job (null if there is none)
QgsMapRendererJob* mActiveJob;
};
#endif // QGSMAPRENDERERV2_H

View File

@ -115,7 +115,6 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
QString providerKey,
bool loadDefaultStyleFlag )
: QgsMapLayer( VectorLayer, baseName, vectorLayerPath )
, mUpdateThreshold( 0 ) // XXX better default value?
, mDataProvider( NULL )
, mProviderKey( providerKey )
, mReadOnly( false )
@ -379,11 +378,6 @@ void QgsVectorLayer::drawLabels( QgsRenderContext& rendererContext )
}
QgsDebugMsg( QString( "Total features processed %1" ).arg( featureCount ) );
// XXX Something in our draw event is triggering an additional draw event when resizing [TE 01/26/06]
// XXX Calling this will begin processing the next draw event causing image havoc and recursion crashes.
//qApp->processEvents();
}
}
@ -399,10 +393,6 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
QSettings settings;
bool vertexMarkerOnlyForSelection = settings.value( "/qgis/digitizing/marker_only_for_selected", false ).toBool();
#ifndef Q_WS_MAC
int featureCount = 0;
#endif //Q_WS_MAC
QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
@ -411,29 +401,9 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
if ( !fet.geometry() )
continue; // skip features without geometry
#ifndef Q_WS_MAC //MH: disable this on Mac for now to avoid problems with resizing
#ifdef Q_WS_X11
if ( !mEnableBackbuffer ) // do not handle events, as we're already inside a paint event
{
#endif // Q_WS_X11
if ( mUpdateThreshold > 0 && 0 == featureCount % mUpdateThreshold )
{
emit screenUpdateRequested();
// emit drawingProgress( featureCount, totalFeatures );
qApp->processEvents();
}
else if ( featureCount % 1000 == 0 )
{
// emit drawingProgress( featureCount, totalFeatures );
qApp->processEvents();
}
#ifdef Q_WS_X11
}
#endif // Q_WS_X11
#endif // Q_WS_MAC
if ( rendererContext.renderingStopped() )
{
qDebug("breaking!");
break;
}
@ -468,18 +438,11 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
.arg( fet.id() ).arg( cse.what() ) );
}
#ifndef Q_WS_MAC
++featureCount;
#endif //Q_WS_MAC
}
stopRendererV2( rendererContext, NULL );
mCurrentRendererContext = NULL;
#ifndef Q_WS_MAC
QgsDebugMsg( QString( "Total features processed %1" ).arg( featureCount ) );
#endif
}
void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderContext& rendererContext, bool labeling )
@ -503,9 +466,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
// 1. fetch features
QgsFeature fet;
#ifndef Q_WS_MAC
int featureCount = 0;
#endif //Q_WS_MAC
while ( fit.nextFeature( fet ) )
{
if ( !fet.geometry() )
@ -513,15 +473,11 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
if ( rendererContext.renderingStopped() )
{
qDebug("rendering stop!");
stopRendererV2( rendererContext, selRenderer );
return;
}
#ifndef Q_WS_MAC
if ( featureCount % 1000 == 0 )
{
qApp->processEvents();
}
#endif //Q_WS_MAC
QgsSymbolV2* sym = mRendererV2->symbolForFeature( fet );
if ( !sym )
{
@ -551,10 +507,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
rendererContext.labelingEngine()->registerDiagramFeature( this, fet, rendererContext );
}
}
#ifndef Q_WS_MAC
++featureCount;
#endif //Q_WS_MAC
}
// find out the order
@ -590,9 +542,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
int layer = item.layer();
QList<QgsFeature>& lst = features[item.symbol()];
QList<QgsFeature>::iterator fit;
#ifndef Q_WS_MAC
featureCount = 0;
#endif //Q_WS_MAC
for ( fit = lst.begin(); fit != lst.end(); ++fit )
{
if ( rendererContext.renderingStopped() )
@ -600,12 +549,7 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
stopRendererV2( rendererContext, selRenderer );
return;
}
#ifndef Q_WS_MAC
if ( featureCount % 1000 == 0 )
{
qApp->processEvents();
}
#endif //Q_WS_MAC
bool sel = mSelectedFeatureIds.contains( fit->id() );
// maybe vertex markers should be drawn only during the last pass...
bool drawMarker = ( mEditBuffer && ( !vertexMarkerOnlyForSelection || sel ) );
@ -620,9 +564,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
.arg( fet.id() ).arg( cse.what() ) );
}
#ifndef Q_WS_MAC
++featureCount;
#endif //Q_WS_MAC
}
}
}
@ -643,20 +584,6 @@ bool QgsVectorLayer::draw( QgsRenderContext& rendererContext )
if ( !hasGeometryType() )
return true;
//set update threshold before each draw to make sure the current setting is picked up
QSettings settings;
mUpdateThreshold = settings.value( "Map/updateThreshold", 0 ).toInt();
// users could accidently set updateThreshold threshold to a small value
// and complain about bad performance -> force min 1000 here
if ( mUpdateThreshold > 0 && mUpdateThreshold < 1000 )
{
mUpdateThreshold = 1000;
}
#ifdef Q_WS_X11
mEnableBackbuffer = settings.value( "/Map/enableBackbuffer", 1 ).toBool();
#endif
if ( !mRendererV2 )
return false;

View File

@ -1572,17 +1572,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
private: // Private attributes
/** Update threshold for drawing features as they are read. A value of zero indicates
* that no features will be drawn until all have been read
*/
int mUpdateThreshold;
/** Enables backbuffering for the map window. This improves graphics performance,
* but the possibility to cancel rendering and incremental feature drawing will be lost.
*
*/
bool mEnableBackbuffer;
/** Pointer to data provider derived from the abastract base class QgsDataProvider */
QgsVectorDataProvider *mDataProvider;

View File

@ -148,6 +148,7 @@ ENDIF (WITH_TOUCH)
SET(QGIS_GUI_MOC_HDRS
qgscolorbutton.h
qgsmapcanvasmap.h
raster/qgsrasterminmaxwidget.h
raster/qgspalettedrendererwidget.h

View File

@ -81,8 +81,8 @@ class QgsMapCanvas::CanvasProperties
QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
: QGraphicsView( parent )
, mCanvasProperties( new CanvasProperties )
, mNewSize( QSize() )
, mPainting( false )
//, mNewSize( QSize() )
//, mPainting( false )
, mAntiAliasing( false )
{
setObjectName( name );
@ -96,7 +96,6 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
mMapTool = NULL;
mLastNonZoomMapTool = NULL;
mBackbufferEnabled = true;
mDrawing = false;
mFrozen = false;
mDirty = true;
@ -178,8 +177,8 @@ void QgsMapCanvas::enableAntiAliasing( bool theFlag )
void QgsMapCanvas::useImageToRender( bool theFlag )
{
mMap->useImageToRender( theFlag );
refresh(); // redraw the map on change - prevents black map view
//mMap->useImageToRender( theFlag );
//refresh(); // redraw the map on change - prevents black map view
}
QgsMapCanvasMap* QgsMapCanvas::map()
@ -187,10 +186,10 @@ QgsMapCanvasMap* QgsMapCanvas::map()
return mMap;
}
QgsMapRenderer* QgsMapCanvas::mapRenderer()
/*QgsMapRenderer* QgsMapCanvas::mapRenderer()
{
return mMapRenderer;
}
}*/
QgsMapLayer* QgsMapCanvas::layer( int index )
@ -235,7 +234,7 @@ bool QgsMapCanvas::isDrawing()
// device size
const QgsMapToPixel * QgsMapCanvas::getCoordinateTransform()
{
return mMapRenderer->coordinateTransform();
return mMap->coordinateTransform();
}
void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
@ -284,7 +283,6 @@ void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
// Ticket #811 - racicot
QgsMapLayer *currentLayer = layer( i );
disconnect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
disconnect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( isVectLyr )
{
@ -300,7 +298,6 @@ void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
// Ticket #811 - racicot
QgsMapLayer *currentLayer = layer( i );
connect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
connect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( isVectLyr )
{
@ -353,6 +350,11 @@ void QgsMapCanvas::enableOverviewMode( QgsMapOverviewCanvas* overview )
}
}
const QgsMapRendererSettings &QgsMapCanvas::mapRendererSettings() const
{
return mMap->settings();
}
void QgsMapCanvas::updateOverview()
{
@ -372,6 +374,9 @@ QgsMapLayer* QgsMapCanvas::currentLayer()
void QgsMapCanvas::refresh()
{
mMap->refresh();
/*
// we can't draw again if already drawing...
if ( mDrawing )
return;
@ -384,32 +389,6 @@ void QgsMapCanvas::refresh()
t.start();
}
#ifdef Q_WS_X11
bool enableBackbufferSetting = settings.value( "/Map/enableBackbuffer", 1 ).toBool();
#endif
#ifdef Q_WS_X11
#ifndef ANDROID
// disable the update that leads to the resize crash on X11 systems
if ( viewport() )
{
if ( enableBackbufferSetting != mBackbufferEnabled )
{
qDebug() << "Enable back buffering: " << enableBackbufferSetting;
if ( enableBackbufferSetting )
{
viewport()->setAttribute( Qt::WA_PaintOnScreen, false );
}
else
{
viewport()->setAttribute( Qt::WA_PaintOnScreen, true );
}
mBackbufferEnabled = enableBackbufferSetting;
}
}
#endif // ANDROID
#endif // Q_WS_X11
mDrawing = true;
if ( mRenderFlag && !mFrozen )
@ -454,15 +433,12 @@ void QgsMapCanvas::refresh()
}
QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
}
*/
} // refresh
void QgsMapCanvas::updateMap()
{
if ( mMap )
{
mMap->updateContents();
}
}
//the format defaults to "PNG" if not specified
@ -484,6 +460,7 @@ void QgsMapCanvas::saveAsImage( QString theFileName, QPixmap * theQPixmap, QStri
}
else //use the map view
{
// TODO[MD]: fix
QPixmap *pixmap = dynamic_cast<QPixmap *>( &mMap->paintDevice() );
if ( !pixmap )
return;
@ -1022,13 +999,28 @@ void QgsMapCanvas::mouseReleaseEvent( QMouseEvent * e )
void QgsMapCanvas::resizeEvent( QResizeEvent * e )
{
mNewSize = e->size();
QGraphicsView::resizeEvent(e);
QSize lastSize = size();
//set map size before scene size helps keep scene indexes updated properly
// this was the cause of rubberband artifacts
mMap->resize( lastSize );
mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
// notify canvas items of change
updateCanvasItemPositions();
updateScale();
refresh();
emit extentsChanged();
}
void QgsMapCanvas::paintEvent( QPaintEvent *e )
{
if ( mNewSize.isValid() )
{
/*
if ( mPainting || mDrawing )
{
//cancel current render progress
@ -1042,31 +1034,7 @@ void QgsMapCanvas::paintEvent( QPaintEvent *e )
}
return;
}
mPainting = true;
while ( mNewSize.isValid() )
{
QSize lastSize = mNewSize;
mNewSize = QSize();
//set map size before scene size helps keep scene indexes updated properly
// this was the cause of rubberband artifacts
mMap->resize( lastSize );
mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
// notify canvas items of change
updateCanvasItemPositions();
updateScale();
refresh();
emit extentsChanged();
}
mPainting = false;
}
*/
QGraphicsView::paintEvent( e );
} // paintEvent
@ -1401,12 +1369,19 @@ void QgsMapCanvas::panActionEnd( QPoint releasePoint )
QgsPoint start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
QgsPoint end = getCoordinateTransform()->toMapCoordinates( releasePoint );
qDebug("start %f,%f", start.x(), start.y());
qDebug("end %f,%f", end.x(), end.y());
double dx = qAbs( end.x() - start.x() );
double dy = qAbs( end.y() - start.y() );
// modify the extent
QgsRectangle r = mMapRenderer->extent();
qDebug(" -------------XXX diff: %f,%f", dx, dy);
qDebug(" ------------oldR: %f,%f", r.xMinimum(), r.yMinimum());
if ( end.x() < start.x() )
{
r.setXMinimum( r.xMinimum() + dx );
@ -1432,6 +1407,10 @@ void QgsMapCanvas::panActionEnd( QPoint releasePoint )
}
setExtent( r );
r = mMapRenderer->extent();
qDebug(" ------------newR: %f,%f", r.xMinimum(), r.yMinimum());
refresh();
}

View File

@ -57,6 +57,7 @@ class QgsHighlight;
class QgsVectorLayer;
class QgsMapRenderer;
class QgsMapRendererSettings;
class QgsMapCanvasMap;
class QgsMapOverviewCanvas;
class QgsMapTool;
@ -118,11 +119,16 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
void enableOverviewMode( QgsMapOverviewCanvas* overview );
const QgsMapRendererSettings& mapRendererSettings() const;
//! @deprecated since 2.1 - there could be more than just one "map" items
QgsMapCanvasMap* map();
QgsMapRenderer* mapRenderer();
//! @deprecated since 2.1 - use mapRendererSettings() for anything related to current renderer settings
// temporarily disabled QgsMapRenderer* mapRenderer();
//! Accessor for the canvas paint device
//! @deprecated since 2.1
QPaintDevice &canvasPaintDevice();
//! Get the last reported scale of the canvas
@ -291,7 +297,7 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
/** The map units may have changed, so cope with that */
void mapUnitsChanged();
/** updates pixmap on render progress */
//! \note Deprecated in 2.1 - does nothing - kept for API compatibility
void updateMap();
//! show whatever error is exposed by the QgsMapLayer.
@ -437,8 +443,6 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! map overview widget - it's controlled by QgsMapCanvas
QgsMapOverviewCanvas* mMapOverview;
//! If backbuffering is currently enabled
bool mBackbufferEnabled;
//! Flag indicating a map refresh is in progress
bool mDrawing;
@ -487,10 +491,10 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
WheelAction mWheelAction;
//! resize canvas size
QSize mNewSize;
//QSize mNewSize;
//! currently in paint event
bool mPainting;
//bool mPainting;
//! indicates whether antialiasing will be used for rendering
bool mAntiAliasing;

View File

@ -17,39 +17,136 @@
#include "qgsmapcanvas.h"
#include "qgsmapcanvasmap.h"
#include "qgsmaprenderer.h"
#include "qgsmaprendererv2.h"
#include "qgsmaplayer.h"
#include <QPainter>
QgsMapCanvasMap::QgsMapCanvasMap( QgsMapCanvas* canvas )
: mCanvas( canvas )
, mDirty(true)
{
mRend = new QgsMapRendererV2();
setZValue( -10 );
setPos( 0, 0 );
resize( QSize( 1, 1 ) );
mUseQImageToRender = true;
connect(mRend, SIGNAL(finished()), SLOT(finish()));
connect(&mTimer, SIGNAL(timeout()), SLOT(onMapUpdateTimeout()));
mTimer.setInterval(400);
}
QgsMapCanvasMap::~QgsMapCanvasMap()
{
delete mRend;
mRend = 0;
}
void QgsMapCanvasMap::refresh()
{
if (mRend->isRendering())
{
qDebug("need to cancel first!");
mRend->cancel();
}
mDirty = true;
update();
}
void QgsMapCanvasMap::paint( QPainter* p, const QStyleOptionGraphicsItem*, QWidget* )
{
//refreshes the canvas map with the current offscreen image
p->drawPixmap( 0, 0, mPixmap );
qDebug("paint()");
if (mDirty)
{
if (mRend->isRendering())
{
qDebug("already rendering");
}
else
{
qDebug("need to render");
QStringList layerIds;
foreach (QgsMapLayer* l, mCanvas->layers())
layerIds.append(l->id());
mRend->setLayers(layerIds);
mRend->setExtent(mCanvas->extent());
mRend->setOutputSize(mImage.size());
mRend->setOutputDpi(120);
mRend->updateDerived();
const QgsMapRendererSettings& s = mRend->settings();
mMapToPixel = QgsMapToPixel( s.mapUnitsPerPixel, s.size.height(), s.visibleExtent.yMinimum(), s.visibleExtent.xMinimum() );
qDebug("----------> EXTENT %f,%f", mRend->extent().xMinimum(), mRend->extent().yMinimum());
mImage.fill(mBgColor.rgb());
mPainter = new QPainter(&mImage);
// TODO[MD]: need to setup clipping?
//paint.setClipRect( mImage.rect() );
// antialiasing
if ( mAntiAliasing )
mPainter->setRenderHint( QPainter::Antialiasing );
mRend->startWithCustomPainter(mPainter);
mTimer.start();
//p->drawImage(0,0, mLastImage);
//return; // do not redraw the image
}
}
#ifdef EGA_MODE
QImage i2( mImage.size()/3, mImage.format() );
QPainter p2(&i2);
p2.drawImage( QRect(0,0, mImage.width()/3, mImage.height()/3), mImage ); //, 0,0, mImage.width()/3, mImage.height()/3);
p2.end();
p->drawImage( QRect( 0, 0, mImage.width(), mImage.height()), i2 ) ;//, 0, 0, i2.width()*3, i2.height()*3 );
#else
p->drawImage( 0, 0, mImage );
#endif
}
QRectF QgsMapCanvasMap::boundingRect() const
{
return QRectF( 0, 0, mPixmap.width(), mPixmap.height() );
return QRectF( 0, 0, mImage.width(), mImage.height() );
}
const QgsMapRendererSettings &QgsMapCanvasMap::settings() const
{
return mRend->settings();
}
QgsMapToPixel *QgsMapCanvasMap::coordinateTransform()
{
return &mMapToPixel;
}
void QgsMapCanvasMap::resize( QSize size )
{
if (mRend->isRendering())
{
qDebug("need to cancel first!");
mRend->cancel();
}
QgsDebugMsg( QString( "resizing to %1x%2" ).arg( size.width() ).arg( size.height() ) );
prepareGeometryChange(); // to keep QGraphicsScene indexes up to date on size change
mPixmap = QPixmap( size );
mPixmap.fill( mBgColor.rgb() );
mImage = QImage( size, QImage::Format_RGB32 ); // temporary image - build it here so it is available when switching from QPixmap to QImage rendering
mCanvas->mapRenderer()->setOutputSize( size, mPixmap.logicalDpiX() );
mImage = QImage( size, QImage::Format_ARGB32_Premultiplied );
//mCanvas->mapRenderer()->setOutputSize( size, mImage.logicalDpiX() );
}
void QgsMapCanvasMap::setPanningOffset( const QPoint& point )
@ -58,64 +155,32 @@ void QgsMapCanvasMap::setPanningOffset( const QPoint& point )
setPos( mOffset );
}
void QgsMapCanvasMap::render()
{
QgsDebugMsg( QString( "mUseQImageToRender = %1" ).arg( mUseQImageToRender ) );
if ( mUseQImageToRender )
{
// use temporary image for rendering
mImage.fill( mBgColor.rgb() );
// clear the pixmap so that old map won't be displayed while rendering
// TODO: do the canvas updates wisely -> this wouldn't be needed
mPixmap = QPixmap( mImage.size() );
mPixmap.fill( mBgColor.rgb() );
QPainter paint;
paint.begin( &mImage );
// Clip drawing to the QImage
paint.setClipRect( mImage.rect() );
// antialiasing
if ( mAntiAliasing )
paint.setRenderHint( QPainter::Antialiasing );
mCanvas->mapRenderer()->render( &paint );
paint.end();
// convert QImage to QPixmap to achieve faster drawing on screen
mPixmap = QPixmap::fromImage( mImage );
}
else
{
mPixmap.fill( mBgColor.rgb() );
QPainter paint;
paint.begin( &mPixmap );
// Clip our drawing to the QPixmap
paint.setClipRect( mPixmap.rect() );
// antialiasing
if ( mAntiAliasing )
paint.setRenderHint( QPainter::Antialiasing );
mCanvas->mapRenderer()->render( &paint );
paint.end();
}
update();
}
QPaintDevice& QgsMapCanvasMap::paintDevice()
{
return mPixmap;
return mImage;
}
void QgsMapCanvasMap::updateContents()
{
// make sure we're using current contents
if ( mUseQImageToRender )
mPixmap = QPixmap::fromImage( mImage );
// trigger update of this item
void QgsMapCanvasMap::finish()
{
qDebug("finish!");
mTimer.stop();
mDirty = false;
delete mPainter;
mPainter = 0;
//mLastImage = mImage;
update();
}
void QgsMapCanvasMap::onMapUpdateTimeout()
{
qDebug("update timer!");
update();
}

View File

@ -18,56 +18,68 @@
#include <QGraphicsRectItem>
#include <QPixmap>
#include <QTimer>
#include <qgis.h>
#include <qgsmaptopixel.h>
class QgsMapRenderer;
class QgsMapRendererSettings;
class QgsMapRendererV2;
class QgsMapCanvas;
/** \ingroup gui
* A rectangular graphics item representing the map on the canvas.
*/
class GUI_EXPORT QgsMapCanvasMap : public QGraphicsRectItem
class GUI_EXPORT QgsMapCanvasMap : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
//! constructor
QgsMapCanvasMap( QgsMapCanvas* canvas );
~QgsMapCanvasMap();
void refresh();
//! resize canvas item and pixmap
void resize( QSize size );
void enableAntiAliasing( bool flag ) { mAntiAliasing = flag; }
void useImageToRender( bool flag ) { mUseQImageToRender = flag; }
//! renders map using QgsMapRenderer to mPixmap
void render();
//! @deprecated in 2.1 - does nothing. Kept for API compatibility
void render() {}
void setBackgroundColor( const QColor& color ) { mBgColor = color; }
void setPanningOffset( const QPoint& point );
//! @deprecated in 2.1
QPaintDevice& paintDevice();
void paint( QPainter* p, const QStyleOptionGraphicsItem*, QWidget* );
QRectF boundingRect() const;
//! Update contents - can be called while drawing to show the status.
//! Added in version 1.2
void updateContents();
//! @deprecated in 2.1 - does nothing. Kept for API compatibility
void updateContents() {}
const QgsMapRendererSettings& settings() const;
QgsMapToPixel* coordinateTransform(); // TODO: rename!
public slots:
void finish();
void onMapUpdateTimeout();
private:
//! indicates whether antialiasing will be used for rendering
bool mAntiAliasing;
//! Whether to use a QPixmap or a QImage for the rendering
bool mUseQImageToRender;
QPixmap mPixmap;
QImage mImage;
QImage mLastImage;
//QgsMapRenderer* mRender;
QgsMapCanvas* mCanvas;
@ -75,6 +87,16 @@ class GUI_EXPORT QgsMapCanvasMap : public QGraphicsRectItem
QColor mBgColor;
QPoint mOffset;
bool mDirty; //!< whether a new rendering job should be started upon next paint() call
QgsMapRendererV2* mRend;
QPainter* mPainter;
QTimer mTimer;
QgsMapToPixel mMapToPixel;
};
#endif