51#include <QElapsedTimer>
57#include "moc_qgsmaprendererjob.cpp"
59using namespace Qt::StringLiterals;
70LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
75 mContext = std::move( other.mContext );
80 renderer = other.renderer;
81 other.renderer =
nullptr;
83 previewRenderImage = other.previewRenderImage;
84 other.previewRenderImage =
nullptr;
86 imageInitialized = other.imageInitialized;
87 previewRenderImageInitialized = other.previewRenderImageInitialized;
89 blendMode = other.blendMode;
90 opacity = other.opacity;
91 cached = other.cached;
93 renderAboveLabels = other.renderAboveLabels;
94 completed = other.completed;
95 renderingTime = other.renderingTime;
96 estimatedRenderingTime = other.estimatedRenderingTime;
97 errors = other.errors;
98 layerId = other.layerId;
100 maskPaintDevice = std::move( other.maskPaintDevice );
102 firstPassJob = other.firstPassJob;
103 other.firstPassJob =
nullptr;
105 picture = std::move( other.picture );
107 maskJobs = other.maskJobs;
109 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
111 elevationMap = other.elevationMap;
112 maskPainter = std::move( other.maskPainter );
117LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
118 : imageInitialized( other.imageInitialized )
119 , previewRenderImageInitialized( other.previewRenderImageInitialized )
120 , blendMode( other.blendMode )
121 , opacity( other.opacity )
122 , cached( other.cached )
123 , renderAboveLabels( other.renderAboveLabels )
124 , layer( other.layer )
125 , completed( other.completed )
126 , renderingTime( other.renderingTime )
127 , estimatedRenderingTime( other.estimatedRenderingTime )
128 , errors( other.errors )
129 , layerId( other.layerId )
130 , maskPainter( nullptr )
131 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
132 , maskJobs( other.maskJobs )
134 mContext = std::move( other.mContext );
139 previewRenderImage = other.previewRenderImage;
140 other.previewRenderImage =
nullptr;
142 renderer = other.renderer;
143 other.renderer =
nullptr;
145 elevationMap = other.elevationMap;
146 other.elevationMap =
nullptr;
148 maskPaintDevice = std::move( other.maskPaintDevice );
150 firstPassJob = other.firstPassJob;
151 other.firstPassJob =
nullptr;
153 picture = std::move( other.picture );
156bool LayerRenderJob::imageCanBeComposed()
const
158 if ( imageInitialized )
162 return renderer->isReadyToCompose();
176 : mSettings( settings )
189 mErrors.append( QgsMapRendererJob::Error( QString(), tr(
"Invalid map settings" ) ) );
221 return mLabelingEngineFeedback;
226 QHash<QgsMapLayer *, int> result;
229 if (
auto &&lKey = it.key() )
230 result.insert( lKey, it.value() );
250 QSet< QgsMapLayer * > labeledLayers;
251 const QList<QgsMapLayer *> layers =
mSettings.layers();
252 for ( QgsMapLayer *ml : layers )
257 switch ( ml->type() )
261 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
271 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
281 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
313 bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
325 const QList<QgsMapLayer *> layers =
mSettings.layers();
326 for ( QgsMapLayer *ml : layers )
331 switch ( ml->type() )
335 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
345 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
355 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
386 QgsCoordinateTransform approxTransform = ct;
399 static const double SPLIT_COORD = 180.0;
412 u
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)"_s.arg( extent.
toString() )
413 .arg( extent.
width() )
416 .arg( extent2.
width() )
418 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
419 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) ),
433 extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
451 if ( ll.
x() > ur.
x() )
478 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
485 catch ( QgsCsException &e )
488 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
489 r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
496QImage *QgsMapRendererJob::allocateImage( QString layerId )
498 QImage *image =
new QImage(
mSettings.deviceOutputSize(),
mSettings.outputImageFormat() );
499 image->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
500 image->setDotsPerMeterX( 1000 *
mSettings.outputDpi() / 25.4 );
501 image->setDotsPerMeterY( 1000 *
mSettings.outputDpi() / 25.4 );
502 if ( image->isNull() )
504 mErrors.append(
Error( layerId, tr(
"Insufficient memory for image %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
511QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
513 auto elevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
514 if ( !elevationMap->isValid() )
516 mErrors.append(
Error( layerId, tr(
"Insufficient memory for elevation map %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
519 return elevationMap.release();
522QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
524 QPainter *painter =
nullptr;
525 image = allocateImage( layerId );
528 painter =
new QPainter( image );
534QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
536 auto picture = std::make_unique<QPicture>();
537 QPainter *painter =
new QPainter( picture.get() );
539 return { std::move( picture ), painter };
544 std::vector< LayerRenderJob > layerJobs;
547 QListIterator<QgsMapLayer *> li(
mSettings.layers() );
553 Q_UNUSED( cacheValid )
559 while ( li.hasPrevious() )
561 QgsMapLayer *ml = li.previous();
564 u
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6"_s.arg( ml->
name() )
576 mErrors.append(
Error( ml->
id(), QString(
"Layer %1 is invalid, skipped" ).arg( ml->
name() ) ) );
582 QgsDebugMsgLevel( u
"Layer not rendered because it is not within the defined visibility scale range"_s, 3 );
588 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's time range"_s, 3 );
594 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the layer's time range"_s, 3 );
598 if ( !
mSettings.zRange().isInfinite() && ml->elevationProperties() && !ml->elevationProperties()->isVisibleInZRange(
mSettings.zRange(), ml ) )
600 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's z range"_s, 3 );
604 QgsRectangle r1 =
mSettings.visibleExtent(), r2;
606 QgsCoordinateTransform ct;
609 bool haveExtentInLayerCrs =
true;
612 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
617 mErrors.append(
Error( ml->id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
621 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
628 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
630 mCache->clearCacheImage( ml->id() );
634 layerJobs.emplace_back( LayerRenderJob() );
635 LayerRenderJob &job = layerJobs.back();
637 job.layerId = ml->id();
638 job.renderAboveLabels = ml->customProperty( u
"rendering/renderAboveLabels"_s ).toBool();
642 if ( !ml->customProperty( u
"_noset_layer_expression_context"_s ).toBool() )
644 job.context()->setPainter( painter );
645 job.context()->setLabelingEngine( labelingEngine2 );
646 job.context()->setLabelSink(
labelSink() );
647 job.context()->setCoordinateTransform( ct );
648 job.context()->setExtent( r1 );
654 if ( mFeatureFilterProvider )
656 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
659 if ( mPerLayerTemporalRange.contains( ml ) )
661 job.context()->setTemporalRange( mPerLayerTemporalRange.value( ml ) );
664 QgsMapLayerStyleOverride styleOverride( ml );
665 if (
mSettings.layerStyleOverrides().contains( ml->id() ) )
666 styleOverride.setOverrideStyle(
mSettings.layerStyleOverrides().value( ml->id() ) );
668 job.blendMode = ml->blendMode();
673 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( ml );
680 job.opacity = ml->opacity();
685 job.opacity = ml->opacity();
688 const QgsElevationShadingRenderer shadingRenderer =
mSettings.elevationShadingRenderer();
692 if ( canUseCache &&
mCache->hasCacheImage( ml->id() ) )
695 job.imageInitialized =
true;
696 job.img =
new QImage(
mCache->cacheImage( ml->id() ) );
699 job.img->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
700 job.renderer =
nullptr;
701 job.context()->setPainter(
nullptr );
706 QElapsedTimer layerTime;
708 job.renderer = ml->createMapRenderer( *( job.context() ) );
711 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
712 job.context()->setFeedback( job.renderer->feedback() );
723 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
726 job.context()->setPainter( allocateImageAndPainter( ml->id(), job.img, job.context() ) );
730 job.renderer =
nullptr;
731 layerJobs.pop_back();
736 if ( shadingRenderer.
isActive() && ml->elevationProperties() && ml->elevationProperties()->hasElevation() )
738 job.elevationMap = allocateElevationMap( ml->id() );
739 job.context()->setElevationMap( job.elevationMap );
746 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + u
"_preview"_s,
mSettings.mapToPixel() );
747 if ( !cachedImage.isNull() )
749 job.previewRenderImage =
new QImage( cachedImage );
750 job.previewRenderImageInitialized =
true;
751 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage ) );
752 job.context()->setPainterFlagsUsingContext( painter );
755 if ( !job.previewRenderImage )
757 job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->id(), job.previewRenderImage, job.context() ) );
758 job.previewRenderImageInitialized =
false;
761 if ( !job.previewRenderImage )
763 delete job.context()->previewRenderPainter();
764 job.context()->setPreviewRenderPainter(
nullptr );
768 job.renderingTime = layerTime.elapsed();
776 std::vector< LayerRenderJob > secondPassJobs;
779 QHash<QString, LayerRenderJob *> layerJobMapping;
782 QMap<QString, bool> maskLayerHasEffects;
783 QMap<int, bool> labelHasEffects;
791 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ )
792 : layerId( layerId_ )
793 , labelRuleId( labelRuleId_ )
794 , labelMaskId( labelMaskId_ )
795 , hasEffects( hasEffects_ )
799 struct MaskedSymbolLayers
801 QSet<QString> maskedSymbolLayerIds;
802 QList<MaskSource> maskSourceList;
808 QHash<QString, MaskedSymbolLayers> maskedSymbolLayers;
814 for ( LayerRenderJob &job : firstPassJobs )
816 layerJobMapping[job.layerId] = &job;
821 QVector< QgsVectorLayer * > allRenderedVectorLayers;
822 for ( LayerRenderJob &job : firstPassJobs )
824 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
828 allRenderedVectorLayers << vl;
833 for ( LayerRenderJob &job : firstPassJobs )
835 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
840 auto collectMasks = [&maskedSymbolLayers,
841 &maskLayerHasEffects](
const QgsMaskedLayers &objectsToBeMaskedByLayer,
const QString &idOfLayerCreatingMask,
const QString &ruleId = QString(),
int labelMaskId = -1 ) {
842 bool hasEffects =
false;
843 for (
auto it = objectsToBeMaskedByLayer.begin(); it != objectsToBeMaskedByLayer.end(); ++it )
845 const QString maskedLayerId = it.key();
846 auto lit = maskedSymbolLayers.find( maskedLayerId );
847 if ( lit == maskedSymbolLayers.end() )
849 MaskedSymbolLayers maskedObjects;
850 maskedObjects.maskedSymbolLayerIds = it.value().symbolLayerIdsToMask;
851 maskedObjects.maskSourceList = QList<MaskSource>() << MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, it.value().hasEffects );
852 maskedSymbolLayers[maskedLayerId] = maskedObjects;
856 if ( lit->maskedSymbolLayerIds != it.value().symbolLayerIdsToMask )
858 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() ) );
861 lit->maskSourceList.push_back( MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, hasEffects ) );
863 hasEffects |= it.value().hasEffects;
865 if ( !objectsToBeMaskedByLayer.isEmpty() && labelMaskId == -1 )
866 maskLayerHasEffects[idOfLayerCreatingMask] = hasEffects;
871 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
873 QString labelRule = it.key();
879 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
881 const QString sourceLayerId = mit.key();
883 if ( !layerJobMapping.contains( sourceLayerId ) )
886 usableMasks.insert( sourceLayerId, mit.value() );
889 if ( usableMasks.empty() )
893 QSet<QgsSymbolLayerReference> slRefs;
894 bool hasEffects =
false;
895 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
897 const QString sourceLayerId = mit.key();
899 if ( !layerJobMapping.contains( sourceLayerId ) )
902 for (
const QString &symbolLayerId : mit.value().symbolLayerIdsToMask )
903 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
905 hasEffects |= mit.value().hasEffects;
908 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
909 labelHasEffects[labelMaskId] = hasEffects;
912 collectMasks( usableMasks, vl->
id(), labelRule, labelMaskId );
917 collectMasks( objectsToBeMaskedByLayer, vl->
id() );
920 if ( maskedSymbolLayers.isEmpty() )
921 return secondPassJobs;
924 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
926 QPaintDevice *maskPaintDevice =
nullptr;
927 QPainter *maskPainter =
nullptr;
928 if ( forceVector && !labelHasEffects[maskId] )
931 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
932 geomPaintDevice->setStrokedPathSegments( 4 );
933 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
934 maskPaintDevice = geomPaintDevice.release();
935 maskPainter =
new QPainter( maskPaintDevice );
940 QImage *maskImage =
nullptr;
941 maskPainter = allocateImageAndPainter( u
"label mask"_s, maskImage, &labelJob.context );
944 maskImage->fill( 0 );
945 maskPaintDevice = maskImage;
948 labelJob.context.setMaskPainter( maskPainter, maskId );
949 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
950 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
952 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
967 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
968 if ( !labelJob.img && canUseImage )
970 labelJob.img = allocateImage( u
"labels"_s );
972 else if ( !labelJob.picture && !canUseImage )
974 labelJob.picture = std::make_unique<QPicture>();
978 for ( LayerRenderJob &job : firstPassJobs )
980 job.maskRequiresLayerRasterization =
false;
982 auto it = maskedSymbolLayers.find( job.layerId );
983 if ( it != maskedSymbolLayers.end() )
985 const QList<MaskSource> &sourceList = it->maskSourceList;
986 for (
const MaskSource &source : sourceList )
988 job.maskRequiresLayerRasterization |= source.hasEffects;
993 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
994 if ( isRasterRendering && !job.img )
996 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
998 else if ( !isRasterRendering && !job.picture )
1000 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
1001 job.picture = std::move( pictureAndPainter.first );
1002 if ( job.context()->painter()->hasClipping() )
1005 pictureAndPainter.second->setClipping(
true );
1006 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1008 job.context()->setPainter( pictureAndPainter.second );
1011 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
1015 if ( maskLayerHasEffects.contains( job.layerId ) )
1017 QPaintDevice *maskPaintDevice =
nullptr;
1018 QPainter *maskPainter =
nullptr;
1019 if ( forceVector && !maskLayerHasEffects[job.layerId] )
1022 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >();
1023 geomPaintDevice->setStrokedPathSegments( 4 );
1024 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
1025 maskPaintDevice = geomPaintDevice.release();
1026 maskPainter =
new QPainter( maskPaintDevice );
1031 QImage *maskImage =
nullptr;
1032 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
1033 maskImage->fill( 0 );
1034 maskPaintDevice = maskImage;
1037 job.context()->setMaskPainter( maskPainter );
1038 job.maskPainter.reset( maskPainter );
1039 job.maskPaintDevice.reset( maskPaintDevice );
1043 for ( LayerRenderJob &job : firstPassJobs )
1045 QgsMapLayer *ml = job.layer;
1047 auto it = maskedSymbolLayers.find( job.layerId );
1048 if ( it == maskedSymbolLayers.end() )
1051 const QList<MaskSource> maskedSourceList = it->maskSourceList;
1052 const QSet<QString> maskedSymbolLayerIds = it->maskedSymbolLayerIds;
1054 secondPassJobs.emplace_back( LayerRenderJob() );
1055 LayerRenderJob &job2 = secondPassJobs.back();
1057 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1060 for (
const MaskSource &source : maskedSourceList )
1062 if ( source.labelMaskId != -1 )
1063 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
1065 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
1069 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1071 job2.layer = job.layer;
1072 job2.renderAboveLabels = job.renderAboveLabels;
1073 job2.layerId = job.layerId;
1076 job2.firstPassJob = &job;
1078 if ( !forceVector || job2.maskRequiresLayerRasterization )
1080 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
1084 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
1085 if ( job.context()->painter()->hasClipping() )
1088 pictureAndPainter.second->setClipping(
true );
1089 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1091 job2.picture = std::move( pictureAndPainter.first );
1092 job2.context()->setPainter( pictureAndPainter.second );
1095 if ( !job2.img && !job2.picture )
1097 secondPassJobs.pop_back();
1103 QgsVectorLayerRenderer *mapRenderer =
static_cast<QgsVectorLayerRenderer *
>( ml->
createMapRenderer( *job2.context() ) );
1104 job2.renderer = mapRenderer;
1105 if ( job2.renderer )
1107 job2.context()->setFeedback( job2.renderer->feedback() );
1112 job2.context()->setDisabledSymbolLayersV2( maskedSymbolLayerIds );
1115 return secondPassJobs;
1120 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1124 if ( !res.contains( it ) )
1136 for ( LayerRenderJob &job : secondPassJobs )
1138 if ( job.maskRequiresLayerRasterization )
1144 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1146 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1148 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1149 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1151 QgsGeometry geometry( geometryDevice->geometry().clone() );
1153#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
1160 for (
const QString &symbolLayerId : layers )
1162 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1167 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1175 job.context.setPainter( painter );
1176 job.context.setLabelingEngine( labelingEngine2 );
1177 job.context.setFeedback( mLabelingEngineFeedback );
1178 if ( labelingEngine2 )
1179 job.context.labelingEngine()->prepare( job.context );
1181 QgsRectangle r1 =
mSettings.visibleExtent();
1183 job.context.setExtent( r1 );
1185 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1186 QgsCoordinateTransform ct;
1188 job.context.setCoordinateTransform( ct );
1199 job.complete =
true;
1201 job.context.setPainter(
nullptr );
1205 if ( canUseLabelCache && (
mCache || !painter ) )
1207 job.img = allocateImage( u
"labels"_s );
1217 for ( LayerRenderJob &job : jobs )
1221 delete job.context()->painter();
1222 job.context()->setPainter(
nullptr );
1224 if (
mCache && !job.cached && job.completed && job.layer )
1227 mCache->setCacheImageWithParameters( job.layerId, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1228 mCache->setCacheImageWithParameters( job.layerId + u
"_preview"_s, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1235 if ( job.previewRenderImage )
1237 delete job.context()->previewRenderPainter();
1238 job.context()->setPreviewRenderPainter(
nullptr );
1239 delete job.previewRenderImage;
1240 job.previewRenderImage =
nullptr;
1243 if ( job.elevationMap )
1245 job.context()->setElevationMap(
nullptr );
1246 if (
mCache && !job.cached && job.completed && job.layer )
1248 QgsDebugMsgLevel( u
"caching elevation map for %1"_s.arg( job.layerId ), 2 );
1249 mCache->setCacheImageWithParameters(
1252 mCache->setCacheImageWithParameters(
1257 delete job.elevationMap;
1258 job.elevationMap =
nullptr;
1263 delete job.context()->painter();
1264 job.context()->setPainter(
nullptr );
1265 job.picture.reset(
nullptr );
1270 const QStringList
errors = job.renderer->errors();
1271 for (
const QString &message :
errors )
1272 mErrors.append(
Error( job.renderer->layerId(), message ) );
1274 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1276 delete job.renderer;
1277 job.renderer =
nullptr;
1283 job.maskPainter.reset(
nullptr );
1284 job.maskPaintDevice.reset(
nullptr );
1292 for ( LayerRenderJob &job : jobs )
1296 delete job.context()->painter();
1297 job.context()->setPainter(
nullptr );
1303 if ( job.previewRenderImage )
1305 delete job.context()->previewRenderPainter();
1306 job.context()->setPreviewRenderPainter(
nullptr );
1307 delete job.previewRenderImage;
1308 job.previewRenderImage =
nullptr;
1313 delete job.context()->painter();
1314 job.context()->setPainter(
nullptr );
1319 delete job.renderer;
1320 job.renderer =
nullptr;
1334 if (
mCache && !job.cached && !job.context.renderingStopped() )
1345 job.picture.reset(
nullptr );
1346 job.maskPainters.clear();
1347 job.maskPaintDevices.clear();
1351#define DEBUG_RENDERING 0
1357 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1358 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1362 std::unique_ptr<QgsElevationMap> mainElevationMap;
1363 if ( mapShadingRenderer.
isActive() )
1366 QPainter painter( &image );
1371 for (
const LayerRenderJob &job : jobs )
1373 if ( job.renderAboveLabels )
1380 painter.setCompositionMode( job.blendMode );
1381 painter.setOpacity( job.opacity );
1383 if ( mainElevationMap )
1386 if ( layerElevationMap.
isValid() )
1392 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1396 painter.drawImage( 0, 0, img );
1399 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1407 if ( labelJob.img && labelJob.complete )
1409 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1410 painter.setOpacity( 1.0 );
1411 painter.drawImage( 0, 0, *labelJob.img );
1413 else if ( labelJob.picture && labelJob.complete )
1415 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1416 painter.setOpacity( 1.0 );
1425 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1426 painter.setOpacity( 1.0 );
1427 painter.drawImage( 0, 0, labelCacheImage );
1431 for (
const LayerRenderJob &job : jobs )
1433 if ( !job.renderAboveLabels )
1440 painter.setCompositionMode( job.blendMode );
1441 painter.setOpacity( job.opacity );
1443 painter.drawImage( 0, 0, img );
1448 image.save(
"/tmp/final.png" );
1455 if ( job.imageCanBeComposed() )
1457 if ( job.previewRenderImage && !job.completed )
1458 return *job.previewRenderImage;
1460 Q_ASSERT( job.img );
1476 if ( job.imageCanBeComposed() && job.elevationMap )
1478 return *job.elevationMap;
1485 return QgsElevationMap();
1492 for ( LayerRenderJob &job : secondPassJobs )
1494 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1497 if ( isRasterRendering && job.maskJobs.size() > 1 )
1499 QPainter *maskPainter =
nullptr;
1500 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1502 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1505 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1509 maskPainter->drawImage( 0, 0, *maskImage );
1514 if ( !job.maskJobs.isEmpty() )
1517 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1518 if ( isRasterRendering )
1520 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1523 QPainter *painter = job.context()->painter();
1525 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1530 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1531 QVector<QRgb> mswTable;
1532 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1533 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1534 maskBinAlpha.setColorTable( mswTable );
1535 painter->drawImage( 0, 0, maskBinAlpha );
1539 QPainter tempPainter;
1542 QPainter *painter1 = job.firstPassJob->context()->painter();
1545 tempPainter.begin( job.firstPassJob->img );
1546 painter1 = &tempPainter;
1550 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1551 painter1->drawImage( 0, 0, *maskImage );
1554 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1555 painter1->drawImage( 0, 0, *job.img );
1560 job.firstPassJob->picture = std::move( job.picture );
1561 job.picture =
nullptr;
1572 QMultiMap<int, QString> elapsed;
1573 for (
const LayerRenderJob &job : jobs )
1574 elapsed.insert( job.renderingTime, job.layerId );
1575 for (
const LayerRenderJob &job : secondPassJobs )
1576 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1578 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1580 QList<int> tt( elapsed.uniqueKeys() );
1581 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1582 for (
int t : std::as_const( tt ) )
1584 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join(
", "_L1 ) ), tr(
"Rendering" ) );
1593 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1596 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), u
"rendering"_s );
1603 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1607 if ( labelingEngine2 )
1609 labelingEngine2->
run( renderContext );
1612 QgsDebugMsgLevel( u
"Draw labeling took (seconds): %1"_s.arg( t.elapsed() / 1000. ), 2 );
1617 Q_UNUSED( settings )
1619 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