QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsmaprenderercustompainterjob.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprenderercustompainterjob.cpp
3  --------------------------------------
4  Date : December 2013
5  Copyright : (C) 2013 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgslogger.h"
19 #include "qgsmaplayerregistry.h"
20 #include "qgsmaplayerrenderer.h"
21 #include "qgspallabeling.h"
22 
23 
25  : QgsMapRendererJob( settings )
26  , mPainter( painter )
27  , mLabelingEngine( 0 )
28  , mActive( false )
29  , mRenderSynchronously( false )
30 {
31  QgsDebugMsg( "QPAINTER construct" );
32 }
33 
35 {
36  QgsDebugMsg( "QPAINTER destruct" );
37  Q_ASSERT( !mFutureWatcher.isRunning() );
38  //cancel();
39 
40  delete mLabelingEngine;
41  mLabelingEngine = 0;
42 }
43 
45 {
46  if ( isActive() )
47  return;
48 
49  mRenderingStart.start();
50 
51  mActive = true;
52 
53  mErrors.clear();
54 
55  QgsDebugMsg( "QPAINTER run!" );
56 
57  QgsDebugMsg( "Preparing list of layer jobs for rendering" );
58  QTime prepareTime;
59  prepareTime.start();
60 
61  // clear the background
62  mPainter->fillRect( 0, 0, mSettings.outputSize().width(), mSettings.outputSize().height(), mSettings.backgroundColor() );
63 
64  mPainter->setRenderHint( QPainter::Antialiasing, mSettings.testFlag( QgsMapSettings::Antialiasing ) );
65 
66  QPaintDevice* thePaintDevice = mPainter->device();
67 
68  QString errMsg = QString( "pre-set DPI not equal to painter's DPI (%1 vs %2)" ).arg( thePaintDevice->logicalDpiX() ).arg( mSettings.outputDpi() );
69  Q_ASSERT_X( thePaintDevice->logicalDpiX() == mSettings.outputDpi(), "Job::startRender()", errMsg.toAscii().data() );
70 
71  delete mLabelingEngine;
72  mLabelingEngine = 0;
73 
75  {
76  mLabelingEngine = new QgsPalLabeling;
77  mLabelingEngine->loadEngineSettings();
78  mLabelingEngine->init( mSettings );
79  }
80 
81  mLayerJobs = prepareJobs( mPainter, mLabelingEngine );
82 
83  QgsDebugMsg( "Rendering prepared in (seconds): " + QString( "%1" ).arg( prepareTime.elapsed() / 1000.0 ) );
84 
85  if ( mRenderSynchronously )
86  {
87  // do the rendering right now!
88  doRender();
89  return;
90  }
91 
92  // now we are ready to start rendering!
93  connect( &mFutureWatcher, SIGNAL( finished() ), SLOT( futureFinished() ) );
94 
95  mFuture = QtConcurrent::run( staticRender, this );
96  mFutureWatcher.setFuture( mFuture );
97 }
98 
99 
101 {
102  if ( !isActive() )
103  {
104  QgsDebugMsg( "QPAINTER not running!" );
105  return;
106  }
107 
108  QgsDebugMsg( "QPAINTER cancelling" );
109  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
110 
111  mLabelingRenderContext.setRenderingStopped( true );
112  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
113  {
114  it->context.setRenderingStopped( true );
115  }
116 
117  QTime t;
118  t.start();
119 
120  mFutureWatcher.waitForFinished();
121 
122  QgsDebugMsg( QString( "QPAINER cancel waited %1 ms" ).arg( t.elapsed() / 1000.0 ) );
123 
124  futureFinished();
125 
126  QgsDebugMsg( "QPAINTER cancelled" );
127 }
128 
130 {
131  if ( !isActive() )
132  return;
133 
134  disconnect( &mFutureWatcher, SIGNAL( finished() ), this, SLOT( futureFinished() ) );
135 
136  QTime t;
137  t.start();
138 
139  mFutureWatcher.waitForFinished();
140 
141  QgsDebugMsg( QString( "waitForFinished: %1 ms" ).arg( t.elapsed() / 1000.0 ) );
142 
143  futureFinished();
144 }
145 
147 {
148  return mActive;
149 }
150 
151 
153 {
154  return mLabelingEngine ? mLabelingEngine->takeResults() : 0;
155 }
156 
157 
158 void QgsMapRendererCustomPainterJob::waitForFinishedWithEventLoop( QEventLoop::ProcessEventsFlags flags )
159 {
160  QEventLoop loop;
161  connect( &mFutureWatcher, SIGNAL( finished() ), &loop, SLOT( quit() ) );
162  loop.exec( flags );
163 }
164 
165 
167 {
168  mRenderSynchronously = true;
169  start();
170  futureFinished();
171  mRenderSynchronously = false;
172 }
173 
174 
176 {
177  mActive = false;
178  mRenderingTime = mRenderingStart.elapsed();
179  QgsDebugMsg( "QPAINTER futureFinished" );
180 
181  // final cleanup
182  cleanupJobs( mLayerJobs );
183 
184  emit finished();
185 }
186 
187 
189 {
190  try
191  {
192  self->doRender();
193  }
194  catch ( QgsException & e )
195  {
196  QgsDebugMsg( "Caught unhandled QgsException: " + e.what() );
197  }
198  catch ( std::exception & e )
199  {
200  QgsDebugMsg( "Caught unhandled std::exception: " + QString::fromAscii( e.what() ) );
201  }
202  catch ( ... )
203  {
204  QgsDebugMsg( "Caught unhandled unknown exception" );
205  }
206 }
207 
209 {
210  QgsDebugMsg( "Starting to render layer stack." );
211  QTime renderTime;
212  renderTime.start();
213 
214  for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
215  {
216  LayerRenderJob& job = *it;
217 
218  if ( job.context.renderingStopped() )
219  break;
220 
221  if ( job.context.useAdvancedEffects() )
222  {
223  // Set the QPainter composition mode so that this layer is rendered using
224  // the desired blending mode
225  mPainter->setCompositionMode( job.blendMode );
226  }
227 
228  if ( !job.cached )
229  job.renderer->render();
230 
231  if ( job.img )
232  {
233  // If we flattened this layer for alternate blend modes, composite it now
234  mPainter->drawImage( 0, 0, *job.img );
235  }
236 
237  }
238 
239  QgsDebugMsg( "Done rendering map layers" );
240 
241  if ( mSettings.testFlag( QgsMapSettings::DrawLabeling ) && !mLabelingRenderContext.renderingStopped() )
242  drawLabeling( mSettings, mLabelingRenderContext, mLabelingEngine, mPainter );
243 
244  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
245 }
246 
247 
248 void QgsMapRendererJob::drawLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine, QPainter* painter )
249 {
250  QgsDebugMsg( "Draw labeling start" );
251 
252  QTime t;
253  t.start();
254 
255  // Reset the composition mode before rendering the labels
256  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
257 
258  // TODO: this is not ideal - we could override rendering stopped flag that has been set in meanwhile
259  renderContext = QgsRenderContext::fromMapSettings( settings );
260  renderContext.setPainter( painter );
261  renderContext.setLabelingEngine( labelingEngine );
262 
263  // old labeling - to be removed at some point...
264  drawOldLabeling( settings, renderContext );
265 
266  drawNewLabeling( settings, renderContext, labelingEngine );
267 
268  QgsDebugMsg( QString( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ) );
269 }
270 
271 
273 {
274  // render all labels for vector layers in the stack, starting at the base
275  QListIterator<QString> li( settings.layers() );
276  li.toBack();
277  while ( li.hasPrevious() )
278  {
279  if ( renderContext.renderingStopped() )
280  {
281  break;
282  }
283 
284  QString layerId = li.previous();
285 
287 
288  if ( !ml || ( ml->type() != QgsMapLayer::VectorLayer ) )
289  continue;
290 
291  // only make labels if the layer is visible
292  // after scale dep viewing settings are checked
293  if ( ml->hasScaleBasedVisibility() && ( settings.scale() < ml->minimumScale() || settings.scale() > ml->maximumScale() ) )
294  continue;
295 
296  const QgsCoordinateTransform* ct = 0;
297  QgsRectangle r1 = settings.visibleExtent(), r2;
298 
299  if ( settings.hasCrsTransformEnabled() )
300  {
301  ct = settings.layerTransform( ml );
302  if ( ct )
303  reprojectToLayerExtent( ct, ml->crs().geographicFlag(), r1, r2 );
304  }
305 
306  renderContext.setCoordinateTransform( ct );
307  renderContext.setExtent( r1 );
308 
309  ml->drawLabels( renderContext );
310  }
311 }
312 
313 
314 void QgsMapRendererJob::drawNewLabeling( const QgsMapSettings& settings, QgsRenderContext& renderContext, QgsPalLabeling* labelingEngine )
315 {
316  if ( labelingEngine && !renderContext.renderingStopped() )
317  {
318  // set correct extent
319  renderContext.setExtent( settings.visibleExtent() );
320  renderContext.setCoordinateTransform( NULL );
321 
322  labelingEngine->drawLabeling( renderContext );
323  labelingEngine->exit();
324  }
325 }
326 
328 {
329  foreach ( QString id, mGeometryCaches.keys() )
330  {
331  const QgsGeometryCache& cache = mGeometryCaches[id];
332  if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( id ) ) )
333  * vl->cache() = cache;
334  }
335  mGeometryCaches.clear();
336 }
337 
338 
340 {
342  {
343  QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
344  if ((( vl->blendMode() != QPainter::CompositionMode_SourceOver )
345  || ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
346  || ( vl->layerTransparency() != 0 ) ) )
347  return true;
348  }
349 
350  return false;
351 }
352