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" ) ) );
222 return mLabelingEngineFeedback;
227 QHash<QgsMapLayer *, int> result;
230 if (
auto &&lKey = it.key() )
231 result.insert( lKey, it.value() );
251 QSet< QgsMapLayer * > labeledLayers;
252 const QList<QgsMapLayer *> layers =
mSettings.layers();
253 for ( QgsMapLayer *ml : layers )
258 switch ( ml->type() )
262 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
272 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
282 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
315 bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
327 const QList<QgsMapLayer *> layers =
mSettings.layers();
328 for ( QgsMapLayer *ml : layers )
333 switch ( ml->type() )
337 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
347 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
357 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
388 QgsCoordinateTransform approxTransform = ct;
401 static const double SPLIT_COORD = 180.0;
416 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
417 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
431 extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
451 if ( ll.
x() > ur.
x() )
480 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
487 catch ( QgsCsException &e )
490 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
491 r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
498QImage *QgsMapRendererJob::allocateImage( QString layerId )
500 QImage *image =
new QImage(
mSettings.deviceOutputSize(),
502 image->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
503 image->setDotsPerMeterX( 1000 *
mSettings.outputDpi() / 25.4 );
504 image->setDotsPerMeterY( 1000 *
mSettings.outputDpi() / 25.4 );
505 if ( image->isNull() )
507 mErrors.append(
Error( layerId, tr(
"Insufficient memory for image %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
514QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
516 auto elevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
517 if ( !elevationMap->isValid() )
519 mErrors.append(
Error( layerId, tr(
"Insufficient memory for elevation map %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
522 return elevationMap.release();
525QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
527 QPainter *painter =
nullptr;
528 image = allocateImage( layerId );
531 painter =
new QPainter( image );
537QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
539 auto picture = std::make_unique<QPicture>();
540 QPainter *painter =
new QPainter( picture.get() );
542 return { std::move( picture ), painter };
547 std::vector< LayerRenderJob > layerJobs;
550 QListIterator<QgsMapLayer *> li(
mSettings.layers() );
556 Q_UNUSED( cacheValid )
562 while ( li.hasPrevious() )
564 QgsMapLayer *ml = li.previous();
566 QgsDebugMsgLevel( u
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6"_s
578 mErrors.append(
Error( ml->
id(), QString(
"Layer %1 is invalid, skipped" ).arg( ml->
name() ) ) );
584 QgsDebugMsgLevel( u
"Layer not rendered because it is not within the defined visibility scale range"_s, 3 );
590 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's time range"_s, 3 );
596 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's z range"_s, 3 );
600 QgsRectangle r1 =
mSettings.visibleExtent(), r2;
602 QgsCoordinateTransform ct;
605 bool haveExtentInLayerCrs =
true;
608 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
613 mErrors.append(
Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
617 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
624 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
626 mCache->clearCacheImage( ml->
id() );
630 layerJobs.emplace_back( LayerRenderJob() );
631 LayerRenderJob &job = layerJobs.back();
633 job.layerId = ml->
id();
634 job.renderAboveLabels = ml->
customProperty( u
"rendering/renderAboveLabels"_s ).toBool();
638 if ( !ml->
customProperty( u
"_noset_layer_expression_context"_s ).toBool() )
640 job.context()->setPainter( painter );
641 job.context()->setLabelingEngine( labelingEngine2 );
642 job.context()->setLabelSink(
labelSink() );
643 job.context()->setCoordinateTransform( ct );
644 job.context()->setExtent( r1 );
650 if ( mFeatureFilterProvider )
652 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
655 QgsMapLayerStyleOverride styleOverride( ml );
656 if (
mSettings.layerStyleOverrides().contains( ml->
id() ) )
657 styleOverride.setOverrideStyle(
mSettings.layerStyleOverrides().value( ml->
id() ) );
664 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( ml );
679 const QgsElevationShadingRenderer shadingRenderer =
mSettings.elevationShadingRenderer();
684 if ( canUseCache &&
mCache->hasCacheImage( ml->
id() ) )
687 job.imageInitialized =
true;
688 job.img =
new QImage(
mCache->cacheImage( ml->
id() ) );
694 job.img->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
695 job.renderer =
nullptr;
696 job.context()->setPainter(
nullptr );
701 QElapsedTimer layerTime;
706 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
707 job.context()->setFeedback( job.renderer->feedback() );
718 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
721 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
725 job.renderer =
nullptr;
726 layerJobs.pop_back();
735 job.elevationMap = allocateElevationMap( ml->
id() );
736 job.context()->setElevationMap( job.elevationMap );
743 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + u
"_preview"_s,
mSettings.mapToPixel() );
744 if ( !cachedImage.isNull() )
746 job.previewRenderImage =
new QImage( cachedImage );
747 job.previewRenderImageInitialized =
true;
748 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage ) );
749 job.context()->setPainterFlagsUsingContext( painter );
752 if ( !job.previewRenderImage )
754 job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
755 job.previewRenderImageInitialized =
false;
758 if ( !job.previewRenderImage )
760 delete job.context()->previewRenderPainter();
761 job.context()->setPreviewRenderPainter(
nullptr );
765 job.renderingTime = layerTime.elapsed();
773 std::vector< LayerRenderJob > secondPassJobs;
776 QHash<QString, LayerRenderJob *> layerJobMapping;
779 QMap<QString, bool> maskLayerHasEffects;
780 QMap<int, bool> labelHasEffects;
788 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ ):
789 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
792 struct MaskedSymbolLayers
794 QSet<QString> maskedSymbolLayerIds;
795 QList<MaskSource> maskSourceList;
801 QHash<QString, MaskedSymbolLayers> maskedSymbolLayers;
808 for ( LayerRenderJob &job : firstPassJobs )
810 layerJobMapping[job.layerId] = &job;
815 QVector< QgsVectorLayer * > allRenderedVectorLayers;
816 for ( LayerRenderJob &job : firstPassJobs )
818 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
822 allRenderedVectorLayers << vl;
827 for ( LayerRenderJob &job : firstPassJobs )
829 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
834 auto collectMasks = [&maskedSymbolLayers, &maskLayerHasEffects](
const QgsMaskedLayers & objectsToBeMaskedByLayer,
const QString & idOfLayerCreatingMask,
const QString &ruleId = QString(),
int labelMaskId = -1 )
836 bool hasEffects =
false;
837 for (
auto it = objectsToBeMaskedByLayer.begin(); it != objectsToBeMaskedByLayer.end(); ++it )
839 const QString maskedLayerId = it.key();
840 auto lit = maskedSymbolLayers.find( maskedLayerId );
841 if ( lit == maskedSymbolLayers.end() )
843 MaskedSymbolLayers maskedObjects;
844 maskedObjects.maskedSymbolLayerIds = it.value().symbolLayerIdsToMask;
845 maskedObjects.maskSourceList = QList<MaskSource>() << MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, it.value().hasEffects );
846 maskedSymbolLayers[maskedLayerId] = maskedObjects;
850 if ( lit->maskedSymbolLayerIds != it.value().symbolLayerIdsToMask )
852 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() ) );
855 lit->maskSourceList.push_back( MaskSource( idOfLayerCreatingMask, ruleId, labelMaskId, hasEffects ) );
857 hasEffects |= it.value().hasEffects;
859 if ( ! objectsToBeMaskedByLayer.isEmpty() && labelMaskId == -1 )
860 maskLayerHasEffects[ idOfLayerCreatingMask ] = hasEffects;
865 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
867 QString labelRule = it.key();
873 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
875 const QString sourceLayerId = mit.key();
877 if ( !layerJobMapping.contains( sourceLayerId ) )
880 usableMasks.insert( sourceLayerId, mit.value() );
883 if ( usableMasks.empty() )
887 QSet<QgsSymbolLayerReference> slRefs;
888 bool hasEffects =
false;
889 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
891 const QString sourceLayerId = mit.key();
893 if ( !layerJobMapping.contains( sourceLayerId ) )
896 for (
const QString &symbolLayerId : mit.value().symbolLayerIdsToMask )
897 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
899 hasEffects |= mit.value().hasEffects;
902 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
903 labelHasEffects[ labelMaskId ] = hasEffects;
906 collectMasks( usableMasks, vl->
id(), labelRule, labelMaskId );
911 collectMasks( objectsToBeMaskedByLayer, vl->
id() );
914 if ( maskedSymbolLayers.isEmpty() )
915 return secondPassJobs;
918 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
920 QPaintDevice *maskPaintDevice =
nullptr;
921 QPainter *maskPainter =
nullptr;
922 if ( forceVector && !labelHasEffects[ maskId ] )
925 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
926 geomPaintDevice->setStrokedPathSegments( 4 );
927 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
928 maskPaintDevice = geomPaintDevice.release();
929 maskPainter =
new QPainter( maskPaintDevice );
934 QImage *maskImage =
nullptr;
935 maskPainter = allocateImageAndPainter( u
"label mask"_s, maskImage, &labelJob.context );
938 maskImage->fill( 0 );
939 maskPaintDevice = maskImage;
942 labelJob.context.setMaskPainter( maskPainter, maskId );
943 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
944 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
946 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
961 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
962 if ( !labelJob.img && canUseImage )
964 labelJob.img = allocateImage( u
"labels"_s );
966 else if ( !labelJob.picture && !canUseImage )
968 labelJob.picture = std::make_unique<QPicture>( );
972 for ( LayerRenderJob &job : firstPassJobs )
974 job.maskRequiresLayerRasterization =
false;
976 auto it = maskedSymbolLayers.find( job.layerId );
977 if ( it != maskedSymbolLayers.end() )
979 const QList<MaskSource> &sourceList = it->maskSourceList;
980 for (
const MaskSource &source : sourceList )
982 job.maskRequiresLayerRasterization |= source.hasEffects;
987 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
988 if ( isRasterRendering && !job.img )
990 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
992 else if ( !isRasterRendering && !job.picture )
994 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
995 job.picture = std::move( pictureAndPainter.first );
996 if ( job.context()->painter()->hasClipping() )
999 pictureAndPainter.second->setClipping(
true );
1000 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1002 job.context()->setPainter( pictureAndPainter.second );
1005 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
1009 if ( maskLayerHasEffects.contains( job.layerId ) )
1011 QPaintDevice *maskPaintDevice =
nullptr;
1012 QPainter *maskPainter =
nullptr;
1013 if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
1016 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
1017 geomPaintDevice->setStrokedPathSegments( 4 );
1018 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
1019 maskPaintDevice = geomPaintDevice.release();
1020 maskPainter =
new QPainter( maskPaintDevice );
1025 QImage *maskImage =
nullptr;
1026 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
1027 maskImage->fill( 0 );
1028 maskPaintDevice = maskImage;
1031 job.context()->setMaskPainter( maskPainter );
1032 job.maskPainter.reset( maskPainter );
1033 job.maskPaintDevice.reset( maskPaintDevice );
1037 for ( LayerRenderJob &job : firstPassJobs )
1039 QgsMapLayer *ml = job.layer;
1041 auto it = maskedSymbolLayers.find( job.layerId );
1042 if ( it == maskedSymbolLayers.end() )
1045 const QList<MaskSource> maskedSourceList = it->maskSourceList;
1046 const QSet<QString> maskedSymbolLayerIds = it->maskedSymbolLayerIds;
1048 secondPassJobs.emplace_back( LayerRenderJob() );
1049 LayerRenderJob &job2 = secondPassJobs.back();
1051 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1054 for (
const MaskSource &source : maskedSourceList )
1056 if ( source.labelMaskId != -1 )
1057 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
1059 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
1063 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1065 job2.layer = job.layer;
1066 job2.renderAboveLabels = job.renderAboveLabels;
1067 job2.layerId = job.layerId;
1070 job2.firstPassJob = &job;
1072 if ( !forceVector || job2.maskRequiresLayerRasterization )
1074 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
1078 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
1079 if ( job.context()->painter()->hasClipping() )
1082 pictureAndPainter.second->setClipping(
true );
1083 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1085 job2.picture = std::move( pictureAndPainter.first );
1086 job2.context()->setPainter( pictureAndPainter.second );
1089 if ( ! job2.img && ! job2.picture )
1091 secondPassJobs.pop_back();
1097 QgsVectorLayerRenderer *mapRenderer =
static_cast<QgsVectorLayerRenderer *
>( ml->
createMapRenderer( *job2.context() ) );
1098 job2.renderer = mapRenderer;
1099 if ( job2.renderer )
1101 job2.context()->setFeedback( job2.renderer->feedback() );
1106 job2.context()->setDisabledSymbolLayersV2( maskedSymbolLayerIds );
1109 return secondPassJobs;
1114 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1118 if ( !res.contains( it ) )
1130 for ( LayerRenderJob &job : secondPassJobs )
1132 if ( job.maskRequiresLayerRasterization )
1138 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1140 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1142 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1143 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1145 QgsGeometry geometry( geometryDevice->geometry().clone() );
1147#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
1154 for (
const QString &symbolLayerId : layers )
1156 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1161 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1169 job.context.setPainter( painter );
1170 job.context.setLabelingEngine( labelingEngine2 );
1171 job.context.setFeedback( mLabelingEngineFeedback );
1172 if ( labelingEngine2 )
1173 job.context.labelingEngine()->prepare( job.context );
1175 QgsRectangle r1 =
mSettings.visibleExtent();
1177 job.context.setExtent( r1 );
1179 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1180 QgsCoordinateTransform ct;
1182 job.context.setCoordinateTransform( ct );
1193 job.complete =
true;
1195 Q_ASSERT( job.img->devicePixelRatio() ==
mSettings.devicePixelRatio() );
1196 job.context.setPainter(
nullptr );
1200 if ( canUseLabelCache && (
mCache || !painter ) )
1202 job.img = allocateImage( u
"labels"_s );
1212 for ( LayerRenderJob &job : jobs )
1216 delete job.context()->painter();
1217 job.context()->setPainter(
nullptr );
1219 if (
mCache && !job.cached && job.completed && job.layer )
1222 mCache->setCacheImageWithParameters( job.layerId, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1223 mCache->setCacheImageWithParameters( job.layerId + u
"_preview"_s, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1230 if ( job.previewRenderImage )
1232 delete job.context()->previewRenderPainter();
1233 job.context()->setPreviewRenderPainter(
nullptr );
1234 delete job.previewRenderImage;
1235 job.previewRenderImage =
nullptr;
1238 if ( job.elevationMap )
1240 job.context()->setElevationMap(
nullptr );
1241 if (
mCache && !job.cached && job.completed && job.layer )
1243 QgsDebugMsgLevel( u
"caching elevation map for %1"_s.arg( job.layerId ), 2 );
1244 mCache->setCacheImageWithParameters(
1246 job.elevationMap->rawElevationImage(),
1249 QList< QgsMapLayer * >() << job.layer );
1250 mCache->setCacheImageWithParameters(
1252 job.elevationMap->rawElevationImage(),
1255 QList< QgsMapLayer * >() << job.layer );
1258 delete job.elevationMap;
1259 job.elevationMap =
nullptr;
1264 delete job.context()->painter();
1265 job.context()->setPainter(
nullptr );
1266 job.picture.reset(
nullptr );
1271 const QStringList
errors = job.renderer->errors();
1272 for (
const QString &message :
errors )
1273 mErrors.append(
Error( job.renderer->layerId(), message ) );
1275 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1277 delete job.renderer;
1278 job.renderer =
nullptr;
1284 job.maskPainter.reset(
nullptr );
1285 job.maskPaintDevice.reset(
nullptr );
1293 for ( LayerRenderJob &job : jobs )
1297 delete job.context()->painter();
1298 job.context()->setPainter(
nullptr );
1304 if ( job.previewRenderImage )
1306 delete job.context()->previewRenderPainter();
1307 job.context()->setPreviewRenderPainter(
nullptr );
1308 delete job.previewRenderImage;
1309 job.previewRenderImage =
nullptr;
1314 delete job.context()->painter();
1315 job.context()->setPainter(
nullptr );
1320 delete job.renderer;
1321 job.renderer =
nullptr;
1335 if (
mCache && !job.cached && !job.context.renderingStopped() )
1346 job.picture.reset(
nullptr );
1347 job.maskPainters.clear();
1348 job.maskPaintDevices.clear();
1352#define DEBUG_RENDERING 0
1355 const std::vector<LayerRenderJob> &jobs,
1356 const LabelRenderJob &labelJob,
1362 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1363 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1367 std::unique_ptr<QgsElevationMap> mainElevationMap;
1368 if ( mapShadingRenderer.
isActive() )
1371 QPainter painter( &image );
1376 for (
const LayerRenderJob &job : jobs )
1378 if ( job.renderAboveLabels )
1385 painter.setCompositionMode( job.blendMode );
1386 painter.setOpacity( job.opacity );
1388 if ( mainElevationMap )
1391 if ( layerElevationMap.
isValid() )
1397 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1401 painter.drawImage( 0, 0, img );
1404 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1412 if ( labelJob.img && labelJob.complete )
1414 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1415 painter.setOpacity( 1.0 );
1416 painter.drawImage( 0, 0, *labelJob.img );
1418 else if ( labelJob.picture && labelJob.complete )
1420 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1421 painter.setOpacity( 1.0 );
1430 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1431 painter.setOpacity( 1.0 );
1432 painter.drawImage( 0, 0, labelCacheImage );
1436 for (
const LayerRenderJob &job : jobs )
1438 if ( !job.renderAboveLabels )
1445 painter.setCompositionMode( job.blendMode );
1446 painter.setOpacity( job.opacity );
1448 painter.drawImage( 0, 0, img );
1453 image.save(
"/tmp/final.png" );
1460 const LayerRenderJob &job,
1464 if ( job.imageCanBeComposed() )
1466 if ( job.previewRenderImage && !job.completed )
1467 return *job.previewRenderImage;
1469 Q_ASSERT( job.img );
1485 if ( job.imageCanBeComposed() && job.elevationMap )
1487 return *job.elevationMap;
1494 return QgsElevationMap();
1501 for ( LayerRenderJob &job : secondPassJobs )
1503 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1506 if ( isRasterRendering && job.maskJobs.size() > 1 )
1508 QPainter *maskPainter =
nullptr;
1509 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1511 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1514 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1518 maskPainter->drawImage( 0, 0, *maskImage );
1523 if ( ! job.maskJobs.isEmpty() )
1526 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1527 if ( isRasterRendering )
1529 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1532 QPainter *painter = job.context()->painter();
1534 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1539 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1540 QVector<QRgb> mswTable;
1541 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1542 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1543 maskBinAlpha.setColorTable( mswTable );
1544 painter->drawImage( 0, 0, maskBinAlpha );
1548 QPainter tempPainter;
1551 QPainter *painter1 = job.firstPassJob->context()->painter();
1554 tempPainter.begin( job.firstPassJob->img );
1555 painter1 = &tempPainter;
1559 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1560 painter1->drawImage( 0, 0, *maskImage );
1563 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1564 painter1->drawImage( 0, 0, *job.img );
1569 job.firstPassJob->picture = std::move( job.picture );
1570 job.picture =
nullptr;
1581 QMultiMap<int, QString> elapsed;
1582 for (
const LayerRenderJob &job : jobs )
1583 elapsed.insert( job.renderingTime, job.layerId );
1584 for (
const LayerRenderJob &job : secondPassJobs )
1585 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1587 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1589 QList<int> tt( elapsed.uniqueKeys() );
1590 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1591 for (
int t : std::as_const( tt ) )
1593 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join(
", "_L1 ) ), tr(
"Rendering" ) );
1602 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1605 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), u
"rendering"_s );
1612 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1616 if ( labelingEngine2 )
1618 labelingEngine2->
run( renderContext );
1621 QgsDebugMsgLevel( u
"Draw labeling took (seconds): %1"_s.arg( t.elapsed() / 1000. ), 2 );
1626 Q_UNUSED( settings )
1628 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())
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 truncated to the sp...
void setXMinimum(double x)
Set the minimum x value.
void setXMaximum(double x)
Set the maximum x value.
void grow(double delta)
Grows the rectangle in place by the specified amount.
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Contains information about the context of a rendering operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Stores collated details of rendered items during a map rendering operation.
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