51#include <QElapsedTimer>
57#include "moc_qgsmaprendererjob.cpp"
59using namespace Qt::StringLiterals;
70LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
75 mContext = std::move( other.mContext );
76 destinationImage = std::move( other.destinationImage );
77 destinationPainter = std::move( other.destinationPainter );
78 renderer = std::move( other.renderer );
79 previewRenderImage = std::move( other.previewRenderImage );
80 previewRenderPainter = std::move( other.previewRenderPainter );
82 imageInitialized = other.imageInitialized;
83 previewRenderImageInitialized = other.previewRenderImageInitialized;
85 blendMode = other.blendMode;
86 opacity = other.opacity;
87 cached = other.cached;
89 renderAboveLabels = other.renderAboveLabels;
90 completed = other.completed;
91 renderingTime = other.renderingTime;
92 estimatedRenderingTime = other.estimatedRenderingTime;
93 errors = other.errors;
94 layerId = other.layerId;
96 maskPaintDevice = std::move( other.maskPaintDevice );
98 firstPassJob = other.firstPassJob;
99 other.firstPassJob =
nullptr;
101 picture = std::move( other.picture );
103 maskJobs = other.maskJobs;
105 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
107 elevationMap = std::move( other.elevationMap );
108 maskPainter = std::move( other.maskPainter );
113LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
114 : imageInitialized( other.imageInitialized )
115 , previewRenderImageInitialized( other.previewRenderImageInitialized )
116 , blendMode( other.blendMode )
117 , opacity( other.opacity )
118 , cached( other.cached )
119 , renderAboveLabels( other.renderAboveLabels )
120 , layer( other.layer )
121 , completed( other.completed )
122 , renderingTime( other.renderingTime )
123 , estimatedRenderingTime( other.estimatedRenderingTime )
124 , errors( other.errors )
125 , layerId( other.layerId )
126 , maskPainter( std::move( other.maskPainter ) )
127 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
128 , maskJobs( other.maskJobs )
130 mContext = std::move( other.mContext );
131 destinationImage = std::move( other.destinationImage );
132 destinationPainter = std::move( other.destinationPainter );
133 previewRenderImage = std::move( other.previewRenderImage );
134 previewRenderPainter = std::move( other.previewRenderPainter );
135 renderer = std::move( other.renderer );
136 elevationMap = std::move( other.elevationMap );
137 maskPaintDevice = std::move( other.maskPaintDevice );
139 firstPassJob = other.firstPassJob;
140 other.firstPassJob =
nullptr;
142 picture = std::move( other.picture );
145bool LayerRenderJob::imageCanBeComposed()
const
147 if ( imageInitialized )
151 return renderer->isReadyToCompose();
165 : mSettings( settings )
178 mErrors.append( QgsMapRendererJob::Error( QString(), tr(
"Invalid map settings" ) ) );
210 return mLabelingEngineFeedback;
215 QHash<QgsMapLayer *, int> result;
218 if (
auto &&lKey = it.key() )
219 result.insert( lKey, it.value() );
239 QSet< QgsMapLayer * > labeledLayers;
240 const QList<QgsMapLayer *> layers =
mSettings.layers();
241 for ( QgsMapLayer *ml : layers )
246 switch ( ml->type() )
250 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
260 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
270 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
302 bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
314 const QList<QgsMapLayer *> layers =
mSettings.layers();
315 for ( QgsMapLayer *ml : layers )
320 switch ( ml->type() )
324 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
334 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
344 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
375 QgsCoordinateTransform approxTransform = ct;
388 static const double SPLIT_COORD = 180.0;
401 u
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)"_s.arg( extent.
toString() )
402 .arg( extent.
width() )
405 .arg( extent2.
width() )
407 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
408 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) ),
422 extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
440 if ( ll.
x() > ur.
x() )
467 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
474 catch ( QgsCsException &e )
477 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
478 r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
485std::unique_ptr<QImage> QgsMapRendererJob::allocateImage(
const QString &layerId )
487 auto image = std::make_unique< QImage >(
mSettings.deviceOutputSize(),
mSettings.outputImageFormat() );
488 image->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
489 image->setDotsPerMeterX( 1000 *
mSettings.outputDpi() / 25.4 );
490 image->setDotsPerMeterY( 1000 *
mSettings.outputDpi() / 25.4 );
491 if ( image->isNull() )
494 mErrors.append(
Error( layerId, tr(
"Insufficient memory for image %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
500std::unique_ptr<QgsElevationMap> QgsMapRendererJob::allocateElevationMap(
const QString &layerId )
502 auto elevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
503 if ( !elevationMap->isValid() )
506 mErrors.append(
Error( layerId, tr(
"Insufficient memory for elevation map %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
512std::tuple< std::unique_ptr< QImage >, std::unique_ptr< QPainter > > QgsMapRendererJob::allocateImageAndPainter(
const QString &layerId,
const QgsRenderContext &context )
514 std::unique_ptr< QPainter > painter;
515 std::unique_ptr< QImage > image = allocateImage( layerId );
518 painter = std::make_unique< QPainter >( image.get() );
521 return { std::move( image ), std::move( painter ) };
524std::tuple<std::unique_ptr<QPicture>, std::unique_ptr< QPainter > > QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext &context )
526 auto picture = std::make_unique<QPicture>();
527 auto painter = std::make_unique< QPainter >( picture.get() );
529 return { std::move( picture ), std::move( painter ) };
534 std::vector< LayerRenderJob > layerJobs;
537 QListIterator<QgsMapLayer *> li(
mSettings.layers() );
543 Q_UNUSED( cacheValid )
549 while ( li.hasPrevious() )
551 QgsMapLayer *ml = li.previous();
554 u
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6"_s.arg( ml->
name() )
566 mErrors.append(
Error( ml->
id(), QString(
"Layer %1 is invalid, skipped" ).arg( ml->
name() ) ) );
572 QgsDebugMsgLevel( u
"Layer not rendered because it is not within the defined visibility scale range"_s, 3 );
578 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's time range"_s, 3 );
584 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the layer's time range"_s, 3 );
588 if ( !
mSettings.zRange().isInfinite() && ml->elevationProperties() && !ml->elevationProperties()->isVisibleInZRange(
mSettings.zRange(), ml ) )
590 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's z range"_s, 3 );
594 QgsRectangle r1 =
mSettings.visibleExtent(), r2;
596 QgsCoordinateTransform ct;
599 bool haveExtentInLayerCrs =
true;
602 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
607 mErrors.append(
Error( ml->id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
611 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
618 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
620 mCache->clearCacheImage( ml->id() );
624 layerJobs.emplace_back( LayerRenderJob() );
625 LayerRenderJob &job = layerJobs.back();
627 job.layerId = ml->id();
628 job.renderAboveLabels = ml->customProperty( u
"rendering/renderAboveLabels"_s ).toBool();
632 if ( !ml->customProperty( u
"_noset_layer_expression_context"_s ).toBool() )
634 job.context()->setPainter( painter );
635 job.context()->setLabelingEngine( labelingEngine2 );
636 job.context()->setLabelSink(
labelSink() );
637 job.context()->setCoordinateTransform( ct );
638 job.context()->setExtent( r1 );
644 if ( mFeatureFilterProvider )
646 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
649 if ( mPerLayerTemporalRange.contains( ml ) )
651 job.context()->setTemporalRange( mPerLayerTemporalRange.value( ml ) );
654 QgsMapLayerStyleOverride styleOverride( ml );
655 if (
mSettings.layerStyleOverrides().contains( ml->id() ) )
656 styleOverride.setOverrideStyle(
mSettings.layerStyleOverrides().value( ml->id() ) );
658 job.blendMode = ml->blendMode();
663 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( ml );
670 job.opacity = ml->opacity();
675 job.opacity = ml->opacity();
678 const QgsElevationShadingRenderer shadingRenderer =
mSettings.elevationShadingRenderer();
682 if ( canUseCache &&
mCache->hasCacheImage( ml->id() ) )
685 job.imageInitialized =
true;
686 job.destinationImage = std::make_unique< QImage >(
mCache->cacheImage( ml->id() ) );
689 job.destinationImage->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
690 job.renderer.reset();
691 job.context()->setPainter(
nullptr );
696 QElapsedTimer layerTime;
698 job.renderer.reset( ml->createMapRenderer( *( job.context() ) ) );
701 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
702 job.context()->setFeedback( job.renderer->feedback() );
713 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
716 auto [image, painter] = allocateImageAndPainter( ml->id(), *job.context() );
717 job.destinationImage = std::move( image );
718 job.destinationPainter = std::move( painter );
719 job.context()->setPainter( job.destinationPainter.get() );
720 if ( !job.destinationImage )
722 job.renderer.reset();
723 layerJobs.pop_back();
728 if ( shadingRenderer.
isActive() && ml->elevationProperties() && ml->elevationProperties()->hasElevation() )
730 job.elevationMap = allocateElevationMap( ml->id() );
731 job.context()->setElevationMap( job.elevationMap.get() );
738 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + u
"_preview"_s,
mSettings.mapToPixel() );
739 if ( !cachedImage.isNull() )
741 job.previewRenderImage = std::make_unique< QImage >( cachedImage );
742 job.previewRenderImageInitialized =
true;
743 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage.get() ) );
744 job.context()->setPainterFlagsUsingContext( painter );
747 if ( !job.previewRenderImage )
749 auto [image, painter] = allocateImageAndPainter( ml->id(), *job.context() );
750 job.previewRenderImage = std::move( image );
751 job.previewRenderPainter = std::move( painter );
752 job.previewRenderImageInitialized =
false;
753 job.context()->setPreviewRenderPainter( job.previewRenderPainter.get() );
756 if ( !job.previewRenderImage )
758 job.context()->setPreviewRenderPainter(
nullptr );
759 job.previewRenderPainter.reset();
763 job.renderingTime = layerTime.elapsed();
771 std::vector< LayerRenderJob > secondPassJobs;
774 QHash<QString, LayerRenderJob *> layerJobMapping;
777 QMap<QString, bool> maskLayerHasEffects;
778 QMap<int, bool> labelHasEffects;
786 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ )
787 : layerId( layerId_ )
788 , labelRuleId( labelRuleId_ )
789 , labelMaskId( labelMaskId_ )
790 , hasEffects( hasEffects_ )
794 struct MaskedSymbolLayers
796 QSet<QString> maskedSymbolLayerIds;
797 QList<MaskSource> maskSourceList;
803 QHash<QString, MaskedSymbolLayers> maskedSymbolLayers;
809 for ( LayerRenderJob &job : firstPassJobs )
811 layerJobMapping[job.layerId] = &job;
816 QVector< QgsVectorLayer * > allRenderedVectorLayers;
817 for ( LayerRenderJob &job : firstPassJobs )
819 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
823 allRenderedVectorLayers << vl;
828 for ( LayerRenderJob &job : firstPassJobs )
830 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
835 auto collectMasks = [&maskedSymbolLayers,
836 &maskLayerHasEffects](
const QgsMaskedLayers &objectsToBeMaskedByLayer,
const QString &idOfLayerCreatingMask,
const QString &ruleId = QString(),
int labelMaskId = -1 ) {
837 bool hasEffects =
false;
838 for (
auto it = objectsToBeMaskedByLayer.begin(); it != objectsToBeMaskedByLayer.end(); ++it )
840 const QString maskedLayerId = it.key();
841 auto lit = maskedSymbolLayers.find( maskedLayerId );
842 if ( lit == maskedSymbolLayers.end() )
844 MaskedSymbolLayers maskedObjects;
845 maskedObjects.maskedSymbolLayerIds = it.value().symbolLayerIdsToMask;
846 maskedObjects.maskSourceList = QList<MaskSource>() << MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, it.value().hasEffects );
847 maskedSymbolLayers[maskedLayerId] = maskedObjects;
851 if ( lit->maskedSymbolLayerIds != it.value().symbolLayerIdsToMask )
853 QgsLogger::warning( u
"Layer %1 : Different sets of symbol layers are masked by different sources ! Only one (arbitrary) set will be retained !"_s.arg( it.key() ) );
856 lit->maskSourceList.push_back( MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, hasEffects ) );
858 hasEffects |= it.value().hasEffects;
860 if ( !objectsToBeMaskedByLayer.isEmpty() && labelMaskId == -1 )
861 maskLayerHasEffects[idOfLayerCreatingMask] = hasEffects;
866 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
868 QString labelRule = it.key();
874 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
876 const QString sourceLayerId = mit.key();
878 if ( !layerJobMapping.contains( sourceLayerId ) )
881 usableMasks.insert( sourceLayerId, mit.value() );
884 if ( usableMasks.empty() )
888 QSet<QgsSymbolLayerReference> slRefs;
889 bool hasEffects =
false;
890 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
892 const QString sourceLayerId = mit.key();
894 if ( !layerJobMapping.contains( sourceLayerId ) )
897 for (
const QString &symbolLayerId : std::as_const( mit.value().symbolLayerIdsToMask ) )
898 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
900 hasEffects |= mit.value().hasEffects;
903 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
904 labelHasEffects[labelMaskId] = hasEffects;
907 collectMasks( usableMasks, vl->
id(), labelRule, labelMaskId );
912 collectMasks( objectsToBeMaskedByLayer, vl->
id() );
915 if ( maskedSymbolLayers.isEmpty() )
916 return secondPassJobs;
919 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
921 std::unique_ptr< QPaintDevice > maskPaintDevice;
922 std::unique_ptr< QPainter > maskPainter;
923 if ( forceVector && !labelHasEffects[maskId] )
926 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
927 geomPaintDevice->setStrokedPathSegments( 4 );
928 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
929 maskPaintDevice = std::move( geomPaintDevice );
930 maskPainter = std::make_unique< QPainter >( maskPaintDevice.get() );
935 auto [maskImage, painter] = allocateImageAndPainter( u
"label mask"_s, labelJob.context );
939 maskImage->fill( 0 );
940 maskPaintDevice = std::move( maskImage );
941 maskPainter = std::move( painter );
944 labelJob.context.setMaskPainter( maskPainter.get(), maskId );
945 labelJob.maskPainters.emplace_back( std::move( maskPainter ) );
946 labelJob.maskPaintDevices.emplace_back( std::move( maskPaintDevice ) );
948 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
963 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
964 if ( !labelJob.img && canUseImage )
966 labelJob.img = allocateImage( u
"labels"_s );
968 else if ( !labelJob.picture && !canUseImage )
970 labelJob.picture = std::make_unique<QPicture>();
974 for ( LayerRenderJob &job : firstPassJobs )
976 job.maskRequiresLayerRasterization =
false;
978 auto it = maskedSymbolLayers.find( job.layerId );
979 if ( it != maskedSymbolLayers.end() )
981 const QList<MaskSource> &sourceList = it->maskSourceList;
982 for (
const MaskSource &source : sourceList )
984 job.maskRequiresLayerRasterization |= source.hasEffects;
989 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
990 if ( isRasterRendering && !job.destinationImage )
992 auto [img, painter] = allocateImageAndPainter( job.layerId, *job.context() );
993 Q_ASSERT( !job.destinationPainter );
994 job.destinationPainter = std::move( painter );
995 job.context()->setPainter( job.destinationPainter.get() );
996 job.destinationImage = std::move( img );
998 else if ( !isRasterRendering && !job.picture )
1000 auto [picture, painter] = allocatePictureAndPainter( *job.context() );
1001 job.picture = std::move( picture );
1002 if ( job.context()->painter()->hasClipping() )
1005 painter->setClipping(
true );
1006 painter->setClipPath( job.context()->painter()->clipPath() );
1008 Q_ASSERT( !job.destinationPainter );
1009 job.destinationPainter = std::move( painter );
1010 job.context()->setPainter( job.destinationPainter.get() );
1013 job.context()->setFeedback(
nullptr );
1014 job.renderer.reset( job.layer->createMapRenderer( *( job.context() ) ) );
1017 job.context()->setFeedback( job.renderer->feedback() );
1022 if ( maskLayerHasEffects.contains( job.layerId ) )
1024 std::unique_ptr< QPaintDevice > maskPaintDevice;
1025 std::unique_ptr< QPainter > maskPainter;
1026 if ( forceVector && !maskLayerHasEffects[job.layerId] )
1029 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >();
1030 geomPaintDevice->setStrokedPathSegments( 4 );
1031 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
1032 maskPaintDevice = std::move( geomPaintDevice );
1033 maskPainter = std::make_unique< QPainter >( maskPaintDevice.get() );
1038 auto [maskImage, painter] = allocateImageAndPainter( job.layerId, *job.context() );
1039 maskImage->fill( 0 );
1040 maskPaintDevice = std::move( maskImage );
1041 maskPainter = std::move( painter );
1044 job.context()->setMaskPainter( maskPainter.get() );
1045 job.maskPainter = std::move( maskPainter );
1046 job.maskPaintDevice = std::move( maskPaintDevice );
1050 for ( LayerRenderJob &job : firstPassJobs )
1052 QgsMapLayer *ml = job.layer;
1054 auto it = maskedSymbolLayers.find( job.layerId );
1055 if ( it == maskedSymbolLayers.end() )
1058 const QList<MaskSource> maskedSourceList = it->maskSourceList;
1059 const QSet<QString> maskedSymbolLayerIds = it->maskedSymbolLayerIds;
1061 secondPassJobs.emplace_back( LayerRenderJob() );
1062 LayerRenderJob &job2 = secondPassJobs.back();
1064 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1067 for (
const MaskSource &source : maskedSourceList )
1069 LayerRenderJob::MaskJob maskJob;
1071 if ( source.labelMaskId != -1 )
1073 maskJob.maskPaintDeviceId = source.labelMaskId;
1077 maskJob.layerRenderJob = layerJobMapping[source.layerId];
1079 job2.maskJobs.emplace_back( maskJob );
1083 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1085 job2.layer = job.layer;
1086 job2.renderAboveLabels = job.renderAboveLabels;
1087 job2.layerId = job.layerId;
1090 job2.firstPassJob = &job;
1092 if ( !forceVector || job2.maskRequiresLayerRasterization )
1094 Q_ASSERT( !job2.destinationImage );
1095 Q_ASSERT( !job2.destinationPainter );
1096 auto [img, painter] = allocateImageAndPainter( job.layerId, *job2.context() );
1097 job2.context()->setPainter( painter.get() );
1098 job2.destinationImage = std::move( img );
1099 job2.destinationPainter = std::move( painter );
1103 auto [picture, painter] = allocatePictureAndPainter( *job2.context() );
1104 if ( job.context()->painter()->hasClipping() )
1107 painter->setClipping(
true );
1108 painter->setClipPath( job.context()->painter()->clipPath() );
1110 job2.picture = std::move( picture );
1111 Q_ASSERT( !job2.destinationPainter );
1112 job2.destinationPainter = std::move( painter );
1113 job2.context()->setPainter( job2.destinationPainter.get() );
1116 if ( !job2.destinationImage && !job2.picture )
1118 secondPassJobs.pop_back();
1124 job2.context()->setFeedback(
nullptr );
1126 if ( job2.renderer )
1128 job2.context()->setFeedback( job2.renderer->feedback() );
1133 job2.context()->setDisabledSymbolLayersV2( maskedSymbolLayerIds );
1136 return secondPassJobs;
1141 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1145 if ( !res.contains( it ) )
1157 for ( LayerRenderJob &job : secondPassJobs )
1159 if ( job.maskRequiresLayerRasterization )
1165 for (
const LayerRenderJob::MaskJob &p : std::as_const( job.maskJobs ) )
1167 QPainter *maskPainter = p.layerRenderJob ? p.layerRenderJob->maskPainter.get() : labelJob.maskPainters[p.maskPaintDeviceId].get();
1169 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1170 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1172 QgsGeometry geometry( geometryDevice->geometry().clone() );
1174#if GEOS_VERSION_NUM < GEOS_COMPUTE_VERSION( 3, 10, 0 )
1181 for (
const QString &symbolLayerId : layers )
1183 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1188 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1196 job.context.setPainter( painter );
1197 job.context.setLabelingEngine( labelingEngine2 );
1198 job.context.setFeedback( mLabelingEngineFeedback );
1199 if ( labelingEngine2 )
1200 job.context.labelingEngine()->prepare( job.context );
1202 QgsRectangle r1 =
mSettings.visibleExtent();
1204 job.context.setExtent( r1 );
1206 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1207 QgsCoordinateTransform ct;
1209 job.context.setCoordinateTransform( ct );
1220 job.complete =
true;
1222 job.context.setPainter(
nullptr );
1226 if ( canUseLabelCache && (
mCache || !painter ) )
1228 job.img = allocateImage( u
"labels"_s );
1238 for ( LayerRenderJob &job : jobs )
1240 if ( job.destinationImage )
1242 job.context()->setPainter(
nullptr );
1243 job.destinationPainter.reset();
1245 if (
mCache && !job.cached && job.completed && job.layer )
1248 mCache->setCacheImageWithParameters( job.layerId, *job.destinationImage,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1249 mCache->setCacheImageWithParameters( job.layerId + u
"_preview"_s, *job.destinationImage,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1252 job.destinationImage.reset();
1255 if ( job.previewRenderImage )
1257 job.context()->setPreviewRenderPainter(
nullptr );
1258 job.previewRenderPainter.reset();
1259 job.previewRenderImage.reset();
1262 if ( job.elevationMap )
1264 job.context()->setElevationMap(
nullptr );
1265 if (
mCache && !job.cached && job.completed && job.layer )
1267 QgsDebugMsgLevel( u
"caching elevation map for %1"_s.arg( job.layerId ), 2 );
1268 mCache->setCacheImageWithParameters(
1271 mCache->setCacheImageWithParameters(
1276 job.elevationMap.reset();
1281 job.context()->setPainter(
nullptr );
1282 job.destinationPainter.reset();
1283 job.picture.reset(
nullptr );
1288 const QStringList
errors = job.renderer->errors();
1289 for (
const QString &message :
errors )
1290 mErrors.append(
Error( job.renderer->layerId(), message ) );
1292 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1294 job.context()->setFeedback(
nullptr );
1295 job.renderer.reset();
1301 job.maskPainter.reset(
nullptr );
1302 job.maskPaintDevice.reset(
nullptr );
1310 for ( LayerRenderJob &job : jobs )
1312 if ( job.destinationImage )
1314 job.context()->setPainter(
nullptr );
1315 job.destinationPainter.reset();
1316 job.destinationImage.reset();
1319 if ( job.previewRenderImage )
1321 job.context()->setPreviewRenderPainter(
nullptr );
1322 job.previewRenderPainter.reset();
1323 job.previewRenderImage.reset();
1328 job.context()->setPainter(
nullptr );
1329 job.destinationPainter.reset();
1334 job.context()->setFeedback(
nullptr );
1335 job.renderer.reset();
1349 if (
mCache && !job.cached && !job.context.renderingStopped() )
1359 job.picture.reset(
nullptr );
1360 job.maskPainters.clear();
1361 job.maskPaintDevices.clear();
1365#define DEBUG_RENDERING 0
1371 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1372 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1376 std::unique_ptr<QgsElevationMap> mainElevationMap;
1377 if ( mapShadingRenderer.
isActive() )
1380 QPainter painter( &image );
1385 for (
const LayerRenderJob &job : jobs )
1387 if ( job.renderAboveLabels )
1394 painter.setCompositionMode( job.blendMode );
1395 painter.setOpacity( job.opacity );
1397 if ( mainElevationMap )
1400 if ( layerElevationMap.
isValid() )
1406 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1410 painter.drawImage( 0, 0, img );
1413 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1421 if ( labelJob.img && labelJob.complete )
1423 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1424 painter.setOpacity( 1.0 );
1425 painter.drawImage( 0, 0, *labelJob.img );
1427 else if ( labelJob.picture && labelJob.complete )
1429 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1430 painter.setOpacity( 1.0 );
1439 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1440 painter.setOpacity( 1.0 );
1441 painter.drawImage( 0, 0, labelCacheImage );
1445 for (
const LayerRenderJob &job : jobs )
1447 if ( !job.renderAboveLabels )
1454 painter.setCompositionMode( job.blendMode );
1455 painter.setOpacity( job.opacity );
1457 painter.drawImage( 0, 0, img );
1462 image.save(
"/tmp/final.png" );
1469 if ( job.imageCanBeComposed() )
1471 if ( job.previewRenderImage && !job.completed )
1472 return *job.previewRenderImage;
1474 Q_ASSERT( job.destinationImage );
1475 return *job.destinationImage;
1490 if ( job.imageCanBeComposed() && job.elevationMap )
1492 return *job.elevationMap;
1499 return QgsElevationMap();
1506 for ( LayerRenderJob &job : secondPassJobs )
1508 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1511 if ( isRasterRendering && job.maskJobs.size() > 1 )
1513 QPainter *maskPainter =
nullptr;
1514 for (
const LayerRenderJob::MaskJob &p : job.maskJobs )
1516 QImage *maskImage =
static_cast<QImage *
>( p.layerRenderJob ? p.layerRenderJob->maskPaintDevice.get() : labelJob.maskPaintDevices[p.maskPaintDeviceId].get() );
1519 maskPainter = p.layerRenderJob ? p.layerRenderJob->maskPainter.get() : labelJob.maskPainters[p.maskPaintDeviceId].get();
1523 maskPainter->drawImage( 0, 0, *maskImage );
1528 if ( !job.maskJobs.empty() )
1531 LayerRenderJob::MaskJob p = *job.maskJobs.begin();
1532 if ( isRasterRendering )
1534 QImage *maskImage =
static_cast<QImage *
>( p.layerRenderJob ? p.layerRenderJob->maskPaintDevice.get() : labelJob.maskPaintDevices[p.maskPaintDeviceId].get() );
1537 QPainter *painter = job.context()->painter();
1539 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1544 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1545 QVector<QRgb> mswTable;
1546 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1547 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1548 maskBinAlpha.setColorTable( mswTable );
1549 painter->drawImage( 0, 0, maskBinAlpha );
1553 QPainter tempPainter;
1556 QPainter *painter1 = job.firstPassJob->context()->painter();
1559 tempPainter.begin( job.firstPassJob->destinationImage.get() );
1560 painter1 = &tempPainter;
1564 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1565 painter1->drawImage( 0, 0, *maskImage );
1568 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1569 painter1->drawImage( 0, 0, *job.destinationImage );
1574 job.firstPassJob->picture = std::move( job.picture );
1575 job.picture =
nullptr;
1586 QMultiMap<int, QString> elapsed;
1587 for (
const LayerRenderJob &job : jobs )
1588 elapsed.insert( job.renderingTime, job.layerId );
1589 for (
const LayerRenderJob &job : secondPassJobs )
1590 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1592 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1594 QList<int> tt( elapsed.uniqueKeys() );
1595 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1596 for (
int t : std::as_const( tt ) )
1598 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join(
", "_L1 ) ), tr(
"Rendering" ) );
1607 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1610 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), u
"rendering"_s );
1617 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1621 if ( labelingEngine2 )
1623 labelingEngine2->
run( renderContext );
1626 QgsDebugMsgLevel( u
"Draw labeling took (seconds): %1"_s.arg( t.elapsed() / 1000. ), 2 );
1631 Q_UNUSED( settings )
1633 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 ...
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.
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.
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 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.
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.
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 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 (...
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.
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.
QColor backgroundColor() const
Returns the background color of the map.
const QgsMapToPixel & mapToPixel() const
float devicePixelRatio() const
Returns the device pixel ratio.
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
const QgsElevationShadingRenderer & elevationShadingRenderer() const
Returns the shading renderer used to render shading on the entire map.
QHash< QString, QgsSelectiveMaskingSourceSet > selectiveMaskingSourceSets() const
Returns a hash of all selective masking source sets defined for the 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.
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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.
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 rounded to the spec...
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.
A boolean settings entry.
static QgsSettingsTreeNode * sTreeMap
static QgsMaskedLayers collectObjectsMaskedBySymbolLayersFromLayer(const QgsVectorLayer *layer, const QHash< QString, QgsSelectiveMaskingSourceSet > &selectiveMaskingSourceSets, const QVector< QgsVectorLayer * > &allRenderedVectorLayers)
Returns all objects that will be masked by the symbol layers for a given vector layer.
static QHash< QString, QgsMaskedLayers > collectObjectsMaskedByLabelsFromLayer(const QgsVectorLayer *layer, const QHash< QString, QgsSelectiveMaskingSourceSet > &selectiveMaskingSourceSets, const QVector< QgsVectorLayer * > &allRenderedVectorLayers)
Returns all objects that will be masked by the labels for a given vector layer.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
bool isEditable() const final
Returns true if the provider is in editing mode.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
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 of the layer that WILL be masked