[QGIS Commit] r13689 - in branches/threading-branch: python/core
src/core
svn_qgis at osgeo.org
svn_qgis at osgeo.org
Tue Jun 8 09:28:09 EDT 2010
Author: wonder
Date: 2010-06-08 09:28:08 -0400 (Tue, 08 Jun 2010)
New Revision: 13689
Modified:
branches/threading-branch/python/core/qgsmaprenderer.sip
branches/threading-branch/src/core/qgscoordinatetransform.cpp
branches/threading-branch/src/core/qgscoordinatetransform.h
branches/threading-branch/src/core/qgsmaprenderer.cpp
branches/threading-branch/src/core/qgsmaprenderer.h
branches/threading-branch/src/core/qgsrendercontext.cpp
branches/threading-branch/src/core/qgsrendercontext.h
Log:
Threaded rendering of map layers - initial implementation, work in progress
Modified: branches/threading-branch/python/core/qgsmaprenderer.sip
===================================================================
--- branches/threading-branch/python/core/qgsmaprenderer.sip 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/python/core/qgsmaprenderer.sip 2010-06-08 13:28:08 UTC (rev 13689)
@@ -154,6 +154,22 @@
//! Added in QGIS v1.4
void setLabelingEngine(QgsLabelingEngineInterface* iface /Transfer/);
+ //! Enable or disable rendering in multiple threads on multiprocessor computers
+ //! Added in QGIS v1.6
+ void setThreadingEnabled( bool use );
+
+ //! Determine whether we are using threaded rendering
+ //! Added in QGIS v1.6
+ bool isThreadingEnabled() const;
+
+ //! Enable or disable caching of rendered layers
+ //! Added in QGIS v1.6
+ void setCachingEnabled( bool enabled );
+
+ //! Determine whether the rendered layers are cached
+ //! Added in QGIS v1.6
+ bool isCachingEnabled() const;
+
signals:
void drawingProgress(int current, int total);
Modified: branches/threading-branch/src/core/qgscoordinatetransform.cpp
===================================================================
--- branches/threading-branch/src/core/qgscoordinatetransform.cpp 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgscoordinatetransform.cpp 2010-06-08 13:28:08 UTC (rev 13689)
@@ -98,6 +98,19 @@
}
}
+QgsCoordinateTransform& QgsCoordinateTransform::operator=( const QgsCoordinateTransform& ct )
+{
+ if ( &ct != this )
+ {
+ mInitialisedFlag = FALSE;
+ mSourceCRS = ct.mSourceCRS;
+ mDestCRS = ct.mDestCRS;
+ initialise();
+ }
+ return *this;
+}
+
+
void QgsCoordinateTransform::setSourceCrs( const QgsCoordinateReferenceSystem& theCRS )
{
mSourceCRS = theCRS;
Modified: branches/threading-branch/src/core/qgscoordinatetransform.h
===================================================================
--- branches/threading-branch/src/core/qgscoordinatetransform.h 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgscoordinatetransform.h 2010-06-08 13:28:08 UTC (rev 13689)
@@ -88,6 +88,9 @@
//! destructor
~QgsCoordinateTransform();
+ //! assignment operator
+ QgsCoordinateTransform& operator=( const QgsCoordinateTransform& ct );
+
//! Enum used to indicate the direction (forward or inverse) of the transform
enum TransformDirection
{
Modified: branches/threading-branch/src/core/qgsmaprenderer.cpp
===================================================================
--- branches/threading-branch/src/core/qgsmaprenderer.cpp 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgsmaprenderer.cpp 2010-06-08 13:28:08 UTC (rev 13689)
@@ -38,9 +38,22 @@
#include <QListIterator>
#include <QSettings>
#include <QTime>
+#include <QtConcurrentMap>
#include "qgslogger.h"
+typedef struct ThreadedRenderContext
+{
+ QgsMapRenderer* mr; // renderer that governs the rendering
+ QgsMapLayer* ml; // source map layer
+ QImage* img; // destination image
+ QgsRenderContext ctx; // private render context
+} ThreadedRenderContext;
+
+void _renderLayerThreading( ThreadedRenderContext& tctx );
+
+/////
+
QgsMapRenderer::QgsMapRenderer()
{
mScaleCalculator = new QgsScaleCalculator;
@@ -335,8 +348,11 @@
}
+
void QgsMapRenderer::renderLayers( QgsOverlayObjectPositionManager* overlayManager )
{
+ QList<ThreadedRenderContext> layers;
+
// render all layers in the stack, starting at the base
QListIterator<QString> li( mLayerSet );
li.toBack();
@@ -407,50 +423,130 @@
//
connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
- renderLayer( ml );
+ if ( !mThreadingEnabled )
+ renderLayerNoThreading( ml );
+ else
+ renderLayerThreading( ml, layers );
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
}
+ if ( mThreadingEnabled )
+ {
+ QgsDebugMsg("STARTING THREADED RENDERING!");
+
+ QtConcurrent::blockingMap(layers, _renderLayerThreading);
+
+ QgsDebugMsg("THREADED RENDERING FINISHED!");
+
+ // draw rendered images and delete temporary painters
+ foreach (ThreadedRenderContext tctx, layers)
+ {
+ mRenderContext.painter()->drawImage( 0, 0, *tctx.img );
+ delete tctx.ctx.painter();
+ delete tctx.img;
+ }
+
+ QgsDebugMsg("THREADED RENDERING DONE!");
+ }
+
}
+void _renderLayerThreading( ThreadedRenderContext& tctx )
+{
+ QgsDebugMsg("threaded rendering start: "+tctx.ml->getLayerID());
+ // TODO: error handling
+ tctx.mr->renderLayer( tctx.ml, tctx.ctx );
-void QgsMapRenderer::renderLayer( QgsMapLayer* ml )
+ QgsDebugMsg("threaded rendering end : "+tctx.ml->getLayerID());
+}
+
+void QgsMapRenderer::renderLayerThreading( QgsMapLayer* ml, QList<ThreadedRenderContext>& lst )
{
- // Store the painter in case we need to swap it out for the
- // cache painter
- QPainter * mypContextPainter = mRenderContext.painter();
+ if ( mCachingEnabled && ml->cacheImage() != 0 )
+ {
+ // do nothing - cached image will be used
+ // TODO: resolve caching
+ }
+ else
+ {
+ ThreadedRenderContext tctx;
+ tctx.ml = ml;
+ // create image
+ QPaintDevice* device = mRenderContext.painter()->device();
+ tctx.img = new QImage( device->width(), device->height(), QImage::Format_ARGB32_Premultiplied );
+ tctx.img->fill( 0 );
+
+ // create private context
+ tctx.mr = this;
+ tctx.ctx = mRenderContext;
+
+ QPainter* painter = new QPainter(tctx.img);
+ if ( mRenderContext.painter()->testRenderHint( QPainter::Antialiasing ) )
+ {
+ painter->setRenderHint( QPainter::Antialiasing );
+ }
+ tctx.ctx.setPainter( painter );
+
+ // schedule DRAW to a list
+ lst.append(tctx);
+ }
+}
+
+void QgsMapRenderer::renderLayerNoThreading( QgsMapLayer* ml )
+{
if ( mCachingEnabled )
{
+ // Store the painter in case we need to swap it out for the
+ // cache painter
+ QPainter * mypContextPainter = mRenderContext.painter();
+
if ( ml->cacheImage() == 0 )
{
+ // create cached image
QgsDebugMsg( "\n\n\nCaching enabled --- redraw forced by extent change or empty cache\n\n\n" );
QPaintDevice* device = mRenderContext.painter()->device();
QImage * mypImage = new QImage( device->width(), device->height(), QImage::Format_ARGB32_Premultiplied );
mypImage->fill( 0 );
ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
+
+ // alter painter
QPainter * mypPainter = new QPainter( ml->cacheImage() );
if ( mypContextPainter->testRenderHint( QPainter::Antialiasing ) )
{
mypPainter->setRenderHint( QPainter::Antialiasing );
}
mRenderContext.setPainter( mypPainter );
+
+ // DRAW!
+ if ( ! renderLayer( ml, mRenderContext ) )
+ emit drawError( ml );
+
+ // 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 );
}
- else
- {
- //draw from cached image
- QgsDebugMsg( "\n\n\nCaching enabled --- drawing layer from cached image\n\n\n" );
- mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
- //short circuit as there is nothing else to do...
- return;
- }
+
+ // draw cached image
+ mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
}
+ else
+ {
+ // DRAW!
+ if ( ! renderLayer( ml, mRenderContext ) )
+ emit drawError( ml );
+ }
+}
+
+bool QgsMapRenderer::renderLayer( QgsMapLayer* ml, QgsRenderContext& ctx )
+{
//decide if we have to scale the raster
//this is necessary in case QGraphicsScene is used
bool scaleRaster = false;
- double rasterScaleFactor = mRenderContext.rasterScaleFactor();
+ double rasterScaleFactor = ctx.rasterScaleFactor();
QgsMapToPixel rasterMapToPixel;
QgsMapToPixel bk_mapToPixel;
@@ -461,57 +557,48 @@
if ( scaleRaster )
{
- bk_mapToPixel = mRenderContext.mapToPixel();
- rasterMapToPixel = mRenderContext.mapToPixel();
- rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
+ bk_mapToPixel = ctx.mapToPixel();
+ rasterMapToPixel = ctx.mapToPixel();
+ rasterMapToPixel.setMapUnitsPerPixel( ctx.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
- mRenderContext.setMapToPixel( rasterMapToPixel );
- mRenderContext.painter()->save();
- mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
+ ctx.setMapToPixel( rasterMapToPixel );
+ ctx.painter()->save();
+ ctx.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
}
// split the rendering into two parts if necessary
bool split = false;
QgsRectangle r1, r2;
- if ( hasCrsTransformEnabled() )
+ if ( ctx.coordinateTransform() )
{
r1 = mExtent;
split = splitLayersExtent( ml, r1, r2 );
- mRenderContext.setExtent( r1 );
+ ctx.setExtent( r1 );
}
// draw the layer
- if ( !ml->draw( mRenderContext ) )
+ if ( !ml->draw( ctx ) )
{
- emit drawError( ml );
+ return false;
}
if ( split )
{
- mRenderContext.setExtent( r2 );
- if ( !ml->draw( mRenderContext ) )
+ ctx.setExtent( r2 );
+ if ( !ml->draw( ctx ) )
{
- emit drawError( ml );
+ return false;
}
- mRenderContext.setExtent( mExtent ); // return back to the original extent
+ ctx.setExtent( mExtent ); // return back to the original extent
}
if ( scaleRaster )
{
- mRenderContext.setMapToPixel( bk_mapToPixel );
- mRenderContext.painter()->restore();
+ ctx.setMapToPixel( bk_mapToPixel );
+ ctx.painter()->restore();
}
- if ( mCachingEnabled )
- {
- // 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
- mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
- }
-
+ return true;
}
Modified: branches/threading-branch/src/core/qgsmaprenderer.h
===================================================================
--- branches/threading-branch/src/core/qgsmaprenderer.h 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgsmaprenderer.h 2010-06-08 13:28:08 UTC (rev 13689)
@@ -38,6 +38,8 @@
class QgsVectorLayer;
class QgsFeature;
+struct ThreadedRenderContext;
+
/** Labeling engine interface.
* \note Added in QGIS v1.4
*/
@@ -237,8 +239,16 @@
void renderLayers( QgsOverlayObjectPositionManager* overlayManager );
//! render one layer
- void renderLayer( QgsMapLayer* ml );
+ void renderLayerNoThreading( QgsMapLayer* ml );
+ //! schedule rendering of a layer
+ void renderLayerThreading( QgsMapLayer* ml, QList<ThreadedRenderContext>& lst );
+
+ //! invoke rendering of the layer with given context
+ bool renderLayer( QgsMapLayer* ml, QgsRenderContext& ctx );
+
+ friend void _renderLayerThreading( ThreadedRenderContext& tctx );
+
//! render labels for vector layers (not using PAL)
void renderLabels();
Modified: branches/threading-branch/src/core/qgsrendercontext.cpp
===================================================================
--- branches/threading-branch/src/core/qgsrendercontext.cpp 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgsrendercontext.cpp 2010-06-08 13:28:08 UTC (rev 13689)
@@ -42,3 +42,29 @@
mCoordTransform = t;
}
+QgsRenderContext& QgsRenderContext::operator=( const QgsRenderContext& ctx )
+{
+ if ( &ctx != this )
+ {
+ delete mCoordTransform;
+ if ( ctx.mCoordTransform )
+ {
+ mCoordTransform = new QgsCoordinateTransform();
+ *mCoordTransform = *ctx.mCoordTransform;
+ }
+ else
+ mCoordTransform = NULL;
+
+ mPainter = ctx.mPainter;
+ mDrawEditingInformation = ctx.mDrawEditingInformation;
+ mExtent = ctx.mExtent;
+ mForceVectorOutput = ctx.mForceVectorOutput;
+ mMapToPixel = ctx.mMapToPixel;
+ mRenderingStopped = ctx.mRenderingStopped;
+ mScaleFactor = ctx.mScaleFactor;
+ mRasterScaleFactor = ctx.mRasterScaleFactor;
+ mRendererScale = ctx.mRendererScale;
+ mLabelingEngine = ctx.mLabelingEngine;
+ }
+ return *this;
+}
Modified: branches/threading-branch/src/core/qgsrendercontext.h
===================================================================
--- branches/threading-branch/src/core/qgsrendercontext.h 2010-06-08 12:45:17 UTC (rev 13688)
+++ branches/threading-branch/src/core/qgsrendercontext.h 2010-06-08 13:28:08 UTC (rev 13689)
@@ -80,6 +80,9 @@
//! Added in QGIS v1.4
void setLabelingEngine( QgsLabelingEngineInterface* iface ) { mLabelingEngine = iface; }
+ //! assignment operator - takes care of copying coord. transform if exists
+ QgsRenderContext& operator=( const QgsRenderContext& ctx );
+
private:
/**Painter for rendering operations*/
More information about the QGIS-commit
mailing list