[QGIS Commit] r13728 - in branches/threading-branch/src: app core gui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sun Jun 13 18:06:25 EDT 2010


Author: wonder
Date: 2010-06-13 22:06:25 +0000 (Sun, 13 Jun 2010)
New Revision: 13728

Modified:
   branches/threading-branch/src/app/qgisapp.cpp
   branches/threading-branch/src/core/qgsmaplayer.h
   branches/threading-branch/src/core/qgsmaprenderer.cpp
   branches/threading-branch/src/core/qgsmaprenderer.h
   branches/threading-branch/src/core/qgsvectorlayer.cpp
   branches/threading-branch/src/gui/qgsmapcanvas.cpp
   branches/threading-branch/src/gui/qgsmapcanvas.h
   branches/threading-branch/src/gui/qgsmapcanvasmap.cpp
   branches/threading-branch/src/gui/qgsmapcanvasmap.h
   branches/threading-branch/src/gui/qgsmapoverviewcanvas.cpp
Log:
Added support for asynchronous rendering of maps in QgsMapRenderer (an alternative to the synchronous rendering).
Made QgsMapCanvas use the asynchronous rendering - the map is updated continuously while the rendering is underway.
(things are still very fragile - no locking, gui is only half-functional while rendering etc.)


Modified: branches/threading-branch/src/app/qgisapp.cpp
===================================================================
--- branches/threading-branch/src/app/qgisapp.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/app/qgisapp.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -2883,10 +2883,7 @@
 
 void QgisApp::fileExit()
 {
-  if ( mMapCanvas && mMapCanvas->isDrawing() )
-  {
-    return;
-  }
+  mMapCanvas->cancelRendering();
 
   if ( saveDirty() )
   {
@@ -3640,15 +3637,7 @@
 {
   if ( mMapCanvas )
   {
-    QgsMapRenderer* mypMapRenderer = mMapCanvas->mapRenderer();
-    if ( mypMapRenderer )
-    {
-      QgsRenderContext* mypRenderContext = mypMapRenderer->rendererContext();
-      if ( mypRenderContext )
-      {
-        mypRenderContext->setRenderingStopped( true );
-      }
-    }
+    mMapCanvas->cancelRendering();
   }
 }
 

Modified: branches/threading-branch/src/core/qgsmaplayer.h
===================================================================
--- branches/threading-branch/src/core/qgsmaplayer.h	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/core/qgsmaplayer.h	2010-06-13 22:06:25 UTC (rev 13728)
@@ -341,6 +341,7 @@
 
     /**The layer emits this signal when a screen update is requested.
      This signal should be connected with the slot QgsMapCanvas::updateMap()*/
+    //! @deprecated not used anymore
     void screenUpdateRequested();
 
     /** This is used to send a request that any mapcanvas using this layer update its extents */

Modified: branches/threading-branch/src/core/qgsmaprenderer.cpp
===================================================================
--- branches/threading-branch/src/core/qgsmaprenderer.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/core/qgsmaprenderer.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -42,14 +42,6 @@
 #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 );
 
 /////
@@ -75,10 +67,18 @@
   mOutputUnits = QgsMapRenderer::Millimeters;
 
   mLabelingEngine = NULL;
+
+  //connect(&mFW, SIGNAL(finished()), SLOT(futureFinished()));
 }
 
 QgsMapRenderer::~QgsMapRenderer()
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("canceling the rendering...");
+    cancelThreadedRendering();
+  }
+
   delete mScaleCalculator;
   delete mDistArea;
   delete mDestCRS;
@@ -98,6 +98,12 @@
 
 bool QgsMapRenderer::setExtent( const QgsRectangle& extent )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return false; // do not allow changes while rendering
+  }
+
   //remember the previous extent
   mLastExtent = mExtent;
 
@@ -140,6 +146,12 @@
 
 void QgsMapRenderer::setOutputSize( QSize size, int dpi )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   mSize = size;
   mScaleCalculator->setDpi( dpi );
   adjustExtentToSize();
@@ -195,9 +207,9 @@
     dymax = mExtent.yMaximum() + whitespace;
   }
 
-  QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2\n" ).arg( mapUnitsPerPixelX ).arg( mapUnitsPerPixelY ) );
-  QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2\n" ).arg( myWidth ).arg( myHeight ) );
-  QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2\n" ).arg( mExtent.width() ).arg( mExtent.height() ) );
+  QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( mapUnitsPerPixelX ).arg( mapUnitsPerPixelY ) );
+  QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( myWidth ).arg( myHeight ) );
+  QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( mExtent.width() ).arg( mExtent.height() ) );
   QgsDebugMsg( mExtent.toString() );
 
   // update extent
@@ -217,8 +229,10 @@
 }
 
 
-void QgsMapRenderer::render( QPainter* painter )
+void QgsMapRenderer::initRendering( QPainter* painter, double deviceDpi )
 {
+  mDrawing = true;
+
   //flag to see if the render context has changed
   //since the last time we rendered. If it hasnt changed we can
   //take some shortcuts with rendering
@@ -228,30 +242,9 @@
 
   QgsDebugMsg( "caching enabled? " + QString::number(mCachingEnabled) );
 
-  if ( mExtent.isEmpty() )
-  {
-    QgsDebugMsg( "empty extent... not rendering" );
-    return;
-  }
-
-  if ( mDrawing )
-  {
-    return;
-  }
-
-  QPaintDevice* thePaintDevice = painter->device();
-  if ( !thePaintDevice )
-  {
-    return;
-  }
-
-  mDrawing = true;
-
-
 #ifdef QGISDEBUG
   QgsDebugMsg( "Starting to render layer stack." );
-  QTime renderTime;
-  renderTime.start();
+  mRenderTime.start();
 #endif
 
   mRenderContext.setDrawEditingInformation( !mOverview );
@@ -270,7 +263,7 @@
   {
     scaleFactor = sceneDpi / 25.4;
   }
-  double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
+  double rasterScaleFactor = deviceDpi / sceneDpi;
   if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
   {
     mRenderContext.setRasterScaleFactor( rasterScaleFactor );
@@ -308,13 +301,11 @@
     }
   }
 
-  QgsOverlayObjectPositionManager* overlayManager = overlayManagerFromSettings();
+  mOverlayManager = overlayManagerFromSettings();
+}
 
-  // do the rendering of all layers
-  renderLayers( overlayManager );
-
-  QgsDebugMsg( "Done rendering map layers" );
-
+void QgsMapRenderer::finishRendering()
+{
   // render labels for vector layers (not using PAL)
   if ( !mOverview )
   {
@@ -322,11 +313,11 @@
   }
 
   //find overlay positions and draw the vector overlays
-  if ( overlayManager )
+  if ( mOverlayManager )
   {
-    overlayManager->drawOverlays( mRenderContext, mScaleCalculator->mapUnits() );
-    delete overlayManager;
-    overlayManager = NULL;
+    mOverlayManager->drawOverlays( mRenderContext, mScaleCalculator->mapUnits() );
+    delete mOverlayManager;
+    mOverlayManager = NULL;
   }
 
   // make sure progress bar arrives at 100%!
@@ -342,16 +333,47 @@
     mLabelingEngine->exit();
   }
 
-  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
+  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( mRenderTime.elapsed() / 1000.0 ) );
 
   mDrawing = false;
+}
 
+void QgsMapRenderer::render( QPainter* painter )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return;
+  }
+
+  if ( mExtent.isEmpty() )
+  {
+    QgsDebugMsg( "empty extent... not rendering" );
+    return;
+  }
+
+  QPaintDevice* thePaintDevice = painter->device();
+  if ( !thePaintDevice )
+  {
+    return;
+  }
+
+  double deviceDpi = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0;
+
+  mThreadingEnabled = false;
+
+  initRendering( painter, deviceDpi );
+
+  // do the rendering of all layers
+  renderLayers();
+
+  finishRendering();
 }
 
 
-void QgsMapRenderer::renderLayers( QgsOverlayObjectPositionManager* overlayManager )
+void QgsMapRenderer::renderLayers()
 {
-  QList<ThreadedRenderContext> layers;
+  mThreadedJobs.clear();
 
   // render all layers in the stack, starting at the base
   QListIterator<QString> li( mLayerSet );
@@ -396,12 +418,12 @@
     mRenderContext.setCoordinateTransform( ct );
 
     //create overlay objects for features within the view extent
-    if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager )
+    if ( ml->type() == QgsMapLayer::VectorLayer && mOverlayManager )
     {
       QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
       if ( vl )
       {
-        overlayManager->addOverlaysForLayer( vl, mRenderContext );
+        mOverlayManager->addOverlaysForLayer( vl, mRenderContext );
       }
     }
 
@@ -426,7 +448,7 @@
     if ( !mThreadingEnabled )
       renderLayerNoThreading( ml );
     else
-      renderLayerThreading( ml, layers );
+      renderLayerThreading( ml );
 
     disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
   }
@@ -435,23 +457,111 @@
   {
     QgsDebugMsg("STARTING THREADED RENDERING!");
 
-    QtConcurrent::blockingMap(layers, _renderLayerThreading);
+    //QtConcurrent::blockingMap(layers, _renderLayerThreading);
 
-    QgsDebugMsg("THREADED RENDERING FINISHED!");
+    connect(&mFW, SIGNAL(finished()), SLOT(futureFinished()));
 
-    // 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;
-    }
+    mFuture = QtConcurrent::map(mThreadedJobs, _renderLayerThreading);
+    mFW.setFuture(mFuture);
+  }
 
-    QgsDebugMsg("THREADED RENDERING DONE!");
+}
+
+QImage QgsMapRenderer::threadedRenderingOutput()
+{
+  QImage i( mSize, QImage::Format_ARGB32_Premultiplied );
+  i.fill(0);
+  QPainter p;
+  p.begin(&i);
+  foreach (const ThreadedRenderContext& tctx, mThreadedJobs)
+  {
+    if (tctx.img == 0)
+      return QImage(); // destroyed already
+    p.drawImage( 0, 0, *tctx.img );
   }
+  p.end();
+  return i;
+}
 
+void QgsMapRenderer::futureFinished()
+{
+  QgsDebugMsg("THREADED RENDERING FINISHED!");
+
+  QImage i = threadedRenderingOutput();
+
+  // delete temporary painters
+  foreach (ThreadedRenderContext tctx, mThreadedJobs)
+  {
+    delete tctx.ctx.painter();
+    delete tctx.img;
+    tctx.img = 0;
+  }
+  mThreadedJobs.clear();
+
+  QgsDebugMsg("THREADED RENDERING DONE!");
+
+  disconnect(&mFW, SIGNAL(finished()), this, SLOT(futureFinished()));
+
+  QPainter painter;
+  painter.begin(&i);
+  mRenderContext.setPainter(&painter);
+
+  // postprocessing
+  finishRendering();
+
+  painter.end();
+
+  emit finishedThreadedRendering(i);
 }
 
+void QgsMapRenderer::startThreadedRendering()
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  if ( mExtent.isEmpty() )
+  {
+    QgsDebugMsg( "empty extent... not rendering" );
+    return;
+  }
+
+  mThreadingEnabled = true;
+  initRendering(NULL, 96); // TODO: what dpi to use?
+
+  renderLayers();
+}
+
+
+void QgsMapRenderer::cancelThreadedRendering()
+{
+  if (!mDrawing)
+  {
+    QgsDebugMsg("nothing to cancel.");
+    return;
+  }
+
+  QgsDebugMsg("canceling render");
+  for (int i = 0; i < mThreadedJobs.count(); i++)
+  {
+    mThreadedJobs[i].ctx.setRenderingStopped(true);
+  }
+
+  disconnect(&mFW, SIGNAL(finished()), this, SLOT(futureFinished()));
+
+  mFuture.cancel();
+  QTime t;
+  t.start();
+  mFW.waitForFinished();
+
+  QgsDebugMsg(QString("waited %1 ms").arg(t.elapsed() / 1000.0));
+
+  futureFinished(); // manually trigger the routines for finalization
+}
+
+
 void _renderLayerThreading( ThreadedRenderContext& tctx )
 {
   QgsDebugMsg("threaded rendering start: "+tctx.ml->getLayerID());
@@ -461,7 +571,7 @@
   QgsDebugMsg("threaded rendering end  : "+tctx.ml->getLayerID());
 }
 
-void QgsMapRenderer::renderLayerThreading( QgsMapLayer* ml, QList<ThreadedRenderContext>& lst )
+void QgsMapRenderer::renderLayerThreading( QgsMapLayer* ml )
 {
   if ( mCachingEnabled && ml->cacheImage() != 0 )
   {
@@ -474,8 +584,7 @@
     tctx.ml = ml;
 
     // create image
-    QPaintDevice* device = mRenderContext.painter()->device();
-    tctx.img = new QImage( device->width(), device->height(), QImage::Format_ARGB32_Premultiplied );
+    tctx.img = new QImage( mSize, QImage::Format_ARGB32_Premultiplied );
     tctx.img->fill( 0 );
 
     // create private context
@@ -483,14 +592,11 @@
     tctx.ctx = mRenderContext;
 
     QPainter* painter = new QPainter(tctx.img);
-    if ( mRenderContext.painter()->testRenderHint( QPainter::Antialiasing ) )
-    {
-      painter->setRenderHint( QPainter::Antialiasing );
-    }
+    painter->setRenderHint( QPainter::Antialiasing, mAntialiasingEnabled );
     tctx.ctx.setPainter( painter );
 
     // schedule DRAW to a list
-    lst.append(tctx);
+    mThreadedJobs.append(tctx);
   }
 }
 
@@ -513,10 +619,7 @@
 
       // alter painter
       QPainter * mypPainter = new QPainter( ml->cacheImage() );
-      if ( mypContextPainter->testRenderHint( QPainter::Antialiasing ) )
-      {
-        mypPainter->setRenderHint( QPainter::Antialiasing );
-      }
+      mypPainter->setRenderHint( QPainter::Antialiasing, mAntialiasingEnabled );
       mRenderContext.setPainter( mypPainter );
 
       // DRAW!
@@ -660,6 +763,12 @@
 
 void QgsMapRenderer::setMapUnits( QGis::UnitType u )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   mScaleCalculator->setMapUnits( u );
 
   // Since the map units have changed, force a recalculation of the scale.
@@ -684,6 +793,12 @@
 
 void QgsMapRenderer::setProjectionsEnabled( bool enabled )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   if ( mProjectionsEnabled != enabled )
   {
     mProjectionsEnabled = enabled;
@@ -701,6 +816,12 @@
 
 void QgsMapRenderer::setDestinationSrs( const QgsCoordinateReferenceSystem& srs )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   QgsDebugMsg( "* Setting destCRS : = " + srs.toProj4() );
   QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( srs.srsid() ) );
   if ( *mDestCRS != srs )
@@ -936,6 +1057,12 @@
 
 void QgsMapRenderer::setLayerSet( const QStringList& layers )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   mLayerSet = layers;
   updateFullExtent();
 }
@@ -1124,9 +1251,81 @@
 
 void QgsMapRenderer::setLabelingEngine( QgsLabelingEngineInterface* iface )
 {
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
   if ( mLabelingEngine )
     delete mLabelingEngine;
 
   mLabelingEngine = iface;
 }
 
+
+void QgsMapRenderer::setScale( double scale )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mScale = scale;
+}
+
+void QgsMapRenderer::enableOverviewMode( bool isOverview )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mOverview = isOverview;
+}
+
+void QgsMapRenderer::setOutputUnits( OutputUnits u )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mOutputUnits = u;
+}
+
+void QgsMapRenderer::setThreadingEnabled( bool use )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mThreadingEnabled = use;
+}
+
+void QgsMapRenderer::setCachingEnabled( bool enabled )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mCachingEnabled = enabled;
+}
+
+void QgsMapRenderer::setAntialiasingEnabled( bool enabled )
+{
+  if ( mDrawing )
+  {
+    QgsDebugMsg("Ignored --- drawing now!");
+    return; // do not allow changes while rendering
+  }
+
+  mAntialiasingEnabled = enabled;
+}

Modified: branches/threading-branch/src/core/qgsmaprenderer.h
===================================================================
--- branches/threading-branch/src/core/qgsmaprenderer.h	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/core/qgsmaprenderer.h	2010-06-13 22:06:25 UTC (rev 13728)
@@ -19,6 +19,10 @@
 
 #include <QSize>
 #include <QStringList>
+#include <QFuture>
+#include <QFutureWatcher>
+#include <QTime>
+#include <QImage>
 
 #include "qgis.h"
 #include "qgsrectangle.h"
@@ -38,8 +42,16 @@
 class QgsVectorLayer;
 class QgsFeature;
 
-struct ThreadedRenderContext;
 
+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;
+
+
 /** Labeling engine interface.
  * \note Added in QGIS v1.4
  */
@@ -104,7 +116,7 @@
     double scale() const { return mScale; }
     /**Sets scale for scale based visibility. Normally, the scale is calculated automatically. This
      function is only used to force a preview scale (e.g. for print composer)*/
-    void setScale( double scale ) {mScale = scale;}
+    void setScale( double scale );
     double mapUnitsPerPixel() const { return mMapUnitsPerPixel; }
 
     int width() const { return mSize.width(); };
@@ -119,7 +131,7 @@
     void setMapUnits( QGis::UnitType u );
 
     //! sets whether map image will be for overview
-    void enableOverviewMode( bool isOverview = true ) { mOverview = isOverview; }
+    void enableOverviewMode( bool isOverview = true );
 
     void setOutputSize( QSize size, int dpi );
 
@@ -152,7 +164,7 @@
     //! returns CRS ID of destination spatial reference system
     const QgsCoordinateReferenceSystem& destinationSrs();
 
-    void setOutputUnits( OutputUnits u ) {mOutputUnits = u;}
+    void setOutputUnits( OutputUnits u );
 
     OutputUnits outputUnits() const {return mOutputUnits;}
 
@@ -188,7 +200,7 @@
 
     //! Enable or disable rendering in multiple threads on multiprocessor computers
     //! Added in QGIS v1.6
-    void setThreadingEnabled( bool use ) { mThreadingEnabled = use; }
+    void setThreadingEnabled( bool use );
 
     //! Determine whether we are using threaded rendering
     //! Added in QGIS v1.6
@@ -196,14 +208,46 @@
 
     //! Enable or disable caching of rendered layers
     //! Added in QGIS v1.6
-    void setCachingEnabled( bool enabled ) { mCachingEnabled = enabled; }
+    void setCachingEnabled( bool enabled );
 
     //! Determine whether the rendered layers are cached
     //! Added in QGIS v1.6
     bool isCachingEnabled() const { return mCachingEnabled; }
 
+    //! Added in QGIS v1.6
+    void setAntialiasingEnabled( bool enabled );
+
+    //! Added in QGIS v1.6
+    bool isAntialiasingEnabled() const { return mAntialiasingEnabled; }
+
+    //! Schedule a redraw of the layers.
+    //! This function returns immediately after starting the asynchronous rendering process.
+    //! Any previous rendering operations must be finished/canceled first.
+    //! Added in QGIS v1.6
+    void startThreadedRendering();
+
+    //! Cancel pending asynchronous rendering. Waits until the rendering is canceled.
+    //! Does nothing if no asynchronous rendering is running.
+    //! Added in QGIS v1.6
+    void cancelThreadedRendering();
+
+    //! Return the intermediate output of the asynchronous rendering.
+    //! An invalid image is returned when the rendering has finished.
+    //! (to obtain the final output image, use finishedThreadedRendering signal)
+    //! Added in QGIS v1.6
+    QImage threadedRenderingOutput();
+
+    //! Tell whether the rendering takes place
+    //! Added in QGIS v1.6
+    bool isDrawing() const { return mDrawing; }
+
   signals:
 
+    //! emitted when asynchronous rendering is finished (or canceled).
+    //! The passed image is the result of the rendering process.
+    //! Added in QGIS v1.6
+    void finishedThreadedRendering(QImage i);
+
     void drawingProgress( int current, int total );
 
     void hasCrsTransformEnabled( bool flag );
@@ -222,6 +266,8 @@
     //! called by signal from layer current being drawn
     void onDrawingProgress( int current, int total );
 
+    void futureFinished();
+
   protected:
 
     //! adjust extent to fit the pixmap size
@@ -235,14 +281,20 @@
      */
     bool splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 );
 
+    //! initialize the rendering process
+    void initRendering( QPainter* painter, double deviceDpi );
+
+    //! finalize the rendering process (labeling, overlays)
+    void finishRendering();
+
     //! render the whole layer set
-    void renderLayers( QgsOverlayObjectPositionManager* overlayManager );
+    void renderLayers();
 
     //! render one layer
     void renderLayerNoThreading( QgsMapLayer* ml );
 
     //! schedule rendering of a layer
-    void renderLayerThreading( QgsMapLayer* ml, QList<ThreadedRenderContext>& lst );
+    void renderLayerThreading( QgsMapLayer* ml );
 
     //! invoke rendering of the layer with given context
     bool renderLayer( QgsMapLayer* ml, QgsRenderContext& ctx );
@@ -300,9 +352,6 @@
     //! tool for measuring
     QgsDistanceArea* mDistArea;
 
-    //!Encapsulates context of rendering
-    QgsRenderContext mRenderContext;
-
     //!Output units
     OutputUnits mOutputUnits;
 
@@ -314,6 +363,18 @@
 
     //! Render caching
     bool mCachingEnabled;
+
+    bool mAntialiasingEnabled;
+
+    QFuture<void> mFuture;
+    QFutureWatcher<void> mFW;
+
+    // variables valid only while rendering:
+    QTime mRenderTime;
+    QgsOverlayObjectPositionManager* mOverlayManager;
+    //!Encapsulates context of rendering
+    QgsRenderContext mRenderContext;
+    QList<ThreadedRenderContext> mThreadedJobs;
 };
 
 #endif

Modified: branches/threading-branch/src/core/qgsvectorlayer.cpp
===================================================================
--- branches/threading-branch/src/core/qgsvectorlayer.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/core/qgsvectorlayer.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -705,10 +705,6 @@
 
   mRendererV2->startRender( rendererContext, this );
 
-#ifndef Q_WS_MAC
-  int featureCount = 0;
-#endif //Q_WS_MAC
-
   QgsFeature fet;
   while ( nextFeature( fet ) )
   {
@@ -719,20 +715,6 @@
         break;
       }
 
-#ifndef Q_WS_MAC //MH: disable this on Mac for now to avoid problems with resizing
-      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();
-      }
-#endif //Q_WS_MAC
-
       bool sel = mSelectedFeatureIds.contains( fet.id() );
       bool drawMarker = ( mEditable && ( !vertexMarkerOnlyForSelection || sel ) );
 
@@ -754,9 +736,6 @@
       QgsDebugMsg( QString( "Failed to transform a point while drawing a feature of type '%1'. Ignoring this feature. %2" )
                    .arg( fet.typeName() ).arg( cse.what() ) );
     }
-#ifndef Q_WS_MAC
-    ++featureCount;
-#endif //Q_WS_MAC
   }
 }
 
@@ -781,9 +760,6 @@
 
   // 1. fetch features
   QgsFeature fet;
-#ifndef Q_WS_MAC
-  int featureCount = 0;
-#endif //Q_WS_MAC
   while ( nextFeature( fet ) )
   {
     if ( rendererContext.renderingStopped() )
@@ -791,12 +767,6 @@
       stopRendererV2( rendererContext, selRenderer );
       return;
     }
-#ifndef Q_WS_MAC
-    if ( featureCount % 1000 == 0 )
-    {
-      qApp->processEvents();
-    }
-#endif //Q_WS_MAC
     QgsSymbolV2* sym = mRendererV2->symbolForFeature( fet );
     if ( !features.contains( sym ) )
     {
@@ -812,9 +782,6 @@
       // Cache this for the use of (e.g.) modifying the feature's uncommitted geometry.
       mCachedGeometries[fet.id()] = *fet.geometry();
     }
-#ifndef Q_WS_MAC
-    ++featureCount;
-#endif //Q_WS_MAC
   }
 
   // find out the order
@@ -848,9 +815,6 @@
       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() )
@@ -858,12 +822,6 @@
           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 = ( mEditable && ( !vertexMarkerOnlyForSelection || sel ) );
@@ -877,9 +835,6 @@
           QgsDebugMsg( QString( "Failed to transform a point while drawing a feature of type '%1'. Ignoring this feature. %2" )
                        .arg( fet.typeName() ).arg( cse.what() ) );
         }
-#ifndef Q_WS_MAC
-        ++featureCount;
-#endif //Q_WS_MAC
       }
     }
   }
@@ -998,22 +953,6 @@
           break;
         }
 
-#ifndef Q_WS_MAC //MH: disable this on Mac for now to avoid problems with resizing
-        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();
-        }
-// #else
-//         Q_UNUSED( totalFeatures );
-#endif //Q_WS_MAC
-
         // check if feature is selected
         // only show selections of the current layer
         // TODO: create a mechanism to let layer know whether it's current layer or not [MD]

Modified: branches/threading-branch/src/gui/qgsmapcanvas.cpp
===================================================================
--- branches/threading-branch/src/gui/qgsmapcanvas.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/gui/qgsmapcanvas.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -91,7 +91,6 @@
   mMapTool = NULL;
   mLastNonZoomMapTool = NULL;
 
-  mDrawing = false;
   mFrozen = false;
   mDirty = true;
 
@@ -104,6 +103,7 @@
   setFocusPolicy( Qt::StrongFocus );
 
   mMapRenderer = new QgsMapRenderer;
+  connect(mMapRenderer, SIGNAL(finishedThreadedRendering(QImage)), SLOT(renderingFinished(QImage)));
 
   // create map canvas item which will show the map
   mMap = new QgsMapCanvasMap( this );
@@ -112,7 +112,7 @@
 
   moveCanvasContents( true );
 
-  //connect(mMapRenderer, SIGNAL(updateMap()), this, SLOT(updateMap()));
+  connect( &mMapUpdateTimer, SIGNAL(timeout()), this, SLOT(updateMap()));
   connect( mMapRenderer, SIGNAL( drawError( QgsMapLayer* ) ), this, SLOT( showError( QgsMapLayer* ) ) );
 
   // project handling
@@ -126,6 +126,8 @@
 
 QgsMapCanvas::~QgsMapCanvas()
 {
+  cancelRendering();
+
   if ( mMapTool )
   {
     mMapTool->deactivate();
@@ -155,7 +157,8 @@
 
 void QgsMapCanvas::enableAntiAliasing( bool theFlag )
 {
-  mMap->enableAntiAliasing( theFlag );
+  mMapRenderer->setAntialiasingEnabled( theFlag );
+
   if ( mMapOverview )
     mMapOverview->enableAntiAliasing( theFlag );
 } // anti aliasing
@@ -211,7 +214,7 @@
 
 bool QgsMapCanvas::isDrawing()
 {
-  return mDrawing;
+  return mMapRenderer->isDrawing();
 } // isDrawing
 
 
@@ -224,7 +227,7 @@
 
 void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -264,7 +267,6 @@
       // 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 )
       {
@@ -280,7 +282,6 @@
       // 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 )
       {
@@ -359,47 +360,37 @@
 
 void QgsMapCanvas::refresh()
 {
-  // we can't draw again if already drawing...
-  if ( mDrawing )
+  if ( !mRenderFlag || mFrozen )
     return;
 
-  mDrawing = true;
+  cancelRendering();
 
-  if ( mRenderFlag && !mFrozen )
-  {
-    clear();
+  clear();
 
-    // Tell the user we're going to be a while
-    QApplication::setOverrideCursor( Qt::WaitCursor );
+  // Tell the user we're going to be a while
+  //QApplication::setOverrideCursor( Qt::WaitCursor );
 
-    emit renderStarting();
+  emit renderStarting();
 
-    mMap->render();
+  // TRIGGER RENDERING
+  //mMap->render();
+  qDebug("STARTING \n\n\n\n\n");
+  mMapRenderer->startThreadedRendering();
 
-    mDirty = false;
+  mMapUpdateTimer.start(250);
 
-    // notify any listeners that rendering is complete
-    QPainter p;
-    p.begin( &mMap->paintDevice() );
-    emit renderComplete( &p );
-    p.end();
+  updateMap();
 
-    // notifies current map tool
-    if ( mMapTool )
-      mMapTool->renderComplete();
-
-    // Tell the user we've finished going to be a while
-    QApplication::restoreOverrideCursor();
-  }
-
-  mDrawing = false;
 } // refresh
 
 void QgsMapCanvas::updateMap()
 {
-  if ( mMap )
+  QgsDebugMsg("updating map!");
+  QImage i = mMapRenderer->threadedRenderingOutput();
+  if (!i.isNull())
   {
-    mMap->updateContents();
+    mMap->setMap(i);
+    mMap->update();
   }
 }
 
@@ -482,7 +473,7 @@
 
 void QgsMapCanvas::setExtent( QgsRectangle const & r )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -551,7 +542,7 @@
 
 void QgsMapCanvas::zoomToFullExtent()
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -572,7 +563,7 @@
 
 void QgsMapCanvas::zoomToPreviousExtent()
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -595,7 +586,7 @@
 
 void QgsMapCanvas::zoomToNextExtent()
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -650,7 +641,7 @@
 
 void QgsMapCanvas::zoomToSelected( QgsVectorLayer* layer )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -698,7 +689,7 @@
 void QgsMapCanvas::keyPressEvent( QKeyEvent * e )
 {
 
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     e->ignore();
   }
@@ -799,7 +790,7 @@
 {
   QgsDebugMsg( "keyRelease event" );
 
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -835,7 +826,7 @@
 
 void QgsMapCanvas::mouseDoubleClickEvent( QMouseEvent * e )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -848,10 +839,6 @@
 
 void QgsMapCanvas::mousePressEvent( QMouseEvent * e )
 {
-  if ( mDrawing )
-  {
-    return;
-  }
 
   //use middle mouse button for panning, map tools won't receive any events in that case
   if ( e->button() == Qt::MidButton )
@@ -880,11 +867,6 @@
 
 void QgsMapCanvas::mouseReleaseEvent( QMouseEvent * e )
 {
-  if ( mDrawing )
-  {
-    return;
-  }
-
   //use middle mouse button for panning, map tools won't receive any events in that case
   if ( e->button() == Qt::MidButton )
   {
@@ -925,25 +907,11 @@
 
 void QgsMapCanvas::resizeEvent( QResizeEvent * e )
 {
-  static bool isAlreadyIn = false;
   static QSize lastSize = QSize( -1, -1 );
 
   lastSize = e->size();
 
-  if ( isAlreadyIn || mDrawing )
-  {
-    //cancel current render progress
-    if ( mMapRenderer )
-    {
-      QgsRenderContext* theRenderContext = mMapRenderer->rendererContext();
-      if ( theRenderContext )
-      {
-        theRenderContext->setRenderingStopped( true );
-      }
-    }
-    return;
-  }
-  isAlreadyIn = true;
+  cancelRendering();
 
   while ( lastSize != QSize( -1, -1 ) )
   {
@@ -968,7 +936,6 @@
 #endif
     emit extentsChanged();
   }
-  isAlreadyIn = false;
 } // resizeEvent
 
 
@@ -997,10 +964,7 @@
 
   QgsDebugMsg( "Wheel event delta " + QString::number( e->delta() ) );
 
-  if ( mDrawing )
-  {
-    return;
-  }
+  cancelRendering();
 
   switch ( mWheelAction )
   {
@@ -1064,7 +1028,7 @@
 
 void QgsMapCanvas::zoomWithCenter( int x, int y, bool zoomIn )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -1081,11 +1045,6 @@
 
 void QgsMapCanvas::mouseMoveEvent( QMouseEvent * e )
 {
-  if ( mDrawing )
-  {
-    return;
-  }
-
   mCanvasProperties->mouseLastXY = e->pos();
 
   if ( mCanvasProperties->panSelectorDown )
@@ -1245,19 +1204,15 @@
 void QgsMapCanvas::setRenderFlag( bool theFlag )
 {
   mRenderFlag = theFlag;
-  if ( mMapRenderer )
-  {
-    QgsRenderContext* rc = mMapRenderer->rendererContext();
-    if ( rc )
-    {
-      rc->setRenderingStopped( !theFlag );
-    }
-  }
 
   if ( mRenderFlag )
   {
     refresh();
   }
+  else
+  {
+    cancelRendering();
+  }
 }
 
 void QgsMapCanvas::connectNotify( const char * signal )
@@ -1274,7 +1229,7 @@
 
 void QgsMapCanvas::panActionEnd( QPoint releasePoint )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -1322,24 +1277,14 @@
 
 void QgsMapCanvas::panAction( QMouseEvent * e )
 {
-  if ( mDrawing )
-  {
-    return;
-  }
+  cancelRendering();
 
   // move all map canvas items
   moveCanvasContents();
-
-  // update canvas
-  //updateContents(); // TODO: need to update?
 }
 
 void QgsMapCanvas::moveCanvasContents( bool reset )
 {
-  if ( mDrawing )
-  {
-    return;
-  }
 
   QPoint pnt( 0, 0 );
   if ( !reset )
@@ -1431,7 +1376,7 @@
 
 void QgsMapCanvas::zoomByFactor( double scaleFactor )
 {
-  if ( mDrawing )
+  if ( isDrawing() )
   {
     return;
   }
@@ -1449,3 +1394,35 @@
   emit selectionChanged( layer );
   refresh();
 }
+
+void QgsMapCanvas::renderingFinished(QImage img)
+{
+  QgsDebugMsg("finished!!!");
+  mMapUpdateTimer.stop();
+
+  mDirty = false;
+
+  // notify any listeners that rendering is complete
+  QPainter p;
+  p.begin( &img );
+  emit renderComplete( &p );
+  p.end();
+
+  mMap->setMap(img);
+  mMap->update();
+
+  // notifies current map tool
+  if ( mMapTool )
+    mMapTool->renderComplete();
+
+  // Tell the user we've finished going to be a while
+  //QApplication::restoreOverrideCursor();
+}
+
+void QgsMapCanvas::cancelRendering()
+{
+  if ( isDrawing() )
+  {
+    mMapRenderer->cancelThreadedRendering();
+  }
+}

Modified: branches/threading-branch/src/gui/qgsmapcanvas.h
===================================================================
--- branches/threading-branch/src/gui/qgsmapcanvas.h	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/gui/qgsmapcanvas.h	2010-06-13 22:06:25 UTC (rev 13728)
@@ -30,6 +30,7 @@
 #include <QDomDocument>
 #include <QGraphicsView>
 #include <QtCore>
+#include <QTimer>
 
 class QWheelEvent;
 class QPixmap;
@@ -255,7 +256,12 @@
     //! returns last position of mouse cursor
     QPoint mouseLastXY();
 
+    //! force stop of the rendering process
+    void cancelRendering();
+
   public slots:
+    //! Called when asynchronous rendering is finished
+    void renderingFinished(QImage img);
 
     /**Repaints the canvas map*/
     void refresh();
@@ -400,7 +406,7 @@
     QgsMapOverviewCanvas* mMapOverview;
 
     //! Flag indicating a map refresh is in progress
-    bool mDrawing;
+    //bool mDrawing;
 
     //! Flag indicating if the map canvas is frozen.
     bool mFrozen;
@@ -451,6 +457,8 @@
     //! Mouse wheel action
     WheelAction mWheelAction;
 
+    QTimer mMapUpdateTimer;
+
 }; // class QgsMapCanvas
 
 

Modified: branches/threading-branch/src/gui/qgsmapcanvasmap.cpp
===================================================================
--- branches/threading-branch/src/gui/qgsmapcanvasmap.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/gui/qgsmapcanvasmap.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -28,6 +28,7 @@
   setPos( 0, 0 );
   resize( QSize( 1, 1 ) );
   mUseQImageToRender = false;
+
 }
 
 void QgsMapCanvasMap::paint( QPainter* p, const QStyleOptionGraphicsItem*, QWidget* )
@@ -47,7 +48,8 @@
   prepareGeometryChange(); // to keep QGraphicsScene indexes up to date on size change
 
   mPixmap = QPixmap( size );
-  mImage = QImage( size, QImage::Format_RGB32 ); // temporary image - build it here so it is available when switching from QPixmap to QImage rendering
+  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() );
 }
 
@@ -59,6 +61,7 @@
 
 void QgsMapCanvasMap::render()
 {
+  /*
   // Rendering to a QImage gives incorrectly filled polygons in some
   // cases (as at Qt4.1.4), but it is the only renderer that supports
   // anti-aliasing, so we provide the means to swap between QImage and
@@ -101,6 +104,7 @@
     paint.end();
   }
   update();
+  */
 }
 
 QPaintDevice& QgsMapCanvasMap::paintDevice()
@@ -110,10 +114,12 @@
 
 void QgsMapCanvasMap::updateContents()
 {
+  /*
   // make sure we're using current contents
   if ( mUseQImageToRender )
     mPixmap = QPixmap::fromImage( mImage );
 
   // trigger update of this item
   update();
+  */
 }

Modified: branches/threading-branch/src/gui/qgsmapcanvasmap.h
===================================================================
--- branches/threading-branch/src/gui/qgsmapcanvasmap.h	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/gui/qgsmapcanvasmap.h	2010-06-13 22:06:25 UTC (rev 13728)
@@ -42,6 +42,7 @@
     void useImageToRender( bool flag ) { mUseQImageToRender = flag; }
 
     //! renders map using QgsMapRenderer to mPixmap
+    //! @deprecated does nothing
     void render();
 
     void setBackgroundColor( const QColor& color ) { mBgColor = color; }
@@ -60,8 +61,11 @@
 
     //! Update contents - can be called while drawing to show the status.
     //! Added in version 1.2
+    //! @deprecated does nothing
     void updateContents();
 
+    void setMap(QImage img) { mPixmap = QPixmap::fromImage(img); }
+
   private:
 
     //! indicates whether antialiasing will be used for rendering
@@ -71,7 +75,7 @@
     bool mUseQImageToRender;
 
     QPixmap mPixmap;
-    QImage mImage;
+    //QImage mImage;
 
     //QgsMapRenderer* mRender;
     QgsMapCanvas* mCanvas;

Modified: branches/threading-branch/src/gui/qgsmapoverviewcanvas.cpp
===================================================================
--- branches/threading-branch/src/gui/qgsmapoverviewcanvas.cpp	2010-06-13 20:33:14 UTC (rev 13727)
+++ branches/threading-branch/src/gui/qgsmapoverviewcanvas.cpp	2010-06-13 22:06:25 UTC (rev 13728)
@@ -251,8 +251,7 @@
   painter.begin( &mPixmap );
 
   // antialiasing
-  if ( mAntiAliasing )
-    painter.setRenderHint( QPainter::Antialiasing );
+  mMapRenderer->setAntialiasingEnabled( mAntiAliasing );
 
   // render image
   mMapRenderer->render( &painter );



More information about the QGIS-commit mailing list