[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