19#include <QElapsedTimer> 
   21#include <QtConcurrentMap> 
   64LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
 
   66  mContext = std::move( other.mContext );
 
   71  renderer = other.renderer;
 
   72  other.renderer = 
nullptr;
 
   74  previewRenderImage = other.previewRenderImage;
 
   75  other.previewRenderImage = 
nullptr;
 
   77  imageInitialized = other.imageInitialized;
 
   78  previewRenderImageInitialized = other.previewRenderImageInitialized;
 
   80  blendMode = other.blendMode;
 
   81  opacity = other.opacity;
 
   82  cached = other.cached;
 
   84  renderAboveLabels = other.renderAboveLabels;
 
   85  completed = other.completed;
 
   86  renderingTime = other.renderingTime;
 
   87  estimatedRenderingTime = other.estimatedRenderingTime ;
 
   88  errors = other.errors;
 
   89  layerId = other.layerId;
 
   91  maskPaintDevice = std::move( other.maskPaintDevice );
 
   93  firstPassJob = other.firstPassJob;
 
   94  other.firstPassJob = 
nullptr;
 
   96  picture = std::move( other.picture );
 
   98  maskJobs = other.maskJobs;
 
  100  maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
 
  102  elevationMap = other.elevationMap;
 
  103  maskPainter = std::move( other.maskPainter );
 
  108LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
 
  109  : imageInitialized( other.imageInitialized )
 
  110  , previewRenderImageInitialized( other.previewRenderImageInitialized )
 
  111  , blendMode( other.blendMode )
 
  112  , opacity( other.opacity )
 
  113  , cached( other.cached )
 
  114  , renderAboveLabels( other.renderAboveLabels )
 
  115  , layer( other.layer )
 
  116  , completed( other.completed )
 
  117  , renderingTime( other.renderingTime )
 
  118  , estimatedRenderingTime( other.estimatedRenderingTime )
 
  119  , errors( other.errors )
 
  120  , layerId( other.layerId )
 
  121  , maskPainter( nullptr ) 
 
  122  , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
 
  123  , maskJobs( other.maskJobs )
 
  125  mContext = std::move( other.mContext );
 
  130  previewRenderImage = other.previewRenderImage;
 
  131  other.previewRenderImage = 
nullptr;
 
  133  renderer = other.renderer;
 
  134  other.renderer = 
nullptr;
 
  136  elevationMap = other.elevationMap;
 
  137  other.elevationMap = 
nullptr;
 
  139  maskPaintDevice = std::move( other.maskPaintDevice );
 
  141  firstPassJob = other.firstPassJob;
 
  142  other.firstPassJob = 
nullptr;
 
  144  picture = std::move( other.picture );
 
  147bool LayerRenderJob::imageCanBeComposed()
 const 
  149  if ( imageInitialized )
 
  153      return renderer->isReadyToCompose();
 
  167  : mSettings( settings )
 
  213  return mLabelingEngineFeedback;
 
  218  QHash<QgsMapLayer *, int> result;
 
  221    if ( 
auto &&lKey = it.key() )
 
  222      result.insert( lKey, it.value() );
 
  242  QSet< QgsMapLayer * > labeledLayers;
 
  249    switch ( ml->type() )
 
  297    bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
 
  326    static const double SPLIT_COORD = 180.0;
 
  338        QgsDebugMsgLevel( QStringLiteral( 
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
 
  341                          .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
 
  342                          .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
 
  376        if ( ll.
x() > ur.
x() )
 
  405        extent = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  415    extent = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  416    r2 = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  423QImage *QgsMapRendererJob::allocateImage( QString layerId )
 
  430  if ( image->isNull() )
 
  439QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
 
  442  if ( !elevationMap->isValid() )
 
  447  return elevationMap.release();
 
  450QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image, 
const QgsRenderContext *context )
 
  452  QPainter *painter = 
nullptr;
 
  453  image = allocateImage( layerId );
 
  456    painter = 
new QPainter( image );
 
  462QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter( 
const QgsRenderContext *context )
 
  464  std::unique_ptr<QPicture> picture = std::make_unique<QPicture>();
 
  465  QPainter *painter = 
new QPainter( picture.get() );
 
  467  return { std::move( picture ), painter };
 
  472  std::vector< LayerRenderJob > layerJobs;
 
  481    Q_UNUSED( cacheValid )
 
  482    QgsDebugMsgLevel( QStringLiteral( 
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
 
  487  while ( li.hasPrevious() )
 
  491    QgsDebugMsgLevel( QStringLiteral( 
"layer %1:  minscale:%2  maxscale:%3  scaledepvis:%4  blendmode:%5 isValid:%6" )
 
  508      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
 
  514      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not visible within the map's time range" ), 3 );
 
  520      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not visible within the map's z range" ), 3 );
 
  529    bool haveExtentInLayerCrs = 
true;
 
  532      haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
 
  537      mErrors.append( Error( ml->
id(), tr( 
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
 
  548      if ( ( vl && vl->
isEditable() ) || requiresLabeling )
 
  554    layerJobs.emplace_back( LayerRenderJob() );
 
  555    LayerRenderJob &job = layerJobs.back();
 
  557    job.layerId = ml->
id();
 
  558    job.renderAboveLabels = ml->
customProperty( QStringLiteral( 
"rendering/renderAboveLabels" ) ).toBool();
 
  562    if ( !ml->
customProperty( QStringLiteral( 
"_noset_layer_expression_context" ) ).toBool() )
 
  564    job.context()->setPainter( painter );
 
  565    job.context()->setLabelingEngine( labelingEngine2 );
 
  566    job.context()->setLabelSink( 
labelSink() );
 
  567    job.context()->setCoordinateTransform( ct );
 
  568    job.context()->setExtent( r1 );
 
  574    if ( mFeatureFilterProvider )
 
  575      job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
 
  608      job.imageInitialized = 
true;
 
  616      job.renderer = 
nullptr;
 
  617      job.context()->setPainter( 
nullptr );
 
  622    QElapsedTimer layerTime;
 
  628      job.context()->setFeedback( job.renderer->feedback() );
 
  634    if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
 
  637      job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
 
  641        job.renderer = 
nullptr;
 
  642        layerJobs.pop_back();
 
  651      job.elevationMap = allocateElevationMap( ml->
id() );
 
  652      job.context()->setElevationMap( job.elevationMap );
 
  660        if ( !cachedImage.isNull() )
 
  662          job.previewRenderImage = 
new QImage( cachedImage );
 
  663          job.previewRenderImageInitialized = 
true;
 
  664          job.context()->setPreviewRenderPainter( 
new QPainter( job.previewRenderImage ) );
 
  665          job.context()->setPainterFlagsUsingContext( painter );
 
  668      if ( !job.previewRenderImage )
 
  670        job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
 
  671        job.previewRenderImageInitialized = 
false;
 
  674      if ( !job.previewRenderImage )
 
  676        delete job.context()->previewRenderPainter();
 
  677        job.context()->setPreviewRenderPainter( 
nullptr );
 
  681    job.renderingTime = layerTime.elapsed(); 
 
  689  const bool useGeometryBackend = 
settingsMaskBackend->
value().compare( QLatin1String( 
"geometry" ), Qt::CaseInsensitive ) == 0;
 
  691  std::vector< LayerRenderJob > secondPassJobs;
 
  694  QHash<QString, LayerRenderJob *> layerJobMapping;
 
  697  QMap<QString, bool> maskLayerHasEffects;
 
  698  QMap<int, bool> labelHasEffects;
 
  706    MaskSource( 
const QString &layerId_, 
const QString &labelRuleId_, 
int labelMaskId_, 
bool hasEffects_ ):
 
  707      layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
 
  712  QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
 
  719  for ( LayerRenderJob &job : firstPassJobs )
 
  721    layerJobMapping[job.layerId] = &job;
 
  726  for ( LayerRenderJob &job : firstPassJobs )
 
  733    auto collectMasks = [&]( 
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(), 
int labelMaskId = -1 )
 
  735      bool hasEffects = 
false;
 
  736      for ( 
auto it = masks->begin(); it != masks->end(); ++it )
 
  738        auto lit = maskedSymbolLayers.find( it.key() );
 
  739        if ( lit == maskedSymbolLayers.end() )
 
  741          maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
 
  745          if ( lit->first != it.value().symbolLayerIds )
 
  747            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() ) );
 
  750          lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
 
  752        hasEffects |= it.value().hasEffects;
 
  754      if ( ! masks->isEmpty() && labelMaskId == -1 )
 
  755        maskLayerHasEffects[ sourceLayerId ] = hasEffects;
 
  760    for ( 
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
 
  762      QString labelRule = it.key();
 
  768      for ( 
auto mit = masks.begin(); mit != masks.end(); mit++ )
 
  770        const QString sourceLayerId = mit.key();
 
  772        if ( !layerJobMapping.contains( sourceLayerId ) )
 
  775          usableMasks.insert( sourceLayerId, mit.value() );
 
  778      if ( usableMasks.empty() )
 
  782      QSet<QgsSymbolLayerReference> slRefs;
 
  783      bool hasEffects = 
false;
 
  784      for ( 
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
 
  786        const QString sourceLayerId = mit.key();
 
  788        if ( !layerJobMapping.contains( sourceLayerId ) )
 
  791        for ( 
const QString &symbolLayerId : mit.value().symbolLayerIds )
 
  794        hasEffects |= mit.value().hasEffects;
 
  797      int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
 
  798      labelHasEffects[ labelMaskId ] = hasEffects;
 
  801      collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
 
  806    collectMasks( &symbolLayerMasks, vl->
id() );
 
  809  if ( maskedSymbolLayers.isEmpty() )
 
  810    return secondPassJobs;
 
  813  for ( 
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
 
  815    QPaintDevice *maskPaintDevice = 
nullptr;
 
  816    QPainter *maskPainter = 
nullptr;
 
  817    if ( forceVector && !labelHasEffects[ maskId ] )
 
  820      if ( useGeometryBackend )
 
  822        std::unique_ptr< QgsGeometryPaintDevice > geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( 
true );
 
  823        geomPaintDevice->setStrokedPathSegments( 4 );
 
  824        geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
 
  825        maskPaintDevice = geomPaintDevice.release();
 
  831      maskPainter = 
new QPainter( maskPaintDevice );
 
  836      QImage *maskImage = 
nullptr;
 
  837      maskPainter = allocateImageAndPainter( QStringLiteral( 
"label mask" ), maskImage, &labelJob.context );
 
  838      maskImage->fill( 0 );
 
  839      maskPaintDevice = maskImage;
 
  842    labelJob.context.setMaskPainter( maskPainter, maskId );
 
  843    labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
 
  844    labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
 
  846  labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
 
  857  if ( !labelJob.img && !forceVector )
 
  859    labelJob.img = allocateImage( QStringLiteral( 
"labels" ) );
 
  861  else if ( !labelJob.picture && forceVector )
 
  863    labelJob.picture.reset( 
new QPicture() );
 
  867  for ( LayerRenderJob &job : firstPassJobs )
 
  869    job.maskRequiresLayerRasterization = 
false;
 
  871    auto it = maskedSymbolLayers.find( job.layerId );
 
  872    if ( it != maskedSymbolLayers.end() )
 
  874      const QList<MaskSource> &sourceList = it->second;
 
  875      for ( 
const MaskSource &source : sourceList )
 
  877        job.maskRequiresLayerRasterization |= source.hasEffects;
 
  882    const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
 
  883    if ( isRasterRendering && !job.img )
 
  885      job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
 
  887    else if ( !isRasterRendering && !job.picture )
 
  889      PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
 
  890      job.picture = std::move( pictureAndPainter.first );
 
  891      job.context()->setPainter( pictureAndPainter.second );
 
  894      job.renderer = job.layer->createMapRenderer( *( job.context() ) );
 
  898    if ( maskLayerHasEffects.contains( job.layerId ) )
 
  900      QPaintDevice *maskPaintDevice = 
nullptr;
 
  901      QPainter *maskPainter = 
nullptr;
 
  902      if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
 
  905        if ( useGeometryBackend )
 
  907          std::unique_ptr< QgsGeometryPaintDevice > geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
 
  908          geomPaintDevice->setStrokedPathSegments( 4 );
 
  909          geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
 
  910          maskPaintDevice = geomPaintDevice.release();
 
  917        maskPainter = 
new QPainter( maskPaintDevice );
 
  922        QImage *maskImage = 
nullptr;
 
  923        maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
 
  924        maskImage->fill( 0 );
 
  925        maskPaintDevice = maskImage;
 
  928      job.context()->setMaskPainter( maskPainter );
 
  929      job.maskPainter.reset( maskPainter );
 
  930      job.maskPaintDevice.reset( maskPaintDevice );
 
  934  for ( LayerRenderJob &job : firstPassJobs )
 
  938    auto it = maskedSymbolLayers.find( job.layerId );
 
  939    if ( it == maskedSymbolLayers.end() )
 
  942    QList<MaskSource> &sourceList = it->second;
 
  943    const QSet<QString> symbolList = it->first;
 
  945    secondPassJobs.emplace_back( LayerRenderJob() );
 
  946    LayerRenderJob &job2 = secondPassJobs.back();
 
  948    job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
 
  951    for ( MaskSource &source : sourceList )
 
  953      if ( source.labelMaskId != -1 )
 
  954        job2.maskJobs.push_back( qMakePair( 
nullptr, source.labelMaskId ) );
 
  956        job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
 
  960    job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
 
  962    job2.layer = job.layer;
 
  963    job2.renderAboveLabels = job.renderAboveLabels;
 
  964    job2.layerId = job.layerId;
 
  967    job2.firstPassJob = &job;
 
  969    if ( !forceVector || job2.maskRequiresLayerRasterization )
 
  971      job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
 
  975      PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
 
  976      job2.picture = std::move( pictureAndPainter.first );
 
  977      job2.context()->setPainter( pictureAndPainter.second );
 
  980    if ( ! job2.img && ! job2.picture )
 
  982      secondPassJobs.pop_back();
 
  989    job2.renderer = mapRenderer;
 
  992      job2.context()->setFeedback( job2.renderer->feedback() );
 
  997    job2.context()->setDisabledSymbolLayersV2( symbolList );
 
 1000  return secondPassJobs;
 
 1008  for ( LayerRenderJob &job : secondPassJobs )
 
 1010    if ( job.maskRequiresLayerRasterization )
 
 1016    for ( 
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
 
 1018      QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
 
 1020      const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
 
 1023        QPainterPath path = maskDevice->maskPainterPath();
 
 1024        for ( 
const QString &symbolLayerId : layers )
 
 1026          job.context()->addSymbolLayerClipPath( symbolLayerId, path );
 
 1031        QgsGeometry geometry( geometryDevice->geometry().clone() );
 
 1033#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10 
 1040        for ( 
const QString &symbolLayerId : layers )
 
 1042          job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
 
 1047    job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
 
 1055  job.context.setPainter( painter );
 
 1056  job.context.setLabelingEngine( labelingEngine2 );
 
 1057  job.context.setFeedback( mLabelingEngineFeedback );
 
 1061  job.context.setExtent( r1 );
 
 1063  job.context.setFeatureFilterProvider( mFeatureFilterProvider );
 
 1066  job.context.setCoordinateTransform( ct );
 
 1077    job.complete = 
true;
 
 1080    job.context.setPainter( 
nullptr );
 
 1084    if ( canUseLabelCache && ( 
mCache || !painter ) )
 
 1086      job.img = allocateImage( QStringLiteral( 
"labels" ) );
 
 1096  for ( LayerRenderJob &job : jobs )
 
 1100      delete job.context()->painter();
 
 1101      job.context()->setPainter( 
nullptr );
 
 1103      if ( 
mCache && !job.cached && job.completed && job.layer )
 
 1105        QgsDebugMsgLevel( QStringLiteral( 
"caching image for %1" ).arg( job.layerId ), 2 );
 
 1114    if ( job.previewRenderImage )
 
 1116      delete job.context()->previewRenderPainter();
 
 1117      job.context()->setPreviewRenderPainter( 
nullptr );
 
 1118      delete job.previewRenderImage;
 
 1119      job.previewRenderImage = 
nullptr;
 
 1122    if ( job.elevationMap )
 
 1124      job.context()->setElevationMap( 
nullptr );
 
 1125      if ( 
mCache && !job.cached && job.completed && job.layer )
 
 1127        QgsDebugMsgLevel( QStringLiteral( 
"caching elevation map for %1" ).arg( job.layerId ), 2 );
 
 1130          job.elevationMap->rawElevationImage(),
 
 1133          QList< QgsMapLayer * >() << job.layer );
 
 1136          job.elevationMap->rawElevationImage(),
 
 1139          QList< QgsMapLayer * >() << job.layer );
 
 1142      delete job.elevationMap;
 
 1143      job.elevationMap = 
nullptr;
 
 1148      delete job.context()->painter();
 
 1149      job.context()->setPainter( 
nullptr );
 
 1150      job.picture.reset( 
nullptr );
 
 1155      const QStringList 
errors = job.renderer->errors();
 
 1156      for ( 
const QString &message : 
errors )
 
 1157        mErrors.append( Error( job.renderer->layerId(), message ) );
 
 1159      mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
 
 1161      delete job.renderer;
 
 1162      job.renderer = 
nullptr;
 
 1168    job.maskPainter.reset( 
nullptr );
 
 1169    job.maskPaintDevice.reset( 
nullptr );
 
 1177  for ( LayerRenderJob &job : jobs )
 
 1181      delete job.context()->painter();
 
 1182      job.context()->setPainter( 
nullptr );
 
 1188    if ( job.previewRenderImage )
 
 1190      delete job.context()->previewRenderPainter();
 
 1191      job.context()->setPreviewRenderPainter( 
nullptr );
 
 1192      delete job.previewRenderImage;
 
 1193      job.previewRenderImage = 
nullptr;
 
 1198      delete job.context()->painter();
 
 1199      job.context()->setPainter( 
nullptr );
 
 1204      delete job.renderer;
 
 1205      job.renderer = 
nullptr;
 
 1219    if ( 
mCache && !job.cached && !job.context.renderingStopped() )
 
 1230  job.picture.reset( 
nullptr );
 
 1231  job.maskPainters.clear();
 
 1232  job.maskPaintDevices.clear();
 
 1236#define DEBUG_RENDERING 0 
 1239                                        const std::vector<LayerRenderJob> &jobs,
 
 1240                                        const LabelRenderJob &labelJob,
 
 1246  image.setDotsPerMeterX( 
static_cast<int>( settings.
outputDpi() * 39.37 ) );
 
 1247  image.setDotsPerMeterY( 
static_cast<int>( settings.
outputDpi() * 39.37 ) );
 
 1251  std::unique_ptr<QgsElevationMap> mainElevationMap;
 
 1252  if ( mapShadingRenderer.
isActive() )
 
 1255  QPainter painter( &image );
 
 1260  for ( 
const LayerRenderJob &job : jobs )
 
 1262    if ( job.renderAboveLabels )
 
 1269    painter.setCompositionMode( job.blendMode );
 
 1270    painter.setOpacity( job.opacity );
 
 1272    if ( mainElevationMap )
 
 1275      if ( layerElevationMap.
isValid() )
 
 1281    img.save( QString( 
"/tmp/final_%1.png" ).arg( i ) );
 
 1285    painter.drawImage( 0, 0, img );
 
 1288  if ( mapShadingRenderer.
isActive() &&  mainElevationMap )
 
 1296  if ( labelJob.img && labelJob.complete )
 
 1298    painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1299    painter.setOpacity( 1.0 );
 
 1300    painter.drawImage( 0, 0, *labelJob.img );
 
 1308    painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1309    painter.setOpacity( 1.0 );
 
 1310    painter.drawImage( 0, 0, labelCacheImage );
 
 1314  for ( 
const LayerRenderJob &job : jobs )
 
 1316    if ( !job.renderAboveLabels )
 
 1323    painter.setCompositionMode( job.blendMode );
 
 1324    painter.setOpacity( job.opacity );
 
 1326    painter.drawImage( 0, 0, img );
 
 1331  image.save( 
"/tmp/final.png" );
 
 1338  const LayerRenderJob &job,
 
 1342  if ( job.imageCanBeComposed() )
 
 1344    if ( job.previewRenderImage && !job.completed )
 
 1345      return *job.previewRenderImage;
 
 1347    Q_ASSERT( job.img );
 
 1352    if ( cache && cache->
hasAnyCacheImage( job.layerId + QStringLiteral( 
"_preview" ) ) )
 
 1363  if ( job.imageCanBeComposed() && job.elevationMap )
 
 1365    return *job.elevationMap;
 
 1379  for ( LayerRenderJob &job : secondPassJobs )
 
 1381    const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
 
 1384    if ( isRasterRendering && job.maskJobs.size() > 1 )
 
 1386      QPainter *maskPainter = 
nullptr;
 
 1387      for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
 
 1389        QImage *maskImage = 
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
 
 1392          maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
 
 1396          maskPainter->drawImage( 0, 0, *maskImage );
 
 1401    if ( ! job.maskJobs.isEmpty() )
 
 1404      QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
 
 1405      if ( isRasterRendering )
 
 1407        QImage *maskImage = 
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
 
 1410        QPainter *painter = job.context()->painter();
 
 1412        painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
 
 1417        QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
 
 1418        QVector<QRgb> mswTable;
 
 1419        mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
 
 1420        mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
 
 1421        maskBinAlpha.setColorTable( mswTable );
 
 1422        painter->drawImage( 0, 0, maskBinAlpha );
 
 1426          QPainter tempPainter;
 
 1429          QPainter *painter1 = job.firstPassJob->context()->painter();
 
 1432            tempPainter.begin( job.firstPassJob->img );
 
 1433            painter1 = &tempPainter;
 
 1437          painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
 
 1438          painter1->drawImage( 0, 0, *maskImage );
 
 1441          painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
 
 1442          painter1->drawImage( 0, 0, *job.img );
 
 1447        job.firstPassJob->picture = std::move( job.picture );
 
 1448        job.picture = 
nullptr;
 
 1459  QMultiMap<int, QString> elapsed;
 
 1460  for ( 
const LayerRenderJob &job : jobs )
 
 1462  for ( 
const LayerRenderJob &job : secondPassJobs )
 
 1463    elapsed.insert( job.
renderingTime, job.layerId + QString( 
" (second pass)" ) );
 
 1465  elapsed.insert( labelJob.renderingTime, tr( 
"Labeling" ) );
 
 1467  QList<int> tt( elapsed.uniqueKeys() );
 
 1468  std::sort( tt.begin(), tt.end(), std::greater<int>() );
 
 1469  for ( 
int t : std::as_const( tt ) )
 
 1471    QgsMessageLog::logMessage( tr( 
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String( 
", " ) ) ), tr( 
"Rendering" ) );
 
 1480  std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
 
 1483    labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"(labeling)" ), QStringLiteral( 
"rendering" ) );
 
 1490  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1494  if ( labelingEngine2 )
 
 1496    labelingEngine2->
run( renderContext );
 
 1499  QgsDebugMsgLevel( QStringLiteral( 
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
 
 1504  Q_UNUSED( settings )
 
 1506  drawLabeling( renderContext, labelingEngine2, painter );
 
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
 
@ Group
Composite group layer. Added in QGIS 3.24.
 
@ Plugin
Plugin based layer.
 
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
 
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
 
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
 
@ Mesh
Mesh layer. Added in QGIS 3.2.
 
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
 
@ RenderPartialOutputOverPreviousCachedImage
When rendering temporary in-progress preview renders, these preview renders can be drawn over any pre...
 
@ RenderPartialOutputs
The renderer benefits from rendering temporary in-progress preview renders. These are temporary resul...
 
@ ApplyClipAfterReprojection
Feature geometry clipping to mapExtent() must be performed after the geometries are transformed using...
 
@ RecordProfile
Enable run-time profiling while rendering (since QGIS 3.34)
 
@ Linework
Combines all rings into a set of noded lines and then extracts valid polygons from that linework.
 
@ Structure
Structured method, first makes all rings valid and then merges shells and subtracts holes from shells...
 
@ Forward
Forward transform (from source to destination)
 
@ Reverse
Reverse/inverse transform (from destination to source)
 
@ ForceVectorOutput
Vector graphics should not be cached and drawn as raster images.
 
@ RenderPartialOutput
Whether to make extra effort to update map image with partially rendered layers (better for interacti...
 
@ ForceRasterMasks
Force symbol masking to be applied using a raster method. This is considerably faster when compared t...
 
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
 
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
 
Custom exception class for Coordinate Reference System related exceptions.
 
bool isInfinite() const
Returns true if the range consists of all possible values.
 
Stores digital elevation model in a raster image which may get updated as a part of map layer renderi...
 
bool isValid() const
Returns whether the elevation map is valid.
 
This class can render elevation shading on an image with different methods (eye dome lighting,...
 
Qgis::ElevationMapCombineMethod combinedElevationMethod() const
Returns the method used when conbining different elevation sources.
 
bool isActive() const
Returns whether this shading renderer is active.
 
void renderShading(const QgsElevationMap &elevation, QImage &image, const QgsRenderContext &context) const
Render shading on image condidering the elevation map elevation and the renderer context context If e...
 
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
 
A paint device which converts everything renderer to a QgsGeometry representation of the rendered sha...
 
A geometry is the spatial representation of a feature.
 
QgsFeedback subclass for granular reporting of labeling engine progress.
 
The QgsLabelingEngine class provides map labeling functionality.
 
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
 
static void warning(const QString &msg)
Goes to qWarning.
 
virtual bool isVisibleInZRange(const QgsDoubleRange &range, QgsMapLayer *layer=nullptr) const
Returns true if the layer should be visible and rendered for the specified z range.
 
virtual bool hasElevation() const
Returns true if the layer has an elevation or z component.
 
virtual void setLayerRenderingTimeHint(int time)
Sets approximate render time (in ms) for the layer to render.
 
Restore overridden layer style on destruction.
 
virtual bool isVisibleInTemporalRange(const QgsDateTimeRange &range) const
Returns true if the layer should be visible and rendered for the specified time range.
 
Base class for all map layer types.
 
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
 
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
 
QgsCoordinateReferenceSystem crs
 
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
 
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
 
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
 
virtual QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext)=0
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
 
double minimumScale() const
Returns the minimum map scale (i.e.
 
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
 
double maximumScale() const
Returns the maximum map scale (i.e.
 
This class is responsible for keeping cache of rendered images resulting from a map rendering job.
 
bool updateParameters(const QgsRectangle &extent, const QgsMapToPixel &mtp)
Sets extent and scale parameters.
 
QList< QgsMapLayer * > dependentLayers(const QString &cacheKey) const
Returns a list of map layers on which an image in the cache depends.
 
bool hasCacheImage(const QString &cacheKey) const
Returns true if the cache contains an image with the specified cacheKey that has the same extent and ...
 
QImage cacheImage(const QString &cacheKey) const
Returns the cached image for the specified cacheKey.
 
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 (...
 
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...
 
void clearCacheImage(const QString &cacheKey)
Removes an image from the cache with matching cacheKey.
 
QImage transformedCacheImage(const QString &cacheKey, const QgsMapToPixel &mtp) const
Returns the cached image for the specified cacheKey transformed to the particular extent and scale.
 
Abstract base class for map rendering implementations.
 
void logRenderingTime(const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob)
 
static QgsElevationMap layerElevationToBeComposed(const QgsMapSettings &settings, const LayerRenderJob &job, const QgsMapRendererCache *cache)
 
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
 
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
 
void setCache(QgsMapRendererCache *cache)
Assign a cache to be used for reading and storing rendered images of individual layers.
 
QHash< QgsMapLayer *, int > perLayerRenderingTime() const
Returns the render time (in ms) per layer.
 
void initSecondPassJobs(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob) const
Initialize secondPassJobs according to what have been rendered (mask clipping path e....
 
static QImage layerImageToBeComposed(const QgsMapSettings &settings, const LayerRenderJob &job, const QgsMapRendererCache *cache)
 
QHash< QString, int > mLayerRenderingTimeHints
Approximate expected layer rendering time per layer, by layer ID.
 
std::unique_ptr< QgsRenderedItemResults > mRenderedItemResults
 
Errors errors() const
List of errors that happened during the rendering job - available when the rendering has been finishe...
 
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
 
static const QString LABEL_PREVIEW_CACHE_ID
QgsMapRendererCache ID string for cached label image during preview compositions only.
 
std::vector< LayerRenderJob > prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
 
void cleanupJobs(std::vector< LayerRenderJob > &jobs)
 
const QgsMapSettings & mapSettings() const
Returns map settings with which this job was started.
 
QgsMapRendererCache * mCache
 
void finished()
emitted when asynchronous rendering is finished (or canceled).
 
static const QgsSettingsEntryBool * settingsLogCanvasRefreshEvent
Settings entry log canvas refresh event.
 
QgsMapRendererJob(const QgsMapSettings &settings)
 
~QgsMapRendererJob() override
 
void start()
Start the rendering job and immediately return.
 
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
 
QStringList mLayersRedrawnFromCache
 
QStringList layersRedrawnFromCache() const
Returns a list of the layer IDs for all layers which were redrawn from cached images.
 
QList< QgsMapRendererJob::Error > Errors
 
static const QString LABEL_CACHE_ID
QgsMapRendererCache ID string for cached label image.
 
static const QString ELEVATION_MAP_CACHE_PREFIX
QgsMapRendererCache prefix string for cached elevation map image.
 
QHash< QgsWeakMapLayerPointer, int > mPerLayerRenderingTime
Render time (in ms) per layer, by layer ID.
 
QgsRenderedItemResults * takeRenderedItemResults()
Takes the rendered item results from the map render job and returns them.
 
QgsLabelingEngineFeedback * labelingEngineFeedback()
Returns the associated labeling engine feedback object.
 
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob, bool forceVector=false)
Compose second pass images into first pass images.
 
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).
 
static const QgsSettingsEntryString * settingsMaskBackend
Settings entry for mask painting backend engine.
 
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
 
void setLayerRenderingTimeHints(const QHash< QString, int > &hints)
Sets approximate render times (in ms) for map layers.
 
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
 
QgsLabelSink * labelSink() const
Returns the label sink associated to this rendering job.
 
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
 
QgsMapRendererQImageJob(const QgsMapSettings &settings)
 
The QgsMapSettings class contains configuration for rendering of the map.
 
QSize deviceOutputSize() const
Returns the device output size of the map render.
 
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
 
double scale() const
Returns the calculated map scale.
 
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
 
QgsDoubleRange zRange() const
Returns the range of z-values which will be visible in the map.
 
QColor backgroundColor() const
Returns the background color of the map.
 
const QgsMapToPixel & mapToPixel() const
 
float devicePixelRatio() const
Returns the device pixel ratio.
 
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
 
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
 
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
 
Qgis::MapSettingsFlags flags() const
Returns combination of flags used for rendering.
 
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
 
const QgsElevationShadingRenderer & elevationShadingRenderer() const
Returns the shading renderer used to render shading on the entire map.
 
double outputDpi() const
Returns the DPI (dots per inch) used for conversion between real world units (e.g.
 
bool testFlag(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
 
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
 
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
 
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
 
Mask painter device that can be used to register everything painted into a QPainterPath used later as...
 
Represents a mesh layer supporting display of data on structured or unstructured meshes.
 
const QgsAbstractMeshLayerLabeling * labeling() const
Access to const labeling configuration.
 
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
 
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).
 
static bool staticWillUseLayer(const QgsMapLayer *layer)
Called to find out whether a specified layer is used for labeling.
 
A class to represent a 2D point.
 
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
 
Represents a raster layer.
 
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
 
virtual Qgis::RasterRendererFlags flags() const
Returns flags which dictate renderer behavior.
 
A rectangle specified with double values.
 
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
 
double xMinimum() const
Returns the x minimum value (left side of rectangle).
 
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
 
void setXMinimum(double x)
Set the minimum x value.
 
double width() const
Returns the width of the rectangle.
 
double xMaximum() const
Returns the x maximum value (right side of rectangle).
 
double yMaximum() const
Returns the y maximum value (top side of rectangle).
 
void setXMaximum(double x)
Set the maximum x value.
 
void grow(double delta)
Grows the rectangle in place by the specified amount.
 
double height() const
Returns the height of the rectangle.
 
bool isFinite() const
Returns true if the rectangle has finite boundaries.
 
Contains information about the context of a rendering operation.
 
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
 
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
 
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
 
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
 
Stores collated details of rendered items during a map rendering operation.
 
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
 
A boolean settings entry.
 
static QgsSettingsTreeNode * sTreeMap
 
Type used to refer to a specific symbol layer in a symbol of a layer.
 
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
 
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
 
Implementation of threaded rendering for vector layers.
 
static QgsMaskedLayers symbolLayerMasks(const QgsVectorLayer *)
Returns all masks that may be defined on symbol layers for a given vector layer.
 
static QHash< QString, QgsMaskedLayers > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
 
Represents a vector layer which manages a vector based data sets.
 
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
 
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
 
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
QHash< QString, QgsMaskedLayer > QgsMaskedLayers
masked layers where key is the layer id