QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmaprendererjob.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaprendererjob.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 
16 #include "qgsmaprendererjob.h"
17 
18 #include <QPainter>
19 #include <QElapsedTimer>
20 #include <QTimer>
21 #include <QtConcurrentMap>
22 
23 #include <QPicture>
24 
25 #include "qgslogger.h"
26 #include "qgsrendercontext.h"
27 #include "qgsmaplayer.h"
28 #include "qgsproject.h"
29 #include "qgsmaplayerrenderer.h"
31 #include "qgsmaprenderercache.h"
32 #include "qgsmessagelog.h"
33 #include "qgspallabeling.h"
34 #include "qgsexception.h"
35 #include "qgslabelingengine.h"
36 #include "qgsmaplayerlistutils_p.h"
37 #include "qgsvectorlayerlabeling.h"
38 #include "qgssettings.h"
40 #include "qgssymbol.h"
41 #include "qgsrenderer.h"
42 #include "qgssymbollayer.h"
43 #include "qgsvectorlayerutils.h"
44 #include "qgssymbollayerutils.h"
47 #include "qgsvectorlayerrenderer.h"
48 #include "qgsrendereditemresults.h"
49 #include "qgsmaskpaintdevice.h"
50 
52 
53 const QString QgsMapRendererJob::LABEL_CACHE_ID = QStringLiteral( "_labels_" );
54 const QString QgsMapRendererJob::LABEL_PREVIEW_CACHE_ID = QStringLiteral( "_preview_labels_" );
55 
56 LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
57 {
58  mContext = std::move( other.mContext );
59 
60  img = other.img;
61  other.img = nullptr;
62 
63  renderer = other.renderer;
64  other.renderer = nullptr;
65 
66  imageInitialized = other.imageInitialized;
67  blendMode = other.blendMode;
68  opacity = other.opacity;
69  cached = other.cached;
70  layer = other.layer;
71  completed = other.completed;
72  renderingTime = other.renderingTime;
73  estimatedRenderingTime = other.estimatedRenderingTime ;
74  errors = other.errors;
75  layerId = other.layerId;
76 
77  maskPaintDevice = std::move( other.maskPaintDevice );
78 
79  firstPassJob = other.firstPassJob;
80  other.firstPassJob = nullptr;
81 
82  picture = std::move( other.picture );
83 
84  maskJobs = other.maskJobs;
85 
86  maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
87 
88  return *this;
89 }
90 
91 LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
92  : imageInitialized( other.imageInitialized )
93  , blendMode( other.blendMode )
94  , opacity( other.opacity )
95  , cached( other.cached )
96  , layer( other.layer )
97  , completed( other.completed )
98  , renderingTime( other.renderingTime )
99  , estimatedRenderingTime( other.estimatedRenderingTime )
100  , errors( other.errors )
101  , layerId( other.layerId )
102  , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
103  , maskJobs( other.maskJobs )
104 {
105  mContext = std::move( other.mContext );
106 
107  img = other.img;
108  other.img = nullptr;
109 
110  renderer = other.renderer;
111  other.renderer = nullptr;
112 
113  maskPaintDevice = std::move( other.maskPaintDevice );
114 
115  firstPassJob = other.firstPassJob;
116  other.firstPassJob = nullptr;
117 
118  picture = std::move( other.picture );
119 }
120 
121 bool LayerRenderJob::imageCanBeComposed() const
122 {
123  if ( imageInitialized )
124  {
125  if ( renderer )
126  {
127  return renderer->isReadyToCompose();
128  }
129  else
130  {
131  return true;
132  }
133  }
134  else
135  {
136  return false;
137  }
138 }
139 
141  : mSettings( settings )
142  , mRenderedItemResults( std::make_unique< QgsRenderedItemResults >( settings.extent() ) )
143  , mLabelingEngineFeedback( new QgsLabelingEngineFeedback( this ) )
144 {}
145 
147 
149 {
150  if ( mSettings.hasValidSettings() )
151  startPrivate();
152  else
153  {
154  mErrors.append( QgsMapRendererJob::Error( QString(), tr( "Invalid map settings" ) ) );
155  emit finished();
156  }
157 }
158 
160 {
162 }
163 
165 {
166  return mRenderedItemResults.release();
167 }
168 
170  : QgsMapRendererJob( settings )
171 {
172 }
173 
174 
176 {
177  return mErrors;
178 }
179 
181 {
182  mCache = cache;
183 }
184 
186 {
187  return mLabelingEngineFeedback;
188 }
189 
190 QHash<QgsMapLayer *, int> QgsMapRendererJob::perLayerRenderingTime() const
191 {
192  QHash<QgsMapLayer *, int> result;
193  for ( auto it = mPerLayerRenderingTime.constBegin(); it != mPerLayerRenderingTime.constEnd(); ++it )
194  {
195  if ( auto &&lKey = it.key() )
196  result.insert( lKey, it.value() );
197  }
198  return result;
199 }
200 
201 void QgsMapRendererJob::setLayerRenderingTimeHints( const QHash<QString, int> &hints )
202 {
203  mLayerRenderingTimeHints = hints;
204 }
205 
207 {
208  return mSettings;
209 }
210 
212 {
213  bool canCache = mCache;
214 
215  // calculate which layers will be labeled
216  QSet< QgsMapLayer * > labeledLayers;
217  const QList<QgsMapLayer *> layers = mSettings.layers();
218  for ( QgsMapLayer *ml : layers )
219  {
221  labeledLayers << ml;
222 
223  switch ( ml->type() )
224  {
226  {
227  QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
228  if ( vl->labelsEnabled() && vl->labeling()->requiresAdvancedEffects() )
229  {
230  canCache = false;
231  }
232  break;
233  }
234 
236  {
237  // TODO -- add detection of advanced labeling effects for vector tile layers
238  break;
239  }
240 
247  break;
248  }
249 
250  if ( !canCache )
251  break;
252 
253  }
254 
256  {
257  // we may need to clear label cache and re-register labeled features - check for that here
258 
259  // can we reuse the cached label solution?
260  bool canUseCache = canCache && qgis::listToSet( mCache->dependentLayers( LABEL_CACHE_ID ) ) == labeledLayers;
261  if ( !canUseCache )
262  {
263  // no - participating layers have changed
265  }
266  }
267  return canCache;
268 }
269 
270 
271 bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform &ct, QgsRectangle &extent, QgsRectangle &r2 )
272 {
273  bool res = true;
274  // we can safely use ballpark transforms without bothering the user here -- at the likely scale of layer extents there
275  // won't be an appreciable difference, and we aren't actually transforming any rendered points here anyway (just the layer extent)
276  QgsCoordinateTransform approxTransform = ct;
277  approxTransform.setBallparkTransformsAreAppropriate( true );
278 
279  try
280  {
281 #ifdef QGISDEBUG
282  // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
283 #endif
284  // Split the extent into two if the source CRS is
285  // geographic and the extent crosses the split in
286  // geographic coordinates (usually +/- 180 degrees,
287  // and is assumed to be so here), and draw each
288  // extent separately.
289  static const double SPLIT_COORD = 180.0;
290 
291  if ( ml->crs().isGeographic() )
292  {
293  if ( ml->type() == QgsMapLayerType::VectorLayer && !approxTransform.destinationCrs().isGeographic() )
294  {
295  // if we transform from a projected coordinate system check
296  // check if transforming back roughly returns the input
297  // extend - otherwise render the world.
298  QgsRectangle extent1 = approxTransform.transformBoundingBox( extent, Qgis::TransformDirection::Reverse );
299  QgsRectangle extent2 = approxTransform.transformBoundingBox( extent1, Qgis::TransformDirection::Forward );
300 
301  QgsDebugMsgLevel( QStringLiteral( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
302  .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() )
303  .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() )
304  .arg( std::fabs( 1.0 - extent2.width() / extent.width() ) )
305  .arg( std::fabs( 1.0 - extent2.height() / extent.height() ) )
306  , 3 );
307 
308  // can differ by a maximum of up to 20% of height/width
309  if ( qgsDoubleNear( extent2.xMinimum(), extent.xMinimum(), extent.width() * 0.2 )
310  && qgsDoubleNear( extent2.xMaximum(), extent.xMaximum(), extent.width() * 0.2 )
311  && qgsDoubleNear( extent2.yMinimum(), extent.yMinimum(), extent.height() * 0.2 )
312  && qgsDoubleNear( extent2.yMaximum(), extent.yMaximum(), extent.height() * 0.2 )
313  )
314  {
315  extent = extent1;
316  }
317  else
318  {
319  extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
320  res = false;
321  }
322  }
323  else
324  {
325  // Note: ll = lower left point
326  QgsPointXY ll = approxTransform.transform( extent.xMinimum(), extent.yMinimum(),
327  Qgis::TransformDirection::Reverse );
328 
329  // and ur = upper right point
330  QgsPointXY ur = approxTransform.transform( extent.xMaximum(), extent.yMaximum(),
331  Qgis::TransformDirection::Reverse );
332 
333  QgsDebugMsgLevel( QStringLiteral( "in:%1 (ll:%2 ur:%3)" ).arg( extent.toString(), ll.toString(), ur.toString() ), 4 );
334 
335  extent = approxTransform.transformBoundingBox( extent, Qgis::TransformDirection::Reverse );
336 
337  QgsDebugMsgLevel( QStringLiteral( "out:%1 (w:%2 h:%3)" ).arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ), 4 );
338 
339  if ( ll.x() > ur.x() )
340  {
341  // the coordinates projected in reverse order than what one would expect.
342  // we are probably looking at an area that includes longitude of 180 degrees.
343  // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180)
344  // so let's use (-180,180). This hopefully does not add too much overhead. It is
345  // more straightforward than rendering with two separate extents and more consistent
346  // for rendering, labeling and caching as everything is rendered just in one go
347  extent.setXMinimum( -SPLIT_COORD );
348  extent.setXMaximum( SPLIT_COORD );
349  res = false;
350  }
351  }
352 
353  // TODO: the above rule still does not help if using a projection that covers the whole
354  // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx.
355  // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90,
356  // but in fact the extent should cover the whole world.
357  }
358  else // can't cross 180
359  {
360  if ( approxTransform.destinationCrs().isGeographic() &&
361  ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 ||
362  extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) )
363  // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates.
364  // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity.
365  // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat,
366  // but this seems like a safer choice.
367  {
368  extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
369  res = false;
370  }
371  else
372  extent = approxTransform.transformBoundingBox( extent, Qgis::TransformDirection::Reverse );
373  }
374  }
375  catch ( QgsCsException & )
376  {
377  QgsDebugMsg( QStringLiteral( "Transform error caught" ) );
378  extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
379  r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
380  res = false;
381  }
382 
383  return res;
384 }
385 
386 QImage *QgsMapRendererJob::allocateImage( QString layerId )
387 {
388  QImage *image = new QImage( mSettings.deviceOutputSize(),
390  image->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) );
391  image->setDotsPerMeterX( 1000 * mSettings.outputDpi() / 25.4 );
392  image->setDotsPerMeterY( 1000 * mSettings.outputDpi() / 25.4 );
393  if ( image->isNull() )
394  {
395  mErrors.append( Error( layerId, tr( "Insufficient memory for image %1x%2" ).arg( mSettings.outputSize().width() ).arg( mSettings.outputSize().height() ) ) );
396  delete image;
397  return nullptr;
398  }
399  return image;
400 }
401 
402 QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image, const QgsRenderContext *context )
403 {
404  QPainter *painter = nullptr;
405  image = allocateImage( layerId );
406  if ( image )
407  {
408  painter = new QPainter( image );
409  context->setPainterFlagsUsingContext( painter );
410  }
411  return painter;
412 }
413 
414 QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter( const QgsRenderContext *context )
415 {
416  std::unique_ptr<QPicture> picture = std::make_unique<QPicture>();
417  QPainter *painter = new QPainter( picture.get() );
418  context->setPainterFlagsUsingContext( painter );
419  return { std::move( picture ), painter };
420 }
421 
422 std::vector<LayerRenderJob> QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet )
423 {
424  std::vector< LayerRenderJob > layerJobs;
425 
426  // render all layers in the stack, starting at the base
427  QListIterator<QgsMapLayer *> li( mSettings.layers() );
428  li.toBack();
429 
430  if ( mCache )
431  {
433  Q_UNUSED( cacheValid )
434  QgsDebugMsgLevel( QStringLiteral( "CACHE VALID: %1" ).arg( cacheValid ), 4 );
435  }
436 
437  bool requiresLabelRedraw = !( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) );
438 
439  while ( li.hasPrevious() )
440  {
441  QgsMapLayer *ml = li.previous();
442 
443  QgsDebugMsgLevel( QStringLiteral( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
444  .arg( ml->name() )
445  .arg( ml->minimumScale() )
446  .arg( ml->maximumScale() )
447  .arg( ml->hasScaleBasedVisibility() )
448  .arg( ml->blendMode() )
449  .arg( ml->isValid() )
450  , 3 );
451 
452  if ( !ml->isValid() )
453  {
454  QgsDebugMsgLevel( QStringLiteral( "Invalid Layer skipped" ), 3 );
455  continue;
456  }
457 
458  if ( !ml->isInScaleRange( mSettings.scale() ) ) //|| mOverview )
459  {
460  QgsDebugMsgLevel( QStringLiteral( "Layer not rendered because it is not within the defined visibility scale range" ), 3 );
461  continue;
462  }
463 
465  {
466  QgsDebugMsgLevel( QStringLiteral( "Layer not rendered because it is not visible within the map's time range" ), 3 );
467  continue;
468  }
469 
471  {
472  QgsDebugMsgLevel( QStringLiteral( "Layer not rendered because it is not visible within the map's z range" ), 3 );
473  continue;
474  }
475 
477  r1.grow( mSettings.extentBuffer() );
479 
480  ct = mSettings.layerTransform( ml );
481  bool haveExtentInLayerCrs = true;
482  if ( ct.isValid() )
483  {
484  haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
485  }
486  QgsDebugMsgLevel( "extent: " + r1.toString(), 3 );
487  if ( !r1.isFinite() || !r2.isFinite() )
488  {
489  mErrors.append( Error( ml->id(), tr( "There was a problem transforming the layer's extent. Layer skipped." ) ) );
490  continue;
491  }
492 
493  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
494 
495  // Force render of layers that are being edited
496  // or if there's a labeling engine that needs the layer to register features
497  if ( mCache )
498  {
499  const bool requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( ml ) ) && requiresLabelRedraw;
500  if ( ( vl && vl->isEditable() ) || requiresLabeling )
501  {
502  mCache->clearCacheImage( ml->id() );
503  }
504  }
505 
506  layerJobs.emplace_back( LayerRenderJob() );
507  LayerRenderJob &job = layerJobs.back();
508  job.layer = ml;
509  job.layerId = ml->id();
510  job.estimatedRenderingTime = mLayerRenderingTimeHints.value( ml->id(), 0 );
511 
512  job.setContext( std::make_unique< QgsRenderContext >( QgsRenderContext::fromMapSettings( mSettings ) ) );
513  if ( !ml->customProperty( QStringLiteral( "_noset_layer_expression_context" ) ).toBool() )
514  job.context()->expressionContext().appendScope( QgsExpressionContextUtils::layerScope( ml ) );
515  job.context()->setPainter( painter );
516  job.context()->setLabelingEngine( labelingEngine2 );
517  job.context()->setLabelSink( labelSink() );
518  job.context()->setCoordinateTransform( ct );
519  job.context()->setExtent( r1 );
520  if ( !haveExtentInLayerCrs )
521  job.context()->setFlag( Qgis::RenderContextFlag::ApplyClipAfterReprojection, true );
522 
523  if ( mFeatureFilterProvider )
524  job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
525 
526  QgsMapLayerStyleOverride styleOverride( ml );
527  if ( mSettings.layerStyleOverrides().contains( ml->id() ) )
528  styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( ml->id() ) );
529 
530  job.blendMode = ml->blendMode();
531 
532  // raster layer opacity is handled directly within the raster layer renderer, so don't
533  // apply default opacity handling here!
534  job.opacity = ml->type() != QgsMapLayerType::RasterLayer ? ml->opacity() : 1.0;
535 
536  // if we can use the cache, let's do it and avoid rendering!
538  && mCache && mCache->hasCacheImage( ml->id() ) )
539  {
540  job.cached = true;
541  job.imageInitialized = true;
542  job.img = new QImage( mCache->cacheImage( ml->id() ) );
543  job.img->setDevicePixelRatio( static_cast<qreal>( mSettings.devicePixelRatio() ) );
544  job.renderer = nullptr;
545  job.context()->setPainter( nullptr );
546  mLayersRedrawnFromCache.append( ml->id() );
547  continue;
548  }
549 
550  QElapsedTimer layerTime;
551  layerTime.start();
552  job.renderer = ml->createMapRenderer( *( job.context() ) );
553  if ( job.renderer )
554  {
555  job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
556  job.context()->setFeedback( job.renderer->feedback() );
557  }
558 
559  // If we are drawing with an alternative blending mode then we need to render to a separate image
560  // before compositing this on the map. This effectively flattens the layer and prevents
561  // blending occurring between objects on the layer
562  if ( mCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
563  {
564  // Flattened image for drawing when a blending mode is set
565  job.context()->setPainter( allocateImageAndPainter( ml->id(), job.img, job.context() ) );
566  if ( ! job.img )
567  {
568  delete job.renderer;
569  job.renderer = nullptr;
570  layerJobs.pop_back();
571  continue;
572  }
573  }
574 
575  job.renderingTime = layerTime.elapsed(); // include job preparation time in layer rendering time
576  }
577 
578  return layerJobs;
579 }
580 
581 std::vector< LayerRenderJob > QgsMapRendererJob::prepareSecondPassJobs( std::vector< LayerRenderJob > &firstPassJobs, LabelRenderJob &labelJob )
582 {
583  std::vector< LayerRenderJob > secondPassJobs;
584 
585  // We will need to quickly access the associated rendering job of a layer
586  QHash<QString, LayerRenderJob *> layerJobMapping;
587 
588  // ... and layer that contains a mask (and whether there is effects implied or not)
589  QMap<QString, bool> maskLayerHasEffects;
590  QMap<int, bool> labelHasEffects;
591 
592  struct MaskSource
593  {
594  QString layerId;
595  QString labelRuleId;
596  int labelMaskId;
597  bool hasEffects;
598  MaskSource( const QString &layerId_, const QString &labelRuleId_, int labelMaskId_, bool hasEffects_ ):
599  layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
600  };
601 
602  // We collect for each layer, the set of symbol layers that will be "masked"
603  // and the list of source layers that have a mask
604  QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;
605 
608 
609  // First up, create a mapping of layer id to jobs. We need this to filter out any masking
610  // which refers to layers which we aren't rendering as part of this map render
611  for ( LayerRenderJob &job : firstPassJobs )
612  {
613  layerJobMapping[job.layerId] = &job;
614  }
615 
616  // next, collate a master list of masked layers, skipping over any which refer to layers
617  // which don't have a corresponding render job
618  for ( LayerRenderJob &job : firstPassJobs )
619  {
620  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
621  if ( ! vl )
622  continue;
623 
624  // lambda function to factor code for both label masks and symbol layer masks
625  auto collectMasks = [&]( QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(), int labelMaskId = -1 )
626  {
627  bool hasEffects = false;
628  for ( auto it = masks->begin(); it != masks->end(); ++it )
629  {
630  auto lit = maskedSymbolLayers.find( it.key() );
631  if ( lit == maskedSymbolLayers.end() )
632  {
633  maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
634  }
635  else
636  {
637  if ( lit->first != it.value().symbolLayerIds )
638  {
639  QgsLogger::warning( QStringLiteral( "Layer %1 : Different sets of symbol layers are masked by different sources ! Only one (arbitrary) set will be retained !" ).arg( it.key() ) );
640  continue;
641  }
642  lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
643  }
644  hasEffects |= it.value().hasEffects;
645  }
646  if ( ! masks->isEmpty() && labelMaskId == -1 )
647  maskLayerHasEffects[ sourceLayerId ] = hasEffects;
648  };
649 
650  // collect label masks
651  QHash<QString, QgsMaskedLayers> labelMasks = QgsVectorLayerUtils::labelMasks( vl );
652  for ( auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
653  {
654  QString labelRule = it.key();
655  // this is a hash of layer id to masks
656  QgsMaskedLayers masks = it.value();
657 
658  // filter out masks to those which we are actually rendering
659  QgsMaskedLayers usableMasks;
660  for ( auto mit = masks.begin(); mit != masks.end(); mit++ )
661  {
662  const QString sourceLayerId = mit.key();
663  // if we aren't rendering the source layer as part of this render, we can't process this mask
664  if ( !layerJobMapping.contains( sourceLayerId ) )
665  continue;
666  else
667  usableMasks.insert( sourceLayerId, mit.value() );
668  }
669 
670  if ( usableMasks.empty() )
671  continue;
672 
673  // group layers by QSet<QgsSymbolLayerReference>
674  QSet<QgsSymbolLayerReference> slRefs;
675  bool hasEffects = false;
676  for ( auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
677  {
678  const QString sourceLayerId = mit.key();
679  // if we aren't rendering the source layer as part of this render, we can't process this mask
680  if ( !layerJobMapping.contains( sourceLayerId ) )
681  continue;
682 
683  for ( auto slIt = mit.value().symbolLayerIds.begin(); slIt != mit.value().symbolLayerIds.end(); slIt++ )
684  {
685  slRefs.insert( QgsSymbolLayerReference( mit.key(), *slIt ) );
686  }
687 
688  hasEffects |= mit.value().hasEffects;
689  }
690  // generate a new mask id for this set
691  int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->id(), it.key(), slRefs );
692  labelHasEffects[ labelMaskId ] = hasEffects;
693 
694  // now collect masks
695  collectMasks( &usableMasks, vl->id(), labelRule, labelMaskId );
696  }
697 
698  // collect symbol layer masks
700  collectMasks( &symbolLayerMasks, vl->id() );
701  }
702 
703  if ( maskedSymbolLayers.isEmpty() )
704  return secondPassJobs;
705 
706  // Prepare label mask images
707  for ( int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
708  {
709  QPaintDevice *maskPaintDevice = nullptr;
710  QPainter *maskPainter = nullptr;
711  if ( forceVector && !labelHasEffects[ maskId ] )
712  {
713  // set a painter to get all masking instruction in order to later clip masked symbol layer
714  maskPaintDevice = new QgsMaskPaintDevice( true );
715  maskPainter = new QPainter( maskPaintDevice );
716  }
717  else
718  {
719  // Note: we only need an alpha channel here, rather than a full RGBA image
720  QImage *maskImage = nullptr;
721  maskPainter = allocateImageAndPainter( QStringLiteral( "label mask" ), maskImage, &labelJob.context );
722  maskImage->fill( 0 );
723  maskPaintDevice = maskImage;
724  }
725 
726  labelJob.context.setMaskPainter( maskPainter, maskId );
727  labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
728  labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
729  }
730  labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
731 
732  // Prepare second pass jobs
733  // - For raster rendering or vector rendering if effects are involved
734  // 1st pass, 2nd pass and mask are rendered in QImage and composed in composeSecondPass
735  // - For vector rendering if no effects are involved
736  // 1st pass is rendered in QImage, clip paths are generated according to mask and used during
737  // masked symbol layer rendering during second pass, which is rendered in QPicture, second
738  // pass job picture
739 
740  // Allocate an image for labels
741  if ( !labelJob.img && !forceVector )
742  {
743  labelJob.img = allocateImage( QStringLiteral( "labels" ) );
744  }
745  else if ( !labelJob.picture && forceVector )
746  {
747  labelJob.picture.reset( new QPicture() );
748  }
749 
750  // first we initialize painter and mask painter for all jobs
751  for ( LayerRenderJob &job : firstPassJobs )
752  {
753  job.maskRequiresLayerRasterization = false;
754 
755  auto it = maskedSymbolLayers.find( job.layerId );
756  if ( it != maskedSymbolLayers.end() )
757  {
758  const QList<MaskSource> &sourceList = it->second;
759  for ( const MaskSource &source : sourceList )
760  {
761  job.maskRequiresLayerRasterization |= source.hasEffects;
762  }
763  }
764 
765  // update first pass job painter and device if needed
766  const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
767  if ( isRasterRendering && !job.img )
768  {
769  job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
770  }
771  else if ( !isRasterRendering && !job.picture )
772  {
773  PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
774  job.picture = std::move( pictureAndPainter.first );
775  job.context()->setPainter( pictureAndPainter.second );
776  // force recreation of layer renderer so it initialize correctly the renderer
777  // especially the RasterLayerRender that need logicalDpiX from painting device
778  job.renderer = job.layer->createMapRenderer( *( job.context() ) );
779  }
780 
781  // for layer that mask, generate mask in first pass job
782  if ( maskLayerHasEffects.contains( job.layerId ) )
783  {
784  QPaintDevice *maskPaintDevice = nullptr;
785  QPainter *maskPainter = nullptr;
786  if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
787  {
788  // set a painter to get all masking instruction in order to later clip masked symbol layer
789  maskPaintDevice = new QgsMaskPaintDevice();
790  maskPainter = new QPainter( maskPaintDevice );
791  }
792  else
793  {
794  // Note: we only need an alpha channel here, rather than a full RGBA image
795  QImage *maskImage = nullptr;
796  maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
797  maskImage->fill( 0 );
798  maskPaintDevice = maskImage;
799  }
800 
801  job.context()->setMaskPainter( maskPainter );
802  job.maskPainter.reset( maskPainter );
803  job.maskPaintDevice.reset( maskPaintDevice );
804  }
805  }
806 
807  for ( LayerRenderJob &job : firstPassJobs )
808  {
809  QgsMapLayer *ml = job.layer;
810 
811  auto it = maskedSymbolLayers.find( job.layerId );
812  if ( it == maskedSymbolLayers.end() )
813  continue;
814 
815  QList<MaskSource> &sourceList = it->second;
816  const QSet<QgsSymbolLayerId> &symbolList = it->first;
817 
818  secondPassJobs.emplace_back( LayerRenderJob() );
819  LayerRenderJob &job2 = secondPassJobs.back();
820 
821  job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
822 
823  // Points to the masking jobs. This will be needed during the second pass composition.
824  for ( MaskSource &source : sourceList )
825  {
826  if ( source.labelMaskId != -1 )
827  job2.maskJobs.push_back( qMakePair( nullptr, source.labelMaskId ) );
828  else
829  job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
830  }
831 
832  // copy the context from the initial job
833  job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
834  // also assign layer to match initial job
835  job2.layer = job.layer;
836  job2.layerId = job.layerId;
837 
838  // associate first pass job with second pass job
839  job2.firstPassJob = &job;
840 
841  if ( !forceVector || job2.maskRequiresLayerRasterization )
842  {
843  job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
844  }
845  else
846  {
847  PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
848  job2.picture = std::move( pictureAndPainter.first );
849  job2.context()->setPainter( pictureAndPainter.second );
850  }
851 
852  if ( ! job2.img && ! job2.picture )
853  {
854  secondPassJobs.pop_back();
855  continue;
856  }
857 
858  // FIXME: another possibility here, to avoid allocating a new map renderer and reuse the one from
859  // the first pass job, would be to be able to call QgsMapLayerRenderer::render() with a QgsRenderContext.
860  QgsVectorLayerRenderer *mapRenderer = static_cast<QgsVectorLayerRenderer *>( ml->createMapRenderer( *job2.context() ) );
861  job2.renderer = mapRenderer;
862  if ( job2.renderer )
863  {
864  job2.context()->setFeedback( job2.renderer->feedback() );
865  }
866 
867  // Render only the non masked symbol layer and we will compose 2nd pass with mask and first pass rendering in composeSecondPass
868  // If vector output is enabled, disabled symbol layers would be actually rendered and masked with clipping path set in QgsMapRendererJob::initSecondPassJobs
869  job2.context()->setDisabledSymbolLayers( QgsSymbolLayerUtils::toSymbolLayerPointers( mapRenderer->featureRenderer(), symbolList ) );
870  }
871 
872  return secondPassJobs;
873 }
874 
875 void QgsMapRendererJob::initSecondPassJobs( std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob ) const
876 {
878  return;
879 
880  for ( LayerRenderJob &job : secondPassJobs )
881  {
882  if ( job.maskRequiresLayerRasterization )
883  continue;
884 
885  // we draw disabled symbol layer but me mask them with clipping path produced during first pass job
886  // Resulting 2nd pass job picture will be the final rendering
887 
888  for ( const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
889  {
890  QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
891  QPainterPath path = static_cast<QgsMaskPaintDevice *>( maskPainter->device() )->maskPainterPath();
892  for ( const QgsSymbolLayer *symbolLayer : job.context()->disabledSymbolLayers() )
893  {
894  job.context()->addSymbolLayerClipPath( symbolLayer, path );
895  }
896  }
897 
898  job.context()->setDisabledSymbolLayers( QSet<const QgsSymbolLayer *>() );
899  }
900 }
901 
902 LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache )
903 {
904  LabelRenderJob job;
906  job.context.setPainter( painter );
907  job.context.setLabelingEngine( labelingEngine2 );
908  job.context.setFeedback( mLabelingEngineFeedback );
909 
911  r1.grow( mSettings.extentBuffer() );
912  job.context.setExtent( r1 );
913 
914  job.context.setFeatureFilterProvider( mFeatureFilterProvider );
917  job.context.setCoordinateTransform( ct );
918 
919  // no cache, no image allocation
921  return job;
922 
923  // if we can use the cache, let's do it and avoid rendering!
924  bool hasCache = canUseLabelCache && mCache && mCache->hasCacheImage( LABEL_CACHE_ID );
925  if ( hasCache )
926  {
927  job.cached = true;
928  job.complete = true;
929  job.img = new QImage( mCache->cacheImage( LABEL_CACHE_ID ) );
930  Q_ASSERT( job.img->devicePixelRatio() == mSettings.devicePixelRatio() );
931  job.context.setPainter( nullptr );
932  }
933  else
934  {
935  if ( canUseLabelCache && ( mCache || !painter ) )
936  {
937  job.img = allocateImage( QStringLiteral( "labels" ) );
938  }
939  }
940 
941  return job;
942 }
943 
944 
945 void QgsMapRendererJob::cleanupJobs( std::vector<LayerRenderJob> &jobs )
946 {
947  for ( LayerRenderJob &job : jobs )
948  {
949  if ( job.img )
950  {
951  delete job.context()->painter();
952  job.context()->setPainter( nullptr );
953 
954  if ( mCache && !job.cached && job.completed && job.layer )
955  {
956  QgsDebugMsgLevel( QStringLiteral( "caching image for %1" ).arg( job.layerId ), 2 );
957  mCache->setCacheImageWithParameters( job.layerId, *job.img, mSettings.visibleExtent(), mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
958  mCache->setCacheImageWithParameters( job.layerId + QStringLiteral( "_preview" ), *job.img, mSettings.visibleExtent(), mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
959  }
960 
961  delete job.img;
962  job.img = nullptr;
963  }
964 
965  if ( job.picture )
966  {
967  delete job.context()->painter();
968  job.context()->setPainter( nullptr );
969  job.picture.reset( nullptr );
970  }
971 
972  if ( job.renderer )
973  {
974  const QStringList errors = job.renderer->errors();
975  for ( const QString &message : errors )
976  mErrors.append( Error( job.renderer->layerId(), message ) );
977 
978  mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
979 
980  delete job.renderer;
981  job.renderer = nullptr;
982  }
983 
984  if ( job.layer )
985  mPerLayerRenderingTime.insert( job.layer, job.renderingTime );
986 
987  job.maskPainter.reset( nullptr );
988  job.maskPaintDevice.reset( nullptr );
989  }
990 
991  jobs.clear();
992 }
993 
994 void QgsMapRendererJob::cleanupSecondPassJobs( std::vector< LayerRenderJob > &jobs )
995 {
996  for ( LayerRenderJob &job : jobs )
997  {
998  if ( job.img )
999  {
1000  delete job.context()->painter();
1001  job.context()->setPainter( nullptr );
1002 
1003  delete job.img;
1004  job.img = nullptr;
1005  }
1006 
1007  if ( job.picture )
1008  {
1009  delete job.context()->painter();
1010  job.context()->setPainter( nullptr );
1011  }
1012 
1013  if ( job.renderer )
1014  {
1015  delete job.renderer;
1016  job.renderer = nullptr;
1017  }
1018 
1019  if ( job.layer )
1020  mPerLayerRenderingTime.insert( job.layer, job.renderingTime );
1021  }
1022 
1023  jobs.clear();
1024 }
1025 
1026 void QgsMapRendererJob::cleanupLabelJob( LabelRenderJob &job )
1027 {
1028  if ( job.img )
1029  {
1030  if ( mCache && !job.cached && !job.context.renderingStopped() )
1031  {
1032  QgsDebugMsgLevel( QStringLiteral( "caching label result image" ), 2 );
1033  mCache->setCacheImageWithParameters( LABEL_CACHE_ID, *job.img, mSettings.visibleExtent(), mSettings.mapToPixel(), _qgis_listQPointerToRaw( job.participatingLayers ) );
1034  mCache->setCacheImageWithParameters( LABEL_PREVIEW_CACHE_ID, *job.img, mSettings.visibleExtent(), mSettings.mapToPixel(), _qgis_listQPointerToRaw( job.participatingLayers ) );
1035  }
1036 
1037  delete job.img;
1038  job.img = nullptr;
1039  }
1040 
1041  job.picture.reset( nullptr );
1042  job.maskPainters.clear();
1043  job.maskPaintDevices.clear();
1044 }
1045 
1046 
1047 #define DEBUG_RENDERING 0
1048 
1049 QImage QgsMapRendererJob::composeImage( const QgsMapSettings &settings,
1050  const std::vector<LayerRenderJob> &jobs,
1051  const LabelRenderJob &labelJob,
1052  const QgsMapRendererCache *cache
1053  )
1054 {
1055  QImage image( settings.deviceOutputSize(), settings.outputImageFormat() );
1056  image.setDevicePixelRatio( settings.devicePixelRatio() );
1057  image.setDotsPerMeterX( static_cast<int>( settings.outputDpi() * 39.37 ) );
1058  image.setDotsPerMeterY( static_cast<int>( settings.outputDpi() * 39.37 ) );
1059  image.fill( settings.backgroundColor().rgba() );
1060 
1061  QPainter painter( &image );
1062 
1063 #if DEBUG_RENDERING
1064  int i = 0;
1065 #endif
1066  for ( const LayerRenderJob &job : jobs )
1067  {
1068  if ( job.layer && job.layer->customProperty( QStringLiteral( "rendering/renderAboveLabels" ) ).toBool() )
1069  continue; // skip layer for now, it will be rendered after labels
1070 
1071  QImage img = layerImageToBeComposed( settings, job, cache );
1072  if ( img.isNull() )
1073  continue; // image is not prepared and not even in cache
1074 
1075  painter.setCompositionMode( job.blendMode );
1076  painter.setOpacity( job.opacity );
1077 
1078 #if DEBUG_RENDERING
1079  img.save( QString( "/tmp/final_%1.png" ).arg( i ) );
1080  i++;
1081 #endif
1082 
1083  painter.drawImage( 0, 0, img );
1084  }
1085 
1086  // IMPORTANT - don't draw labelJob img before the label job is complete,
1087  // as the image is uninitialized and full of garbage before the label job
1088  // commences
1089  if ( labelJob.img && labelJob.complete )
1090  {
1091  painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1092  painter.setOpacity( 1.0 );
1093  painter.drawImage( 0, 0, *labelJob.img );
1094  }
1095  // when checking for a label cache image, we only look for those which would be drawn between 30% and 300% of the
1096  // original size. We don't want to draw massive pixelated labels on top of everything else, and we also don't need
1097  // to draw tiny unreadable labels... better to draw nothing in this case and wait till the updated label results are ready!
1098  else if ( cache && cache->hasAnyCacheImage( LABEL_PREVIEW_CACHE_ID, 0.3, 3 ) )
1099  {
1100  const QImage labelCacheImage = cache->transformedCacheImage( LABEL_PREVIEW_CACHE_ID, settings.mapToPixel() );
1101  painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1102  painter.setOpacity( 1.0 );
1103  painter.drawImage( 0, 0, labelCacheImage );
1104  }
1105 
1106  // render any layers with the renderAboveLabels flag now
1107  for ( const LayerRenderJob &job : jobs )
1108  {
1109  if ( !job.layer || !job.layer->customProperty( QStringLiteral( "rendering/renderAboveLabels" ) ).toBool() )
1110  continue;
1111 
1112  QImage img = layerImageToBeComposed( settings, job, cache );
1113  if ( img.isNull() )
1114  continue; // image is not prepared and not even in cache
1115 
1116  painter.setCompositionMode( job.blendMode );
1117  painter.setOpacity( job.opacity );
1118 
1119  painter.drawImage( 0, 0, img );
1120  }
1121 
1122  painter.end();
1123 #if DEBUG_RENDERING
1124  image.save( "/tmp/final.png" );
1125 #endif
1126  return image;
1127 }
1128 
1130  const QgsMapSettings &settings,
1131  const LayerRenderJob &job,
1132  const QgsMapRendererCache *cache
1133 )
1134 {
1135  if ( job.imageCanBeComposed() )
1136  {
1137  Q_ASSERT( job.img );
1138  return *job.img;
1139  }
1140  else
1141  {
1142  if ( cache && cache->hasAnyCacheImage( job.layerId + QStringLiteral( "_preview" ) ) )
1143  {
1144  return cache->transformedCacheImage( job.layerId + QStringLiteral( "_preview" ), settings.mapToPixel() );
1145  }
1146  else
1147  return QImage();
1148  }
1149 }
1150 
1151 void QgsMapRendererJob::composeSecondPass( std::vector<LayerRenderJob> &secondPassJobs, LabelRenderJob &labelJob, bool forceVector )
1152 {
1153  // compose the second pass with the mask
1154  for ( LayerRenderJob &job : secondPassJobs )
1155  {
1156  const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1157 
1158  // Merge all mask images into the first one if we have more than one mask image
1159  if ( isRasterRendering && job.maskJobs.size() > 1 )
1160  {
1161  QPainter *maskPainter = nullptr;
1162  for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1163  {
1164  QImage *maskImage = static_cast<QImage *>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1165  if ( !maskPainter )
1166  {
1167  maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1168  }
1169  else
1170  {
1171  maskPainter->drawImage( 0, 0, *maskImage );
1172  }
1173  }
1174  }
1175 
1176  if ( ! job.maskJobs.isEmpty() )
1177  {
1178  // All have been merged into the first
1179  QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1180  if ( isRasterRendering )
1181  {
1182  QImage *maskImage = static_cast<QImage *>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1183 
1184  // Only retain parts of the second rendering that are "inside" the mask image
1185  QPainter *painter = job.context()->painter();
1186 
1187  painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1188 
1189  //Create an "alpha binarized" image of the maskImage to :
1190  //* Eliminate antialiasing artifact
1191  //* Avoid applying mask opacity to elements under the mask but not masked
1192  QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1193  QVector<QRgb> mswTable;
1194  mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1195  mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1196  maskBinAlpha.setColorTable( mswTable );
1197  painter->drawImage( 0, 0, maskBinAlpha );
1198 
1199  // Modify the first pass' image ...
1200  {
1201  QPainter tempPainter;
1202 
1203  // reuse the first pass painter, if available
1204  QPainter *painter1 = job.firstPassJob->context()->painter();
1205  if ( ! painter1 )
1206  {
1207  tempPainter.begin( job.firstPassJob->img );
1208  painter1 = &tempPainter;
1209  }
1210 
1211  // ... first retain parts that are "outside" the mask image
1212  painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1213  painter1->drawImage( 0, 0, *maskImage );
1214 
1215  // ... and overpaint the second pass' image on it
1216  painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1217  painter1->drawImage( 0, 0, *job.img );
1218  }
1219  }
1220  else
1221  {
1222  job.firstPassJob->picture = std::move( job.picture );
1223  job.picture = nullptr;
1224  }
1225  }
1226  }
1227 }
1228 
1229 void QgsMapRendererJob::logRenderingTime( const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob )
1230 {
1232  return;
1233 
1234  QMultiMap<int, QString> elapsed;
1235  for ( const LayerRenderJob &job : jobs )
1236  elapsed.insert( job.renderingTime, job.layerId );
1237  for ( const LayerRenderJob &job : secondPassJobs )
1238  elapsed.insert( job.renderingTime, job.layerId + QString( " (second pass)" ) );
1239 
1240  elapsed.insert( labelJob.renderingTime, tr( "Labeling" ) );
1241 
1242  QList<int> tt( elapsed.uniqueKeys() );
1243  std::sort( tt.begin(), tt.end(), std::greater<int>() );
1244  for ( int t : std::as_const( tt ) )
1245  {
1246  QgsMessageLog::logMessage( tr( "%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String( ", " ) ) ), tr( "Rendering" ) );
1247  }
1248  QgsMessageLog::logMessage( QStringLiteral( "---" ), tr( "Rendering" ) );
1249 }
1250 
1251 void QgsMapRendererJob::drawLabeling( QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter )
1252 {
1253  QgsDebugMsgLevel( QStringLiteral( "Draw labeling start" ), 5 );
1254 
1255  QElapsedTimer t;
1256  t.start();
1257 
1258  // Reset the composition mode before rendering the labels
1259  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1260 
1261  renderContext.setPainter( painter );
1262 
1263  if ( labelingEngine2 )
1264  {
1265  labelingEngine2->run( renderContext );
1266  }
1267 
1268  QgsDebugMsgLevel( QStringLiteral( "Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
1269 }
1270 
1271 void QgsMapRendererJob::drawLabeling( const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter )
1272 {
1273  Q_UNUSED( settings )
1274 
1275  drawLabeling( renderContext, labelingEngine2, painter );
1276 }
1277 
QgsMapRendererJob::mLayersRedrawnFromCache
QStringList mLayersRedrawnFromCache
Definition: qgsmaprendererjob.h:517
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
qgsexpressioncontextutils.h
QgsRectangle::isFinite
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Definition: qgsrectangle.h:559
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
QgsMapRendererJob::Error
Definition: qgsmaprendererjob.h:354
qgspallabeling.h
qgsmaplayerstylemanager.h
QgsRenderContext::setPainterFlagsUsingContext
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
Definition: qgsrendercontext.cpp:169
QgsMapRendererJob::logRenderingTime
void logRenderingTime(const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob)
QgsMapRendererJob::mCache
QgsMapRendererCache * mCache
Definition: qgsmaprendererjob.h:494
QgsMapRendererJob::mapSettings
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
QgsPalLabeling::staticWillUseLayer
static bool staticWillUseLayer(const QgsMapLayer *layer)
Called to find out whether a specified layer is used for labeling.
Definition: qgspallabeling.cpp:3796
QgsMapRendererJob::composeImage
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
QgsMapLayerType::MeshLayer
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
QgsMapSettings::outputSize
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
Definition: qgsmapsettings.cpp:239
QgsMapRendererJob::mErrors
Errors mErrors
Definition: qgsmaprendererjob.h:492
QgsMapSettings::layerTransform
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
Definition: qgsmapsettings.cpp:481
QgsRenderContext::fromMapSettings
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Definition: qgsrendercontext.cpp:234
QgsMapLayerType::VectorLayer
@ VectorLayer
Vector layer.
QgsMapSettings::extentBuffer
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
Definition: qgsmapsettings.cpp:92
QgsMapRendererJob::LABEL_CACHE_ID
static const QString LABEL_CACHE_ID
QgsMapRendererCache ID string for cached label image.
Definition: qgsmaprendererjob.h:441
QgsMapRendererJob::QgsMapRendererJob
QgsMapRendererJob(const QgsMapSettings &settings)
qgsmaskpaintdevice.h
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsMapLayer::opacity
double opacity
Definition: qgsmaplayer.h:82
QgsMapLayer::blendMode
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
Definition: qgsmaplayer.cpp:320
QgsTemporalRangeObject::isTemporal
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
Definition: qgstemporalrangeobject.cpp:30
QgsMapSettings::devicePixelRatio
float devicePixelRatio() const
Returns the device pixel ratio.
Definition: qgsmapsettings.cpp:251
QgsMapRendererCache::hasCacheImage
bool hasCacheImage(const QString &cacheKey) const
Returns true if the cache contains an image with the specified cacheKey that has the same extent and ...
Definition: qgsmaprenderercache.cpp:175
QgsMapRendererJob::prepareSecondPassJobs
std::vector< LayerRenderJob > prepareSecondPassJobs(std::vector< LayerRenderJob > &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
QgsMapLayerType::AnnotationLayer
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
qgsvectorlayerrenderer.h
QgsMaskPaintDevice
Mask painter device that can be used to register everything painted into a QPainterPath used later as...
Definition: qgsmaskpaintdevice.h:63
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:512
qgssymbollayerutils.h
QgsSymbolLayerReference
Type used to refer to a specific symbol layer in a symbol of a layer.
Definition: qgssymbollayerreference.h:133
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsCoordinateTransform::transformBoundingBox
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:560
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:334
QgsMapSettings::hasValidSettings
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
Definition: qgsmapsettings.cpp:406
QgsMapRendererJob::mRenderedItemResults
std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults
Definition: qgsmaprendererjob.h:514
QgsTemporalRangeObject::temporalRange
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
Definition: qgstemporalrangeobject.cpp:43
QgsMapRendererJob::prepareJobs
std::vector< LayerRenderJob > prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsMapRendererJob::prepareLabelCache
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
QgsMapSettings::layerStyleOverrides
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
Definition: qgsmapsettings.cpp:340
QgsMapSettings::outputImageFormat
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
Definition: qgsmapsettings.h:444
QgsMapLayer::isValid
bool isValid
Definition: qgsmaplayer.h:81
QgsMapRendererJob::mPerLayerRenderingTime
QHash< QgsWeakMapLayerPointer, int > mPerLayerRenderingTime
Render time (in ms) per layer, by layer ID.
Definition: qgsmaprendererjob.h:499
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgsmaprenderercache.h
QgsMapRendererCache::cacheImage
QImage cacheImage(const QString &cacheKey) const
Returns the cached image for the specified cacheKey.
Definition: qgsmaprenderercache.cpp:213
QgsCoordinateTransform::isValid
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Definition: qgscoordinatetransform.cpp:900
QgsMapLayerStyleOverride
Restore overridden layer style on destruction.
Definition: qgsmaplayerstyle.h:81
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsMapLayerElevationProperties::isVisibleInZRange
virtual bool isVisibleInZRange(const QgsDoubleRange &range) const
Returns true if the layer should be visible and rendered for the specified z range.
Definition: qgsmaplayerelevationproperties.cpp:71
QgsMapRendererCache
This class is responsible for keeping cache of rendered images resulting from a map rendering job.
Definition: qgsmaprenderercache.h:47
QgsVectorLayerUtils::symbolLayerMasks
static QgsMaskedLayers symbolLayerMasks(const QgsVectorLayer *)
Returns all masks that may be defined on symbol layers for a given vector layer.
Definition: qgsvectorlayerutils.cpp:994
QgsMapRendererJob::prepareLabelingJob
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3728
qgsmaplayerlistutils_p.h
QgsCoordinateTransform::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Definition: qgscoordinatetransform.cpp:267
QgsMapRendererJob::setLayerRenderingTimeHints
void setLayerRenderingTimeHints(const QHash< QString, int > &hints)
Sets approximate render times (in ms) for map layers.
QgsMapRendererJob::errors
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
QgsMapLayer::isInScaleRange
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
Definition: qgsmaplayer.cpp:832
QgsCoordinateReferenceSystem::isGeographic
bool isGeographic
Definition: qgscoordinatereferencesystem.h:216
qgsmaplayertemporalproperties.h
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsSymbolLayer
Definition: qgssymbollayer.h:54
QgsMapRendererJob::start
void start()
Start the rendering job and immediately return.
QgsMapRendererJob::perLayerRenderingTime
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
Qgis::MapSettingsFlag::ForceRasterMasks
@ ForceRasterMasks
Force symbol masking to be applied using a raster method. This is considerably faster when compared t...
QgsMapRendererJob::labelingEngineFeedback
QgsLabelingEngineFeedback * labelingEngineFeedback()
Returns the associated labeling engine feedback object.
QgsMapSettings::testFlag
bool testFlag(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
Definition: qgsmapsettings.cpp:395
QgsCoordinateTransform::setDestinationCrs
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination coordinate reference system.
Definition: qgscoordinatetransform.cpp:206
QgsCoordinateTransform::setBallparkTransformsAreAppropriate
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
Definition: qgscoordinatetransform.cpp:939
QgsMapLayerType::GroupLayer
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
QgsPointXY::toString
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Definition: qgspointxy.cpp:51
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsMapRendererJob::setCache
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
QgsVectorLayer::labeling
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
Definition: qgsvectorlayer.h:1656
QgsMapRendererJob::LABEL_PREVIEW_CACHE_ID
static const QString LABEL_PREVIEW_CACHE_ID
QgsMapRendererCache ID string for cached label image during preview compositions only.
Definition: qgsmaprendererjob.h:448
qgsmaplayer.h
QgsMapLayerType::RasterLayer
@ RasterLayer
Raster layer.
QgsMapSettings::zRange
QgsDoubleRange zRange() const
Returns the range of z-values which will be visible in the map.
Definition: qgsmapsettings.cpp:848
QgsMapRendererJob::composeSecondPass
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob, bool forceVector=false)
Compose second pass images into first pass images.
QgsMapSettings::backgroundColor
QColor backgroundColor() const
Returns the background color of the map.
Definition: qgsmapsettings.h:388
Qgis::RenderContextFlag::ApplyClipAfterReprojection
@ ApplyClipAfterReprojection
Feature geometry clipping to mapExtent() must be performed after the geometries are transformed using...
QgsMapLayer::temporalProperties
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1502
QgsMapLayerTemporalProperties::isVisibleInTemporalRange
virtual bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const
Returns true if the layer should be visible and rendered for the specified time range.
Definition: qgsmaplayertemporalproperties.cpp:25
qgsrendercontext.h
QgsMapRendererJob::labelSink
QgsLabelSink * labelSink() const
Returns the label sink associated to this rendering job.
Definition: qgsmaprendererjob.h:382
QgsLabelingEngine::run
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
Qgis::MapSettingsFlag::ForceVectorOutput
@ ForceVectorOutput
Vector graphics should not be cached and drawn as raster images.
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
QgsMapRendererJob::takeRenderedItemResults
QgsRenderedItemResults * takeRenderedItemResults()
Takes the rendered item results from the map render job and returns them.
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
qgsvectorlayerutils.h
qgsmaprendererjob.h
QgsMapRendererCache::dependentLayers
QList< QgsMapLayer * > dependentLayers(const QString &cacheKey) const
Returns a list of map layers on which an image in the cache depends.
Definition: qgsmaprenderercache.cpp:271
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
qgsmaplayerrenderer.h
QgsMapRendererJob::cleanupLabelJob
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
qgssymbollayer.h
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:169
QgsMapSettings::scale
double scale() const
Returns the calculated map scale.
Definition: qgsmapsettings.cpp:458
QgsMapRendererJob::mLayerRenderingTimeHints
QHash< QString, int > mLayerRenderingTimeHints
Approximate expected layer rendering time per layer, by layer ID.
Definition: qgsmaprendererjob.h:506
QgsMapLayer::hasScaleBasedVisibility
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
Definition: qgsmaplayer.cpp:839
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
qgsrenderer.h
QgsMapLayer::minimumScale
double minimumScale() const
Returns the minimum map scale (i.e.
Definition: qgsmaplayer.cpp:904
QgsMapLayer::maximumScale
double maximumScale() const
Returns the maximum map scale (i.e.
Definition: qgsmaplayer.cpp:888
QgsMapRendererCache::hasAnyCacheImage
bool hasAnyCacheImage(const QString &cacheKey, double minimumScaleThreshold=0, double maximumScaleThreshold=0) const
Returns true if the cache contains an image with the specified cacheKey with any cache's parameters (...
Definition: qgsmaprenderercache.cpp:192
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
QgsDoubleRange::isInfinite
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition: qgsrange.h:247
QgsLabelingEngine
The QgsLabelingEngine class provides map labeling functionality. The input for the engine is a list o...
Definition: qgslabelingengine.h:343
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsMapSettings::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Definition: qgsmapsettings.cpp:358
QgsMapSettings::deviceOutputSize
QSize deviceOutputSize() const
Returns the device output size of the map render.
Definition: qgsmapsettings.cpp:262
QgsMapRendererJob::cleanupJobs
void cleanupJobs(std::vector< LayerRenderJob > &jobs)
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
QgsMapRendererJob::settingsLogCanvasRefreshEvent
static const QgsSettingsEntryBool settingsLogCanvasRefreshEvent
Settings entry log canvas refresh event.
Definition: qgsmaprendererjob.h:452
QgsMapLayer::customProperty
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Definition: qgsmaplayer.cpp:1999
QgsMapRendererJob::layersRedrawnFromCache
QStringList layersRedrawnFromCache() const
Returns a list of the layer IDs for all layers which were redrawn from cached images.
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsMapRendererJob::initSecondPassJobs
void initSecondPassJobs(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob) const
Initialize secondPassJobs according to what have been rendered (mask clipping path e....
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsMapRendererJob::mSettings
QgsMapSettings mSettings
Definition: qgsmaprendererjob.h:490
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsVectorLayerRenderer::featureRenderer
QgsFeatureRenderer * featureRenderer()
Returns the feature renderer.
Definition: qgsvectorlayerrenderer.h:71
qgssettings.h
QgsMapLayerType::VectorTileLayer
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
QgsMapRendererJob
Abstract base class for map rendering implementations.
Definition: qgsmaprendererjob.h:269
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:76
QgsMapLayerRenderer::setLayerRenderingTimeHint
virtual void setLayerRenderingTimeHint(int time)
Sets approximate render time (in ms) for the layer to render.
Definition: qgsmaplayerrenderer.h:139
QgsMapRendererCache::clearCacheImage
void clearCacheImage(const QString &cacheKey)
Removes an image from the cache with matching cacheKey.
Definition: qgsmaprenderercache.cpp:310
QgsMapRendererCache::updateParameters
bool updateParameters(const QgsRectangle &extent, const QgsMapToPixel &mtp)
Sets extent and scale parameters.
Definition: qgsmaprenderercache.cpp:106
QgsMaskedLayers
QHash< QString, QgsMaskedLayer > QgsMaskedLayers
Definition: qgsvectorlayerutils.h:39
QgsAbstractVectorLayerLabeling::requiresAdvancedEffects
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
qgsexception.h
QgsMapRendererJob::cleanupSecondPassJobs
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
qgsvectorlayerlabeling.h
QgsMapRendererJob::Errors
QList< QgsMapRendererJob::Error > Errors
Definition: qgsmaprendererjob.h:365
QgsRectangle::grow
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
QgsRenderedItemResults
Stores collated details of rendered items during a map rendering operation.
Definition: qgsrendereditemresults.h:42
QgsMapRendererJob::layerImageToBeComposed
static QImage layerImageToBeComposed(const QgsMapSettings &settings, const LayerRenderJob &job, const QgsMapRendererCache *cache)
qgslogger.h
QgsMapSettings::outputDpi
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
Definition: qgsmapsettings.cpp:267
QgsMapRendererQImageJob::QgsMapRendererQImageJob
QgsMapRendererQImageJob(const QgsMapSettings &settings)
QgsCoordinateTransform::transform
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Definition: qgscoordinatetransform.cpp:272
QgsMapSettings
The QgsMapSettings class contains configuration for rendering of the map. The rendering itself is don...
Definition: qgsmapsettings.h:88
QgsMapSettings::visibleExtent
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
Definition: qgsmapsettings.cpp:411
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsMapLayerType::PointCloudLayer
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
QgsMapRendererJob::~QgsMapRendererJob
~QgsMapRendererJob() override
QgsMapRendererCache::setCacheImageWithParameters
void setCacheImageWithParameters(const QString &cacheKey, const QImage &image, const QgsRectangle &extent, const QgsMapToPixel &mapToPixel, const QList< QgsMapLayer * > &dependentLayers=QList< QgsMapLayer * >())
Set the cached image for a particular cacheKey, using a specific extent and mapToPixel (which may dif...
Definition: qgsmaprenderercache.cpp:135
QgsMapRendererCache::transformedCacheImage
QImage transformedCacheImage(const QString &cacheKey, const QgsMapToPixel &mtp) const
Returns the cached image for the specified cacheKey transformed to the particular extent and scale.
Definition: qgsmaprenderercache.cpp:226
qgssymbol.h
QgsMapSettings::mapToPixel
const QgsMapToPixel & mapToPixel() const
Definition: qgsmapsettings.h:527
QgsLabelingEngineFeedback
QgsFeedback subclass for granular reporting of labeling engine progress.
Definition: qgslabelingengine.h:214
QgsVectorLayerUtils::labelMasks
static QHash< QString, QgsMaskedLayers > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
Definition: qgsvectorlayerutils.cpp:944
qgsproject.h
QgsMapLayerType::PluginLayer
@ PluginLayer
Plugin based layer.
QgsMapLayer::elevationProperties
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Definition: qgsmaplayer.h:1509
QgsMapSettings::layers
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
Definition: qgsmapsettings.cpp:299
QgsVectorLayer::labelsEnabled
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
Definition: qgsvectorlayer.cpp:789
qgslabelingengine.h
QgsVectorLayerRenderer
Implementation of threaded rendering for vector layers.
Definition: qgsvectorlayerrenderer.h:58
qgsmaplayerelevationproperties.h
qgsrendereditemresults.h
QgsMapLayer::createMapRenderer
virtual QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext)=0
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
QgsSettingsEntryByValue::value
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
Definition: qgssettingsentry.h:520
qgsmessagelog.h
QgsMapRendererJob::finished
void finished()
emitted when asynchronous rendering is finished (or canceled).
QgsSymbolLayerUtils::toSymbolLayerPointers
static QSet< const QgsSymbolLayer * > toSymbolLayerPointers(QgsFeatureRenderer *renderer, const QSet< QgsSymbolLayerId > &symbolLayerIds)
Converts a set of symbol layer id to a set of pointers to actual symbol layers carried by the feature...
Definition: qgssymbollayerutils.cpp:4897
QgsMapRendererJob::drawLabeling
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:80