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 map's z range"_s, 3 );
598 QgsRectangle r1 =
mSettings.visibleExtent(), r2;
600 QgsCoordinateTransform ct;
603 bool haveExtentInLayerCrs =
true;
606 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
611 mErrors.append(
Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
615 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
622 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
624 mCache->clearCacheImage( ml->
id() );
628 layerJobs.emplace_back( LayerRenderJob() );
629 LayerRenderJob &job = layerJobs.back();
631 job.layerId = ml->
id();
632 job.renderAboveLabels = ml->
customProperty( u
"rendering/renderAboveLabels"_s ).toBool();
636 if ( !ml->
customProperty( u
"_noset_layer_expression_context"_s ).toBool() )
638 job.context()->setPainter( painter );
639 job.context()->setLabelingEngine( labelingEngine2 );
640 job.context()->setLabelSink(
labelSink() );
641 job.context()->setCoordinateTransform( ct );
642 job.context()->setExtent( r1 );
648 if ( mFeatureFilterProvider )
650 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
653 QgsMapLayerStyleOverride styleOverride( ml );
654 if (
mSettings.layerStyleOverrides().contains( ml->
id() ) )
655 styleOverride.setOverrideStyle(
mSettings.layerStyleOverrides().value( ml->
id() ) );
662 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( ml );
677 const QgsElevationShadingRenderer shadingRenderer =
mSettings.elevationShadingRenderer();
681 if ( canUseCache &&
mCache->hasCacheImage( ml->
id() ) )
684 job.imageInitialized =
true;
685 job.img =
new QImage(
mCache->cacheImage( ml->
id() ) );
688 job.img->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
689 job.renderer =
nullptr;
690 job.context()->setPainter(
nullptr );
695 QElapsedTimer layerTime;
700 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
701 job.context()->setFeedback( job.renderer->feedback() );
712 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
715 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
719 job.renderer =
nullptr;
720 layerJobs.pop_back();
727 job.elevationMap = allocateElevationMap( ml->
id() );
728 job.context()->setElevationMap( job.elevationMap );
735 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + u
"_preview"_s,
mSettings.mapToPixel() );
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_ )
782 , labelRuleId( labelRuleId_ )
783 , labelMaskId( labelMaskId_ )
784 , hasEffects( hasEffects_ )
788 struct MaskedSymbolLayers
790 QSet<QString> maskedSymbolLayerIds;
791 QList<MaskSource> maskSourceList;
797 QHash<QString, MaskedSymbolLayers> maskedSymbolLayers;
803 for ( LayerRenderJob &job : firstPassJobs )
805 layerJobMapping[job.layerId] = &job;
810 QVector< QgsVectorLayer * > allRenderedVectorLayers;
811 for ( LayerRenderJob &job : firstPassJobs )
813 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
817 allRenderedVectorLayers << vl;
822 for ( LayerRenderJob &job : firstPassJobs )
824 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
829 auto collectMasks = [&maskedSymbolLayers,
830 &maskLayerHasEffects](
const QgsMaskedLayers &objectsToBeMaskedByLayer,
const QString &idOfLayerCreatingMask,
const QString &ruleId = QString(),
int labelMaskId = -1 ) {
831 bool hasEffects =
false;
832 for (
auto it = objectsToBeMaskedByLayer.begin(); it != objectsToBeMaskedByLayer.end(); ++it )
834 const QString maskedLayerId = it.key();
835 auto lit = maskedSymbolLayers.find( maskedLayerId );
836 if ( lit == maskedSymbolLayers.end() )
838 MaskedSymbolLayers maskedObjects;
839 maskedObjects.maskedSymbolLayerIds = it.value().symbolLayerIdsToMask;
840 maskedObjects.maskSourceList = QList<MaskSource>() << MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, it.value().hasEffects );
841 maskedSymbolLayers[maskedLayerId] = maskedObjects;
845 if ( lit->maskedSymbolLayerIds != it.value().symbolLayerIdsToMask )
847 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() ) );
850 lit->maskSourceList.push_back( MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, hasEffects ) );
852 hasEffects |= it.value().hasEffects;
854 if ( !objectsToBeMaskedByLayer.isEmpty() && labelMaskId == -1 )
855 maskLayerHasEffects[idOfLayerCreatingMask] = hasEffects;
860 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
862 QString labelRule = it.key();
868 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
870 const QString sourceLayerId = mit.key();
872 if ( !layerJobMapping.contains( sourceLayerId ) )
875 usableMasks.insert( sourceLayerId, mit.value() );
878 if ( usableMasks.empty() )
882 QSet<QgsSymbolLayerReference> slRefs;
883 bool hasEffects =
false;
884 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
886 const QString sourceLayerId = mit.key();
888 if ( !layerJobMapping.contains( sourceLayerId ) )
891 for (
const QString &symbolLayerId : mit.value().symbolLayerIdsToMask )
892 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
894 hasEffects |= mit.value().hasEffects;
897 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
898 labelHasEffects[labelMaskId] = hasEffects;
901 collectMasks( usableMasks, vl->
id(), labelRule, labelMaskId );
906 collectMasks( objectsToBeMaskedByLayer, vl->
id() );
909 if ( maskedSymbolLayers.isEmpty() )
910 return secondPassJobs;
913 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
915 QPaintDevice *maskPaintDevice =
nullptr;
916 QPainter *maskPainter =
nullptr;
917 if ( forceVector && !labelHasEffects[maskId] )
920 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
921 geomPaintDevice->setStrokedPathSegments( 4 );
922 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
923 maskPaintDevice = geomPaintDevice.release();
924 maskPainter =
new QPainter( maskPaintDevice );
929 QImage *maskImage =
nullptr;
930 maskPainter = allocateImageAndPainter( u
"label mask"_s, maskImage, &labelJob.context );
933 maskImage->fill( 0 );
934 maskPaintDevice = maskImage;
937 labelJob.context.setMaskPainter( maskPainter, maskId );
938 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
939 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
941 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
956 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
957 if ( !labelJob.img && canUseImage )
959 labelJob.img = allocateImage( u
"labels"_s );
961 else if ( !labelJob.picture && !canUseImage )
963 labelJob.picture = std::make_unique<QPicture>();
967 for ( LayerRenderJob &job : firstPassJobs )
969 job.maskRequiresLayerRasterization =
false;
971 auto it = maskedSymbolLayers.find( job.layerId );
972 if ( it != maskedSymbolLayers.end() )
974 const QList<MaskSource> &sourceList = it->maskSourceList;
975 for (
const MaskSource &source : sourceList )
977 job.maskRequiresLayerRasterization |= source.hasEffects;
982 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
983 if ( isRasterRendering && !job.img )
985 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
987 else if ( !isRasterRendering && !job.picture )
989 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
990 job.picture = std::move( pictureAndPainter.first );
991 if ( job.context()->painter()->hasClipping() )
994 pictureAndPainter.second->setClipping(
true );
995 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
997 job.context()->setPainter( pictureAndPainter.second );
1000 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
1004 if ( maskLayerHasEffects.contains( job.layerId ) )
1006 QPaintDevice *maskPaintDevice =
nullptr;
1007 QPainter *maskPainter =
nullptr;
1008 if ( forceVector && !maskLayerHasEffects[job.layerId] )
1011 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >();
1012 geomPaintDevice->setStrokedPathSegments( 4 );
1013 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
1014 maskPaintDevice = geomPaintDevice.release();
1015 maskPainter =
new QPainter( maskPaintDevice );
1020 QImage *maskImage =
nullptr;
1021 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
1022 maskImage->fill( 0 );
1023 maskPaintDevice = maskImage;
1026 job.context()->setMaskPainter( maskPainter );
1027 job.maskPainter.reset( maskPainter );
1028 job.maskPaintDevice.reset( maskPaintDevice );
1032 for ( LayerRenderJob &job : firstPassJobs )
1034 QgsMapLayer *ml = job.layer;
1036 auto it = maskedSymbolLayers.find( job.layerId );
1037 if ( it == maskedSymbolLayers.end() )
1040 const QList<MaskSource> maskedSourceList = it->maskSourceList;
1041 const QSet<QString> maskedSymbolLayerIds = it->maskedSymbolLayerIds;
1043 secondPassJobs.emplace_back( LayerRenderJob() );
1044 LayerRenderJob &job2 = secondPassJobs.back();
1046 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1049 for (
const MaskSource &source : maskedSourceList )
1051 if ( source.labelMaskId != -1 )
1052 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
1054 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
1058 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1060 job2.layer = job.layer;
1061 job2.renderAboveLabels = job.renderAboveLabels;
1062 job2.layerId = job.layerId;
1065 job2.firstPassJob = &job;
1067 if ( !forceVector || job2.maskRequiresLayerRasterization )
1069 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
1073 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
1074 if ( job.context()->painter()->hasClipping() )
1077 pictureAndPainter.second->setClipping(
true );
1078 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1080 job2.picture = std::move( pictureAndPainter.first );
1081 job2.context()->setPainter( pictureAndPainter.second );
1084 if ( !job2.img && !job2.picture )
1086 secondPassJobs.pop_back();
1092 QgsVectorLayerRenderer *mapRenderer =
static_cast<QgsVectorLayerRenderer *
>( ml->
createMapRenderer( *job2.context() ) );
1093 job2.renderer = mapRenderer;
1094 if ( job2.renderer )
1096 job2.context()->setFeedback( job2.renderer->feedback() );
1101 job2.context()->setDisabledSymbolLayersV2( maskedSymbolLayerIds );
1104 return secondPassJobs;
1109 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1113 if ( !res.contains( it ) )
1125 for ( LayerRenderJob &job : secondPassJobs )
1127 if ( job.maskRequiresLayerRasterization )
1133 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1135 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1137 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1138 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1140 QgsGeometry geometry( geometryDevice->geometry().clone() );
1142#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 10
1149 for (
const QString &symbolLayerId : layers )
1151 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1156 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1164 job.context.setPainter( painter );
1165 job.context.setLabelingEngine( labelingEngine2 );
1166 job.context.setFeedback( mLabelingEngineFeedback );
1167 if ( labelingEngine2 )
1168 job.context.labelingEngine()->prepare( job.context );
1170 QgsRectangle r1 =
mSettings.visibleExtent();
1172 job.context.setExtent( r1 );
1174 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1175 QgsCoordinateTransform ct;
1177 job.context.setCoordinateTransform( ct );
1188 job.complete =
true;
1190 Q_ASSERT( job.img->devicePixelRatio() ==
mSettings.devicePixelRatio() );
1191 job.context.setPainter(
nullptr );
1195 if ( canUseLabelCache && (
mCache || !painter ) )
1197 job.img = allocateImage( u
"labels"_s );
1207 for ( LayerRenderJob &job : jobs )
1211 delete job.context()->painter();
1212 job.context()->setPainter(
nullptr );
1214 if (
mCache && !job.cached && job.completed && job.layer )
1217 mCache->setCacheImageWithParameters( job.layerId, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1218 mCache->setCacheImageWithParameters( job.layerId + u
"_preview"_s, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1225 if ( job.previewRenderImage )
1227 delete job.context()->previewRenderPainter();
1228 job.context()->setPreviewRenderPainter(
nullptr );
1229 delete job.previewRenderImage;
1230 job.previewRenderImage =
nullptr;
1233 if ( job.elevationMap )
1235 job.context()->setElevationMap(
nullptr );
1236 if (
mCache && !job.cached && job.completed && job.layer )
1238 QgsDebugMsgLevel( u
"caching elevation map for %1"_s.arg( job.layerId ), 2 );
1239 mCache->setCacheImageWithParameters(
1242 mCache->setCacheImageWithParameters(
1247 delete job.elevationMap;
1248 job.elevationMap =
nullptr;
1253 delete job.context()->painter();
1254 job.context()->setPainter(
nullptr );
1255 job.picture.reset(
nullptr );
1260 const QStringList
errors = job.renderer->errors();
1261 for (
const QString &message :
errors )
1262 mErrors.append(
Error( job.renderer->layerId(), message ) );
1264 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1266 delete job.renderer;
1267 job.renderer =
nullptr;
1273 job.maskPainter.reset(
nullptr );
1274 job.maskPaintDevice.reset(
nullptr );
1282 for ( LayerRenderJob &job : jobs )
1286 delete job.context()->painter();
1287 job.context()->setPainter(
nullptr );
1293 if ( job.previewRenderImage )
1295 delete job.context()->previewRenderPainter();
1296 job.context()->setPreviewRenderPainter(
nullptr );
1297 delete job.previewRenderImage;
1298 job.previewRenderImage =
nullptr;
1303 delete job.context()->painter();
1304 job.context()->setPainter(
nullptr );
1309 delete job.renderer;
1310 job.renderer =
nullptr;
1324 if (
mCache && !job.cached && !job.context.renderingStopped() )
1335 job.picture.reset(
nullptr );
1336 job.maskPainters.clear();
1337 job.maskPaintDevices.clear();
1341#define DEBUG_RENDERING 0
1347 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1348 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1352 std::unique_ptr<QgsElevationMap> mainElevationMap;
1353 if ( mapShadingRenderer.
isActive() )
1356 QPainter painter( &image );
1361 for (
const LayerRenderJob &job : jobs )
1363 if ( job.renderAboveLabels )
1370 painter.setCompositionMode( job.blendMode );
1371 painter.setOpacity( job.opacity );
1373 if ( mainElevationMap )
1376 if ( layerElevationMap.
isValid() )
1382 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1386 painter.drawImage( 0, 0, img );
1389 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1397 if ( labelJob.img && labelJob.complete )
1399 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1400 painter.setOpacity( 1.0 );
1401 painter.drawImage( 0, 0, *labelJob.img );
1403 else if ( labelJob.picture && labelJob.complete )
1405 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1406 painter.setOpacity( 1.0 );
1415 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1416 painter.setOpacity( 1.0 );
1417 painter.drawImage( 0, 0, labelCacheImage );
1421 for (
const LayerRenderJob &job : jobs )
1423 if ( !job.renderAboveLabels )
1430 painter.setCompositionMode( job.blendMode );
1431 painter.setOpacity( job.opacity );
1433 painter.drawImage( 0, 0, img );
1438 image.save(
"/tmp/final.png" );
1445 if ( job.imageCanBeComposed() )
1447 if ( job.previewRenderImage && !job.completed )
1448 return *job.previewRenderImage;
1450 Q_ASSERT( job.img );
1466 if ( job.imageCanBeComposed() && job.elevationMap )
1468 return *job.elevationMap;
1475 return QgsElevationMap();
1482 for ( LayerRenderJob &job : secondPassJobs )
1484 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1487 if ( isRasterRendering && job.maskJobs.size() > 1 )
1489 QPainter *maskPainter =
nullptr;
1490 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1492 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1495 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1499 maskPainter->drawImage( 0, 0, *maskImage );
1504 if ( !job.maskJobs.isEmpty() )
1507 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1508 if ( isRasterRendering )
1510 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1513 QPainter *painter = job.context()->painter();
1515 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1520 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1521 QVector<QRgb> mswTable;
1522 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1523 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1524 maskBinAlpha.setColorTable( mswTable );
1525 painter->drawImage( 0, 0, maskBinAlpha );
1529 QPainter tempPainter;
1532 QPainter *painter1 = job.firstPassJob->context()->painter();
1535 tempPainter.begin( job.firstPassJob->img );
1536 painter1 = &tempPainter;
1540 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1541 painter1->drawImage( 0, 0, *maskImage );
1544 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1545 painter1->drawImage( 0, 0, *job.img );
1550 job.firstPassJob->picture = std::move( job.picture );
1551 job.picture =
nullptr;
1562 QMultiMap<int, QString> elapsed;
1563 for (
const LayerRenderJob &job : jobs )
1564 elapsed.insert( job.renderingTime, job.layerId );
1565 for (
const LayerRenderJob &job : secondPassJobs )
1566 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1568 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1570 QList<int> tt( elapsed.uniqueKeys() );
1571 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1572 for (
int t : std::as_const( tt ) )
1574 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join(
", "_L1 ) ), tr(
"Rendering" ) );
1583 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1586 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), u
"rendering"_s );
1593 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1597 if ( labelingEngine2 )
1599 labelingEngine2->
run( renderContext );
1602 QgsDebugMsgLevel( u
"Draw labeling took (seconds): %1"_s.arg( t.elapsed() / 1000. ), 2 );
1607 Q_UNUSED( settings )
1609 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 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 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 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