17#include "moc_qgsmaprendererjob.cpp" 
   20#include <QElapsedTimer> 
   22#include <QtConcurrentMap> 
   66LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
 
   68  mContext = std::move( other.mContext );
 
   73  renderer = other.renderer;
 
   74  other.renderer = 
nullptr;
 
   76  previewRenderImage = other.previewRenderImage;
 
   77  other.previewRenderImage = 
nullptr;
 
   79  imageInitialized = other.imageInitialized;
 
   80  previewRenderImageInitialized = other.previewRenderImageInitialized;
 
   82  blendMode = other.blendMode;
 
   83  opacity = other.opacity;
 
   84  cached = other.cached;
 
   86  renderAboveLabels = other.renderAboveLabels;
 
   87  completed = other.completed;
 
   88  renderingTime = other.renderingTime;
 
   89  estimatedRenderingTime = other.estimatedRenderingTime ;
 
   90  errors = other.errors;
 
   91  layerId = other.layerId;
 
   93  maskPaintDevice = std::move( other.maskPaintDevice );
 
   95  firstPassJob = other.firstPassJob;
 
   96  other.firstPassJob = 
nullptr;
 
   98  picture = std::move( other.picture );
 
  100  maskJobs = other.maskJobs;
 
  102  maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
 
  104  elevationMap = other.elevationMap;
 
  105  maskPainter = std::move( other.maskPainter );
 
  110LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
 
  111  : imageInitialized( other.imageInitialized )
 
  112  , previewRenderImageInitialized( other.previewRenderImageInitialized )
 
  113  , blendMode( other.blendMode )
 
  114  , opacity( other.opacity )
 
  115  , cached( other.cached )
 
  116  , renderAboveLabels( other.renderAboveLabels )
 
  117  , layer( other.layer )
 
  118  , completed( other.completed )
 
  119  , renderingTime( other.renderingTime )
 
  120  , estimatedRenderingTime( other.estimatedRenderingTime )
 
  121  , errors( other.errors )
 
  122  , layerId( other.layerId )
 
  123  , maskPainter( nullptr ) 
 
  124  , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
 
  125  , maskJobs( other.maskJobs )
 
  127  mContext = std::move( other.mContext );
 
  132  previewRenderImage = other.previewRenderImage;
 
  133  other.previewRenderImage = 
nullptr;
 
  135  renderer = other.renderer;
 
  136  other.renderer = 
nullptr;
 
  138  elevationMap = other.elevationMap;
 
  139  other.elevationMap = 
nullptr;
 
  141  maskPaintDevice = std::move( other.maskPaintDevice );
 
  143  firstPassJob = other.firstPassJob;
 
  144  other.firstPassJob = 
nullptr;
 
  146  picture = std::move( other.picture );
 
  149bool LayerRenderJob::imageCanBeComposed()
 const 
  151  if ( imageInitialized )
 
  155      return renderer->isReadyToCompose();
 
  169  : mSettings( settings )
 
  215  return mLabelingEngineFeedback;
 
  220  QHash<QgsMapLayer *, int> result;
 
  223    if ( 
auto &&lKey = it.key() )
 
  224      result.insert( lKey, it.value() );
 
  244  QSet< QgsMapLayer * > labeledLayers;
 
  251    switch ( ml->type() )
 
  308    bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
 
  326    switch ( ml->type() )
 
  394    static const double SPLIT_COORD = 180.0;
 
  406        QgsDebugMsgLevel( QStringLiteral( 
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
 
  409                          .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
 
  410                          .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
 
  444        if ( ll.
x() > ur.
x() )
 
  473        extent = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  483    extent = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  484    r2 = 
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
 
  491QImage *QgsMapRendererJob::allocateImage( QString layerId )
 
  498  if ( image->isNull() )
 
  507QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
 
  510  if ( !elevationMap->isValid() )
 
  515  return elevationMap.release();
 
  518QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image, 
const QgsRenderContext *context )
 
  520  QPainter *painter = 
nullptr;
 
  521  image = allocateImage( layerId );
 
  524    painter = 
new QPainter( image );
 
  530QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter( 
const QgsRenderContext *context )
 
  532  auto picture = std::make_unique<QPicture>();
 
  533  QPainter *painter = 
new QPainter( picture.get() );
 
  535  return { std::move( picture ), painter };
 
  540  std::vector< LayerRenderJob > layerJobs;
 
  549    Q_UNUSED( cacheValid )
 
  550    QgsDebugMsgLevel( QStringLiteral( 
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
 
  555  while ( li.hasPrevious() )
 
  559    QgsDebugMsgLevel( QStringLiteral( 
"layer %1:  minscale:%2  maxscale:%3  scaledepvis:%4  blendmode:%5 isValid:%6" )
 
  576      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
 
  582      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not visible within the map's time range" ), 3 );
 
  588      QgsDebugMsgLevel( QStringLiteral( 
"Layer not rendered because it is not visible within the map's z range" ), 3 );
 
  597    bool haveExtentInLayerCrs = 
true;
 
  600      haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
 
  605      mErrors.append( Error( ml->
id(), tr( 
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
 
  616      if ( ( vl && vl->
isEditable() ) || requiresLabeling )
 
  622    layerJobs.emplace_back( LayerRenderJob() );
 
  623    LayerRenderJob &job = layerJobs.back();
 
  625    job.layerId = ml->
id();
 
  626    job.renderAboveLabels = ml->
customProperty( QStringLiteral( 
"rendering/renderAboveLabels" ) ).toBool();
 
  630    if ( !ml->
customProperty( QStringLiteral( 
"_noset_layer_expression_context" ) ).toBool() )
 
  632    job.context()->setPainter( painter );
 
  633    job.context()->setLabelingEngine( labelingEngine2 );
 
  634    job.context()->setLabelSink( 
labelSink() );
 
  635    job.context()->setCoordinateTransform( ct );
 
  636    job.context()->setExtent( r1 );
 
  642    if ( mFeatureFilterProvider )
 
  644      job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
 
  679      job.imageInitialized = 
true;
 
  687      job.renderer = 
nullptr;
 
  688      job.context()->setPainter( 
nullptr );
 
  693    QElapsedTimer layerTime;
 
  699      job.context()->setFeedback( job.renderer->feedback() );
 
  710    if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
 
  713      job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
 
  717        job.renderer = 
nullptr;
 
  718        layerJobs.pop_back();
 
  727      job.elevationMap = allocateElevationMap( ml->
id() );
 
  728      job.context()->setElevationMap( job.elevationMap );
 
  736        if ( !cachedImage.isNull() )
 
  738          job.previewRenderImage = 
new QImage( cachedImage );
 
  739          job.previewRenderImageInitialized = 
true;
 
  740          job.context()->setPreviewRenderPainter( 
new QPainter( job.previewRenderImage ) );
 
  741          job.context()->setPainterFlagsUsingContext( painter );
 
  744      if ( !job.previewRenderImage )
 
  746        job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
 
  747        job.previewRenderImageInitialized = 
false;
 
  750      if ( !job.previewRenderImage )
 
  752        delete job.context()->previewRenderPainter();
 
  753        job.context()->setPreviewRenderPainter( 
nullptr );
 
  757    job.renderingTime = layerTime.elapsed(); 
 
  765  std::vector< LayerRenderJob > secondPassJobs;
 
  768  QHash<QString, LayerRenderJob *> layerJobMapping;
 
  771  QMap<QString, bool> maskLayerHasEffects;
 
  772  QMap<int, bool> labelHasEffects;
 
  780    MaskSource( 
const QString &layerId_, 
const QString &labelRuleId_, 
int labelMaskId_, 
bool hasEffects_ ):
 
  781      layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
 
  786  QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
 
  793  for ( LayerRenderJob &job : firstPassJobs )
 
  795    layerJobMapping[job.layerId] = &job;
 
  800  for ( LayerRenderJob &job : firstPassJobs )
 
  807    auto collectMasks = [&]( 
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(), 
int labelMaskId = -1 )
 
  809      bool hasEffects = 
false;
 
  810      for ( 
auto it = masks->begin(); it != masks->end(); ++it )
 
  812        auto lit = maskedSymbolLayers.find( it.key() );
 
  813        if ( lit == maskedSymbolLayers.end() )
 
  815          maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
 
  819          if ( lit->first != it.value().symbolLayerIds )
 
  821            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() ) );
 
  824          lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
 
  826        hasEffects |= it.value().hasEffects;
 
  828      if ( ! masks->isEmpty() && labelMaskId == -1 )
 
  829        maskLayerHasEffects[ sourceLayerId ] = hasEffects;
 
  834    for ( 
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
 
  836      QString labelRule = it.key();
 
  842      for ( 
auto mit = masks.begin(); mit != masks.end(); mit++ )
 
  844        const QString sourceLayerId = mit.key();
 
  846        if ( !layerJobMapping.contains( sourceLayerId ) )
 
  849          usableMasks.insert( sourceLayerId, mit.value() );
 
  852      if ( usableMasks.empty() )
 
  856      QSet<QgsSymbolLayerReference> slRefs;
 
  857      bool hasEffects = 
false;
 
  858      for ( 
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
 
  860        const QString sourceLayerId = mit.key();
 
  862        if ( !layerJobMapping.contains( sourceLayerId ) )
 
  865        for ( 
const QString &symbolLayerId : mit.value().symbolLayerIds )
 
  868        hasEffects |= mit.value().hasEffects;
 
  871      int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
 
  872      labelHasEffects[ labelMaskId ] = hasEffects;
 
  875      collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
 
  880    collectMasks( &symbolLayerMasks, vl->
id() );
 
  883  if ( maskedSymbolLayers.isEmpty() )
 
  884    return secondPassJobs;
 
  887  for ( 
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
 
  889    QPaintDevice *maskPaintDevice = 
nullptr;
 
  890    QPainter *maskPainter = 
nullptr;
 
  891    if ( forceVector && !labelHasEffects[ maskId ] )
 
  894      auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( 
true );
 
  895      geomPaintDevice->setStrokedPathSegments( 4 );
 
  896      geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
 
  897      maskPaintDevice = geomPaintDevice.release();
 
  898      maskPainter = 
new QPainter( maskPaintDevice );
 
  903      QImage *maskImage = 
nullptr;
 
  904      maskPainter = allocateImageAndPainter( QStringLiteral( 
"label mask" ), maskImage, &labelJob.context );
 
  905      maskImage->fill( 0 );
 
  906      maskPaintDevice = maskImage;
 
  909    labelJob.context.setMaskPainter( maskPainter, maskId );
 
  910    labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
 
  911    labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
 
  913  labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
 
  928  const bool canUseImage = !forceVector && !hasNonDefaultComposition;
 
  929  if ( !labelJob.img && canUseImage )
 
  931    labelJob.img = allocateImage( QStringLiteral( 
"labels" ) );
 
  933  else if ( !labelJob.picture && !canUseImage )
 
  935    labelJob.picture.reset( 
new QPicture() );
 
  939  for ( LayerRenderJob &job : firstPassJobs )
 
  941    job.maskRequiresLayerRasterization = 
false;
 
  943    auto it = maskedSymbolLayers.find( job.layerId );
 
  944    if ( it != maskedSymbolLayers.end() )
 
  946      const QList<MaskSource> &sourceList = it->second;
 
  947      for ( 
const MaskSource &source : sourceList )
 
  949        job.maskRequiresLayerRasterization |= source.hasEffects;
 
  954    const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
 
  955    if ( isRasterRendering && !job.img )
 
  957      job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
 
  959    else if ( !isRasterRendering && !job.picture )
 
  961      PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
 
  962      job.picture = std::move( pictureAndPainter.first );
 
  963      if ( job.context()->painter()->hasClipping() )
 
  966        pictureAndPainter.second->setClipping( 
true );
 
  967        pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
 
  969      job.context()->setPainter( pictureAndPainter.second );
 
  972      job.renderer = job.layer->createMapRenderer( *( job.context() ) );
 
  976    if ( maskLayerHasEffects.contains( job.layerId ) )
 
  978      QPaintDevice *maskPaintDevice = 
nullptr;
 
  979      QPainter *maskPainter = 
nullptr;
 
  980      if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
 
  983        auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
 
  984        geomPaintDevice->setStrokedPathSegments( 4 );
 
  985        geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
 
  986        maskPaintDevice = geomPaintDevice.release();
 
  987        maskPainter = 
new QPainter( maskPaintDevice );
 
  992        QImage *maskImage = 
nullptr;
 
  993        maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
 
  994        maskImage->fill( 0 );
 
  995        maskPaintDevice = maskImage;
 
  998      job.context()->setMaskPainter( maskPainter );
 
  999      job.maskPainter.reset( maskPainter );
 
 1000      job.maskPaintDevice.reset( maskPaintDevice );
 
 1004  for ( LayerRenderJob &job : firstPassJobs )
 
 1008    auto it = maskedSymbolLayers.find( job.layerId );
 
 1009    if ( it == maskedSymbolLayers.end() )
 
 1012    QList<MaskSource> &sourceList = it->second;
 
 1013    const QSet<QString> symbolList = it->first;
 
 1015    secondPassJobs.emplace_back( LayerRenderJob() );
 
 1016    LayerRenderJob &job2 = secondPassJobs.back();
 
 1018    job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
 
 1021    for ( MaskSource &source : sourceList )
 
 1023      if ( source.labelMaskId != -1 )
 
 1024        job2.maskJobs.push_back( qMakePair( 
nullptr, source.labelMaskId ) );
 
 1026        job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
 
 1030    job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
 
 1032    job2.layer = job.layer;
 
 1033    job2.renderAboveLabels = job.renderAboveLabels;
 
 1034    job2.layerId = job.layerId;
 
 1037    job2.firstPassJob = &job;
 
 1039    if ( !forceVector || job2.maskRequiresLayerRasterization )
 
 1041      job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
 
 1045      PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
 
 1046      if ( job.context()->painter()->hasClipping() )
 
 1049        pictureAndPainter.second->setClipping( 
true );
 
 1050        pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
 
 1052      job2.picture = std::move( pictureAndPainter.first );
 
 1053      job2.context()->setPainter( pictureAndPainter.second );
 
 1056    if ( ! job2.img && ! job2.picture )
 
 1058      secondPassJobs.pop_back();
 
 1065    job2.renderer = mapRenderer;
 
 1066    if ( job2.renderer )
 
 1068      job2.context()->setFeedback( job2.renderer->feedback() );
 
 1073    job2.context()->setDisabledSymbolLayersV2( symbolList );
 
 1076  return secondPassJobs;
 
 1081  QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
 
 1085    if ( !res.contains( it ) )
 
 1097  for ( LayerRenderJob &job : secondPassJobs )
 
 1099    if ( job.maskRequiresLayerRasterization )
 
 1105    for ( 
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
 
 1107      QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
 
 1109      const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
 
 1112        QgsGeometry geometry( geometryDevice->geometry().clone() );
 
 1114#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10 
 1121        for ( 
const QString &symbolLayerId : layers )
 
 1123          job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
 
 1128    job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
 
 1136  job.context.setPainter( painter );
 
 1137  job.context.setLabelingEngine( labelingEngine2 );
 
 1138  job.context.setFeedback( mLabelingEngineFeedback );
 
 1139  if ( labelingEngine2 )
 
 1140    job.context.labelingEngine()->prepare( job.context );
 
 1144  job.context.setExtent( r1 );
 
 1146  job.context.setFeatureFilterProvider( mFeatureFilterProvider );
 
 1149  job.context.setCoordinateTransform( ct );
 
 1160    job.complete = 
true;
 
 1163    job.context.setPainter( 
nullptr );
 
 1167    if ( canUseLabelCache && ( 
mCache || !painter ) )
 
 1169      job.img = allocateImage( QStringLiteral( 
"labels" ) );
 
 1179  for ( LayerRenderJob &job : jobs )
 
 1183      delete job.context()->painter();
 
 1184      job.context()->setPainter( 
nullptr );
 
 1186      if ( 
mCache && !job.cached && job.completed && job.layer )
 
 1188        QgsDebugMsgLevel( QStringLiteral( 
"caching image for %1" ).arg( job.layerId ), 2 );
 
 1197    if ( job.previewRenderImage )
 
 1199      delete job.context()->previewRenderPainter();
 
 1200      job.context()->setPreviewRenderPainter( 
nullptr );
 
 1201      delete job.previewRenderImage;
 
 1202      job.previewRenderImage = 
nullptr;
 
 1205    if ( job.elevationMap )
 
 1207      job.context()->setElevationMap( 
nullptr );
 
 1208      if ( 
mCache && !job.cached && job.completed && job.layer )
 
 1210        QgsDebugMsgLevel( QStringLiteral( 
"caching elevation map for %1" ).arg( job.layerId ), 2 );
 
 1213          job.elevationMap->rawElevationImage(),
 
 1216          QList< QgsMapLayer * >() << job.layer );
 
 1219          job.elevationMap->rawElevationImage(),
 
 1222          QList< QgsMapLayer * >() << job.layer );
 
 1225      delete job.elevationMap;
 
 1226      job.elevationMap = 
nullptr;
 
 1231      delete job.context()->painter();
 
 1232      job.context()->setPainter( 
nullptr );
 
 1233      job.picture.reset( 
nullptr );
 
 1238      const QStringList 
errors = job.renderer->errors();
 
 1239      for ( 
const QString &message : 
errors )
 
 1240        mErrors.append( Error( job.renderer->layerId(), message ) );
 
 1242      mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
 
 1244      delete job.renderer;
 
 1245      job.renderer = 
nullptr;
 
 1251    job.maskPainter.reset( 
nullptr );
 
 1252    job.maskPaintDevice.reset( 
nullptr );
 
 1260  for ( LayerRenderJob &job : jobs )
 
 1264      delete job.context()->painter();
 
 1265      job.context()->setPainter( 
nullptr );
 
 1271    if ( job.previewRenderImage )
 
 1273      delete job.context()->previewRenderPainter();
 
 1274      job.context()->setPreviewRenderPainter( 
nullptr );
 
 1275      delete job.previewRenderImage;
 
 1276      job.previewRenderImage = 
nullptr;
 
 1281      delete job.context()->painter();
 
 1282      job.context()->setPainter( 
nullptr );
 
 1287      delete job.renderer;
 
 1288      job.renderer = 
nullptr;
 
 1302    if ( 
mCache && !job.cached && !job.context.renderingStopped() )
 
 1313  job.picture.reset( 
nullptr );
 
 1314  job.maskPainters.clear();
 
 1315  job.maskPaintDevices.clear();
 
 1319#define DEBUG_RENDERING 0 
 1322                                        const std::vector<LayerRenderJob> &jobs,
 
 1323                                        const LabelRenderJob &labelJob,
 
 1329  image.setDotsPerMeterX( 
static_cast<int>( settings.
outputDpi() * 39.37 ) );
 
 1330  image.setDotsPerMeterY( 
static_cast<int>( settings.
outputDpi() * 39.37 ) );
 
 1334  std::unique_ptr<QgsElevationMap> mainElevationMap;
 
 1335  if ( mapShadingRenderer.
isActive() )
 
 1338  QPainter painter( &image );
 
 1343  for ( 
const LayerRenderJob &job : jobs )
 
 1345    if ( job.renderAboveLabels )
 
 1352    painter.setCompositionMode( job.blendMode );
 
 1353    painter.setOpacity( job.opacity );
 
 1355    if ( mainElevationMap )
 
 1358      if ( layerElevationMap.
isValid() )
 
 1364    img.save( QString( 
"/tmp/final_%1.png" ).arg( i ) );
 
 1368    painter.drawImage( 0, 0, img );
 
 1371  if ( mapShadingRenderer.
isActive() &&  mainElevationMap )
 
 1379  if ( labelJob.img && labelJob.complete )
 
 1381    painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1382    painter.setOpacity( 1.0 );
 
 1383    painter.drawImage( 0, 0, *labelJob.img );
 
 1385  else if ( labelJob.picture && labelJob.complete )
 
 1387    painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1388    painter.setOpacity( 1.0 );
 
 1397    painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1398    painter.setOpacity( 1.0 );
 
 1399    painter.drawImage( 0, 0, labelCacheImage );
 
 1403  for ( 
const LayerRenderJob &job : jobs )
 
 1405    if ( !job.renderAboveLabels )
 
 1412    painter.setCompositionMode( job.blendMode );
 
 1413    painter.setOpacity( job.opacity );
 
 1415    painter.drawImage( 0, 0, img );
 
 1420  image.save( 
"/tmp/final.png" );
 
 1427  const LayerRenderJob &job,
 
 1431  if ( job.imageCanBeComposed() )
 
 1433    if ( job.previewRenderImage && !job.completed )
 
 1434      return *job.previewRenderImage;
 
 1436    Q_ASSERT( job.img );
 
 1441    if ( cache && cache->
hasAnyCacheImage( job.layerId + QStringLiteral( 
"_preview" ) ) )
 
 1452  if ( job.imageCanBeComposed() && job.elevationMap )
 
 1454    return *job.elevationMap;
 
 1468  for ( LayerRenderJob &job : secondPassJobs )
 
 1470    const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
 
 1473    if ( isRasterRendering && job.maskJobs.size() > 1 )
 
 1475      QPainter *maskPainter = 
nullptr;
 
 1476      for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
 
 1478        QImage *maskImage = 
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
 
 1481          maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
 
 1485          maskPainter->drawImage( 0, 0, *maskImage );
 
 1490    if ( ! job.maskJobs.isEmpty() )
 
 1493      QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
 
 1494      if ( isRasterRendering )
 
 1496        QImage *maskImage = 
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
 
 1499        QPainter *painter = job.context()->painter();
 
 1501        painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
 
 1506        QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
 
 1507        QVector<QRgb> mswTable;
 
 1508        mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
 
 1509        mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
 
 1510        maskBinAlpha.setColorTable( mswTable );
 
 1511        painter->drawImage( 0, 0, maskBinAlpha );
 
 1515          QPainter tempPainter;
 
 1518          QPainter *painter1 = job.firstPassJob->context()->painter();
 
 1521            tempPainter.begin( job.firstPassJob->img );
 
 1522            painter1 = &tempPainter;
 
 1526          painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
 
 1527          painter1->drawImage( 0, 0, *maskImage );
 
 1530          painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
 
 1531          painter1->drawImage( 0, 0, *job.img );
 
 1536        job.firstPassJob->picture = std::move( job.picture );
 
 1537        job.picture = 
nullptr;
 
 1548  QMultiMap<int, QString> elapsed;
 
 1549  for ( 
const LayerRenderJob &job : jobs )
 
 1551  for ( 
const LayerRenderJob &job : secondPassJobs )
 
 1552    elapsed.insert( job.
renderingTime, job.layerId + QString( 
" (second pass)" ) );
 
 1554  elapsed.insert( labelJob.renderingTime, tr( 
"Labeling" ) );
 
 1556  QList<int> tt( elapsed.uniqueKeys() );
 
 1557  std::sort( tt.begin(), tt.end(), std::greater<int>() );
 
 1558  for ( 
int t : std::as_const( tt ) )
 
 1560    QgsMessageLog::logMessage( tr( 
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String( 
", " ) ) ), tr( 
"Rendering" ) );
 
 1569  std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
 
 1572    labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"(labeling)" ), QStringLiteral( 
"rendering" ) );
 
 1579  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
 
 1583  if ( labelingEngine2 )
 
 1585    labelingEngine2->
run( renderContext );
 
 1588  QgsDebugMsgLevel( QStringLiteral( 
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
 
 1593  Q_UNUSED( settings )
 
 1595  drawLabeling( renderContext, labelingEngine2, painter );
 
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
 
@ 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.
 
@ AffectsLabeling
The layer rendering will interact with the map labeling.
 
@ 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.
 
@ 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 hasNonDefaultCompositionMode() const =0
Returns true the labeling requires a non-default composition mode.
 
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
 
virtual bool hasNonDefaultCompositionMode() const =0
Returns true the labeling requires a non-default composition mode.
 
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
 
virtual bool hasNonDefaultCompositionMode() const =0
Returns true the labeling requires a non-default composition mode.
 
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 a digital elevation model in a raster image which may get updated as a part of the map layer r...
 
bool isValid() const
Returns whether the elevation map is valid.
 
Renders elevation shading on an image with different methods (eye dome lighting, hillshading,...
 
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.
 
Provides map labeling functionality.
 
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
 
QList< QgsMapLayer * > participatingLayers() const
Returns a list of layers with providers in the engine.
 
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.
 
Responsible for keeping a 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)
 
QList< QPointer< QgsMapLayer > > participatingLabelLayers(QgsLabelingEngine *engine)
Returns a list of the layers participating in the map labeling.
 
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.
 
QList< QPointer< QgsMapLayer > > mAdditionalLabelLayers
Additional layers participating in labeling problem.
 
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).
 
bool labelingHasNonDefaultCompositionModes() const
Returns true if any component of the map labeling requires non-default composition modes.
 
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)
 
Contains configuration for rendering maps.
 
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.
 
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
 
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
 
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
 
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
 
static bool staticWillUseLayer(const QgsMapLayer *layer)
Called to find out whether a specified layer is used for labeling.
 
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
 
Represents a raster layer.
 
const QgsAbstractRasterLayerLabeling * labeling() const
Access to const labeling configuration.
 
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
 
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.
 
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
 
void setXMinimum(double x)
Set the minimum x value.
 
void setXMaximum(double x)
Set the maximum x value.
 
void grow(double delta)
Grows the rectangle in place by the specified amount.
 
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 dataset.
 
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