51#include <QElapsedTimer>
55#include <QtConcurrentMap>
57#include "moc_qgsmaprendererjob.cpp"
68LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
73 mContext = std::move( other.mContext );
78 renderer = other.renderer;
79 other.renderer =
nullptr;
81 previewRenderImage = other.previewRenderImage;
82 other.previewRenderImage =
nullptr;
84 imageInitialized = other.imageInitialized;
85 previewRenderImageInitialized = other.previewRenderImageInitialized;
87 blendMode = other.blendMode;
88 opacity = other.opacity;
89 cached = other.cached;
91 renderAboveLabels = other.renderAboveLabels;
92 completed = other.completed;
93 renderingTime = other.renderingTime;
94 estimatedRenderingTime = other.estimatedRenderingTime ;
95 errors = other.errors;
96 layerId = other.layerId;
98 maskPaintDevice = std::move( other.maskPaintDevice );
100 firstPassJob = other.firstPassJob;
101 other.firstPassJob =
nullptr;
103 picture = std::move( other.picture );
105 maskJobs = other.maskJobs;
107 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
109 elevationMap = other.elevationMap;
110 maskPainter = std::move( other.maskPainter );
115LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
116 : imageInitialized( other.imageInitialized )
117 , previewRenderImageInitialized( other.previewRenderImageInitialized )
118 , blendMode( other.blendMode )
119 , opacity( other.opacity )
120 , cached( other.cached )
121 , renderAboveLabels( other.renderAboveLabels )
122 , layer( other.layer )
123 , completed( other.completed )
124 , renderingTime( other.renderingTime )
125 , estimatedRenderingTime( other.estimatedRenderingTime )
126 , errors( other.errors )
127 , layerId( other.layerId )
128 , maskPainter( nullptr )
129 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
130 , maskJobs( other.maskJobs )
132 mContext = std::move( other.mContext );
137 previewRenderImage = other.previewRenderImage;
138 other.previewRenderImage =
nullptr;
140 renderer = other.renderer;
141 other.renderer =
nullptr;
143 elevationMap = other.elevationMap;
144 other.elevationMap =
nullptr;
146 maskPaintDevice = std::move( other.maskPaintDevice );
148 firstPassJob = other.firstPassJob;
149 other.firstPassJob =
nullptr;
151 picture = std::move( other.picture );
154bool LayerRenderJob::imageCanBeComposed()
const
156 if ( imageInitialized )
160 return renderer->isReadyToCompose();
174 : mSettings( settings )
187 mErrors.append( QgsMapRendererJob::Error( QString(), tr(
"Invalid map settings" ) ) );
220 return mLabelingEngineFeedback;
225 QHash<QgsMapLayer *, int> result;
228 if (
auto &&lKey = it.key() )
229 result.insert( lKey, it.value() );
249 QSet< QgsMapLayer * > labeledLayers;
250 const QList<QgsMapLayer *> layers =
mSettings.layers();
251 for ( QgsMapLayer *ml : layers )
256 switch ( ml->type() )
260 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
270 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
280 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;
411 QgsDebugMsgLevel( QStringLiteral(
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
414 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
415 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
429 extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
449 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(),
500 image->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
501 image->setDotsPerMeterX( 1000 *
mSettings.outputDpi() / 25.4 );
502 image->setDotsPerMeterY( 1000 *
mSettings.outputDpi() / 25.4 );
503 if ( image->isNull() )
505 mErrors.append(
Error( layerId, tr(
"Insufficient memory for image %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
512QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
514 auto elevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
515 if ( !elevationMap->isValid() )
517 mErrors.append(
Error( layerId, tr(
"Insufficient memory for elevation map %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
520 return elevationMap.release();
523QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
525 QPainter *painter =
nullptr;
526 image = allocateImage( layerId );
529 painter =
new QPainter( image );
535QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
537 auto picture = std::make_unique<QPicture>();
538 QPainter *painter =
new QPainter( picture.get() );
540 return { std::move( picture ), painter };
545 std::vector< LayerRenderJob > layerJobs;
548 QListIterator<QgsMapLayer *> li(
mSettings.layers() );
554 Q_UNUSED( cacheValid )
555 QgsDebugMsgLevel( QStringLiteral(
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
560 while ( li.hasPrevious() )
562 QgsMapLayer *ml = li.previous();
564 QgsDebugMsgLevel( QStringLiteral(
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
576 mErrors.append(
Error( ml->
id(), QString(
"Layer %1 is invalid, skipped" ).arg( ml->
name() ) ) );
582 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
588 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's time range" ), 3 );
594 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's z range" ), 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( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool();
636 if ( !ml->
customProperty( QStringLiteral(
"_noset_layer_expression_context" ) ).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();
682 if ( canUseCache &&
mCache->hasCacheImage( ml->
id() ) )
685 job.imageInitialized =
true;
686 job.img =
new QImage(
mCache->cacheImage( ml->
id() ) );
692 job.img->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
693 job.renderer =
nullptr;
694 job.context()->setPainter(
nullptr );
699 QElapsedTimer layerTime;
704 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
705 job.context()->setFeedback( job.renderer->feedback() );
716 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
719 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
723 job.renderer =
nullptr;
724 layerJobs.pop_back();
733 job.elevationMap = allocateElevationMap( ml->
id() );
734 job.context()->setElevationMap( job.elevationMap );
741 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + QStringLiteral(
"_preview" ),
mSettings.mapToPixel() );
742 if ( !cachedImage.isNull() )
744 job.previewRenderImage =
new QImage( cachedImage );
745 job.previewRenderImageInitialized =
true;
746 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage ) );
747 job.context()->setPainterFlagsUsingContext( painter );
750 if ( !job.previewRenderImage )
752 job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
753 job.previewRenderImageInitialized =
false;
756 if ( !job.previewRenderImage )
758 delete job.context()->previewRenderPainter();
759 job.context()->setPreviewRenderPainter(
nullptr );
763 job.renderingTime = layerTime.elapsed();
771 std::vector< LayerRenderJob > secondPassJobs;
774 QHash<QString, LayerRenderJob *> layerJobMapping;
777 QMap<QString, bool> maskLayerHasEffects;
778 QMap<int, bool> labelHasEffects;
786 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ ):
787 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
792 QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
799 for ( LayerRenderJob &job : firstPassJobs )
801 layerJobMapping[job.layerId] = &job;
806 for ( LayerRenderJob &job : firstPassJobs )
808 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
813 auto collectMasks = [&](
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(),
int labelMaskId = -1 )
815 bool hasEffects =
false;
816 for (
auto it = masks->begin(); it != masks->end(); ++it )
818 auto lit = maskedSymbolLayers.find( it.key() );
819 if ( lit == maskedSymbolLayers.end() )
821 maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
825 if ( lit->first != it.value().symbolLayerIds )
827 QgsLogger::warning( QStringLiteral(
"Layer %1 : Different sets of symbol layers are masked by different sources ! Only one (arbitrary) set will be retained !" ).arg( it.key() ) );
830 lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
832 hasEffects |= it.value().hasEffects;
834 if ( ! masks->isEmpty() && labelMaskId == -1 )
835 maskLayerHasEffects[ sourceLayerId ] = hasEffects;
840 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
842 QString labelRule = it.key();
848 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
850 const QString sourceLayerId = mit.key();
852 if ( !layerJobMapping.contains( sourceLayerId ) )
855 usableMasks.insert( sourceLayerId, mit.value() );
858 if ( usableMasks.empty() )
862 QSet<QgsSymbolLayerReference> slRefs;
863 bool hasEffects =
false;
864 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
866 const QString sourceLayerId = mit.key();
868 if ( !layerJobMapping.contains( sourceLayerId ) )
871 for (
const QString &symbolLayerId : mit.value().symbolLayerIds )
872 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
874 hasEffects |= mit.value().hasEffects;
877 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
878 labelHasEffects[ labelMaskId ] = hasEffects;
881 collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
886 collectMasks( &symbolLayerMasks, vl->
id() );
889 if ( maskedSymbolLayers.isEmpty() )
890 return secondPassJobs;
893 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
895 QPaintDevice *maskPaintDevice =
nullptr;
896 QPainter *maskPainter =
nullptr;
897 if ( forceVector && !labelHasEffects[ maskId ] )
900 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
901 geomPaintDevice->setStrokedPathSegments( 4 );
902 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
903 maskPaintDevice = geomPaintDevice.release();
904 maskPainter =
new QPainter( maskPaintDevice );
909 QImage *maskImage =
nullptr;
910 maskPainter = allocateImageAndPainter( QStringLiteral(
"label mask" ), maskImage, &labelJob.context );
911 maskImage->fill( 0 );
912 maskPaintDevice = maskImage;
915 labelJob.context.setMaskPainter( maskPainter, maskId );
916 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
917 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
919 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
934 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
935 if ( !labelJob.img && canUseImage )
937 labelJob.img = allocateImage( QStringLiteral(
"labels" ) );
939 else if ( !labelJob.picture && !canUseImage )
941 labelJob.picture = std::make_unique<QPicture>( );
945 for ( LayerRenderJob &job : firstPassJobs )
947 job.maskRequiresLayerRasterization =
false;
949 auto it = maskedSymbolLayers.find( job.layerId );
950 if ( it != maskedSymbolLayers.end() )
952 const QList<MaskSource> &sourceList = it->second;
953 for (
const MaskSource &source : sourceList )
955 job.maskRequiresLayerRasterization |= source.hasEffects;
960 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
961 if ( isRasterRendering && !job.img )
963 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
965 else if ( !isRasterRendering && !job.picture )
967 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
968 job.picture = std::move( pictureAndPainter.first );
969 if ( job.context()->painter()->hasClipping() )
972 pictureAndPainter.second->setClipping(
true );
973 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
975 job.context()->setPainter( pictureAndPainter.second );
978 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
982 if ( maskLayerHasEffects.contains( job.layerId ) )
984 QPaintDevice *maskPaintDevice =
nullptr;
985 QPainter *maskPainter =
nullptr;
986 if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
989 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
990 geomPaintDevice->setStrokedPathSegments( 4 );
991 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
992 maskPaintDevice = geomPaintDevice.release();
993 maskPainter =
new QPainter( maskPaintDevice );
998 QImage *maskImage =
nullptr;
999 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
1000 maskImage->fill( 0 );
1001 maskPaintDevice = maskImage;
1004 job.context()->setMaskPainter( maskPainter );
1005 job.maskPainter.reset( maskPainter );
1006 job.maskPaintDevice.reset( maskPaintDevice );
1010 for ( LayerRenderJob &job : firstPassJobs )
1012 QgsMapLayer *ml = job.layer;
1014 auto it = maskedSymbolLayers.find( job.layerId );
1015 if ( it == maskedSymbolLayers.end() )
1018 QList<MaskSource> &sourceList = it->second;
1019 const QSet<QString> symbolList = it->first;
1021 secondPassJobs.emplace_back( LayerRenderJob() );
1022 LayerRenderJob &job2 = secondPassJobs.back();
1024 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1027 for ( MaskSource &source : sourceList )
1029 if ( source.labelMaskId != -1 )
1030 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
1032 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
1036 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1038 job2.layer = job.layer;
1039 job2.renderAboveLabels = job.renderAboveLabels;
1040 job2.layerId = job.layerId;
1043 job2.firstPassJob = &job;
1045 if ( !forceVector || job2.maskRequiresLayerRasterization )
1047 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
1051 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
1052 if ( job.context()->painter()->hasClipping() )
1055 pictureAndPainter.second->setClipping(
true );
1056 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1058 job2.picture = std::move( pictureAndPainter.first );
1059 job2.context()->setPainter( pictureAndPainter.second );
1062 if ( ! job2.img && ! job2.picture )
1064 secondPassJobs.pop_back();
1070 QgsVectorLayerRenderer *mapRenderer =
static_cast<QgsVectorLayerRenderer *
>( ml->
createMapRenderer( *job2.context() ) );
1071 job2.renderer = mapRenderer;
1072 if ( job2.renderer )
1074 job2.context()->setFeedback( job2.renderer->feedback() );
1079 job2.context()->setDisabledSymbolLayersV2( symbolList );
1082 return secondPassJobs;
1087 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1091 if ( !res.contains( it ) )
1103 for ( LayerRenderJob &job : secondPassJobs )
1105 if ( job.maskRequiresLayerRasterization )
1111 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1113 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1115 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1116 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1118 QgsGeometry geometry( geometryDevice->geometry().clone() );
1120#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
1127 for (
const QString &symbolLayerId : layers )
1129 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1134 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1142 job.context.setPainter( painter );
1143 job.context.setLabelingEngine( labelingEngine2 );
1144 job.context.setFeedback( mLabelingEngineFeedback );
1145 if ( labelingEngine2 )
1146 job.context.labelingEngine()->prepare( job.context );
1148 QgsRectangle r1 =
mSettings.visibleExtent();
1150 job.context.setExtent( r1 );
1152 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1153 QgsCoordinateTransform ct;
1155 job.context.setCoordinateTransform( ct );
1166 job.complete =
true;
1168 Q_ASSERT( job.img->devicePixelRatio() ==
mSettings.devicePixelRatio() );
1169 job.context.setPainter(
nullptr );
1173 if ( canUseLabelCache && (
mCache || !painter ) )
1175 job.img = allocateImage( QStringLiteral(
"labels" ) );
1185 for ( LayerRenderJob &job : jobs )
1189 delete job.context()->painter();
1190 job.context()->setPainter(
nullptr );
1192 if (
mCache && !job.cached && job.completed && job.layer )
1194 QgsDebugMsgLevel( QStringLiteral(
"caching image for %1" ).arg( job.layerId ), 2 );
1195 mCache->setCacheImageWithParameters( job.layerId, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1196 mCache->setCacheImageWithParameters( job.layerId + QStringLiteral(
"_preview" ), *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1203 if ( job.previewRenderImage )
1205 delete job.context()->previewRenderPainter();
1206 job.context()->setPreviewRenderPainter(
nullptr );
1207 delete job.previewRenderImage;
1208 job.previewRenderImage =
nullptr;
1211 if ( job.elevationMap )
1213 job.context()->setElevationMap(
nullptr );
1214 if (
mCache && !job.cached && job.completed && job.layer )
1216 QgsDebugMsgLevel( QStringLiteral(
"caching elevation map for %1" ).arg( job.layerId ), 2 );
1217 mCache->setCacheImageWithParameters(
1219 job.elevationMap->rawElevationImage(),
1222 QList< QgsMapLayer * >() << job.layer );
1223 mCache->setCacheImageWithParameters(
1225 job.elevationMap->rawElevationImage(),
1228 QList< QgsMapLayer * >() << job.layer );
1231 delete job.elevationMap;
1232 job.elevationMap =
nullptr;
1237 delete job.context()->painter();
1238 job.context()->setPainter(
nullptr );
1239 job.picture.reset(
nullptr );
1244 const QStringList
errors = job.renderer->errors();
1245 for (
const QString &message :
errors )
1246 mErrors.append(
Error( job.renderer->layerId(), message ) );
1248 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1250 delete job.renderer;
1251 job.renderer =
nullptr;
1257 job.maskPainter.reset(
nullptr );
1258 job.maskPaintDevice.reset(
nullptr );
1266 for ( LayerRenderJob &job : jobs )
1270 delete job.context()->painter();
1271 job.context()->setPainter(
nullptr );
1277 if ( job.previewRenderImage )
1279 delete job.context()->previewRenderPainter();
1280 job.context()->setPreviewRenderPainter(
nullptr );
1281 delete job.previewRenderImage;
1282 job.previewRenderImage =
nullptr;
1287 delete job.context()->painter();
1288 job.context()->setPainter(
nullptr );
1293 delete job.renderer;
1294 job.renderer =
nullptr;
1308 if (
mCache && !job.cached && !job.context.renderingStopped() )
1319 job.picture.reset(
nullptr );
1320 job.maskPainters.clear();
1321 job.maskPaintDevices.clear();
1325#define DEBUG_RENDERING 0
1328 const std::vector<LayerRenderJob> &jobs,
1329 const LabelRenderJob &labelJob,
1335 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1336 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1340 std::unique_ptr<QgsElevationMap> mainElevationMap;
1341 if ( mapShadingRenderer.
isActive() )
1344 QPainter painter( &image );
1349 for (
const LayerRenderJob &job : jobs )
1351 if ( job.renderAboveLabels )
1358 painter.setCompositionMode( job.blendMode );
1359 painter.setOpacity( job.opacity );
1361 if ( mainElevationMap )
1364 if ( layerElevationMap.
isValid() )
1370 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1374 painter.drawImage( 0, 0, img );
1377 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1385 if ( labelJob.img && labelJob.complete )
1387 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1388 painter.setOpacity( 1.0 );
1389 painter.drawImage( 0, 0, *labelJob.img );
1391 else if ( labelJob.picture && labelJob.complete )
1393 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1394 painter.setOpacity( 1.0 );
1403 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1404 painter.setOpacity( 1.0 );
1405 painter.drawImage( 0, 0, labelCacheImage );
1409 for (
const LayerRenderJob &job : jobs )
1411 if ( !job.renderAboveLabels )
1418 painter.setCompositionMode( job.blendMode );
1419 painter.setOpacity( job.opacity );
1421 painter.drawImage( 0, 0, img );
1426 image.save(
"/tmp/final.png" );
1433 const LayerRenderJob &job,
1437 if ( job.imageCanBeComposed() )
1439 if ( job.previewRenderImage && !job.completed )
1440 return *job.previewRenderImage;
1442 Q_ASSERT( job.img );
1447 if ( cache && cache->
hasAnyCacheImage( job.layerId + QStringLiteral(
"_preview" ) ) )
1458 if ( job.imageCanBeComposed() && job.elevationMap )
1460 return *job.elevationMap;
1467 return QgsElevationMap();
1474 for ( LayerRenderJob &job : secondPassJobs )
1476 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1479 if ( isRasterRendering && job.maskJobs.size() > 1 )
1481 QPainter *maskPainter =
nullptr;
1482 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1484 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1487 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1491 maskPainter->drawImage( 0, 0, *maskImage );
1496 if ( ! job.maskJobs.isEmpty() )
1499 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1500 if ( isRasterRendering )
1502 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1505 QPainter *painter = job.context()->painter();
1507 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1512 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1513 QVector<QRgb> mswTable;
1514 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1515 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1516 maskBinAlpha.setColorTable( mswTable );
1517 painter->drawImage( 0, 0, maskBinAlpha );
1521 QPainter tempPainter;
1524 QPainter *painter1 = job.firstPassJob->context()->painter();
1527 tempPainter.begin( job.firstPassJob->img );
1528 painter1 = &tempPainter;
1532 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1533 painter1->drawImage( 0, 0, *maskImage );
1536 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1537 painter1->drawImage( 0, 0, *job.img );
1542 job.firstPassJob->picture = std::move( job.picture );
1543 job.picture =
nullptr;
1554 QMultiMap<int, QString> elapsed;
1555 for (
const LayerRenderJob &job : jobs )
1556 elapsed.insert( job.renderingTime, job.layerId );
1557 for (
const LayerRenderJob &job : secondPassJobs )
1558 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1560 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1562 QList<int> tt( elapsed.uniqueKeys() );
1563 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1564 for (
int t : std::as_const( tt ) )
1566 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String(
", " ) ) ), tr(
"Rendering" ) );
1575 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1578 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), QStringLiteral(
"rendering" ) );
1585 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1589 if ( labelingEngine2 )
1591 labelingEngine2->
run( renderContext );
1594 QgsDebugMsgLevel( QStringLiteral(
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
1599 Q_UNUSED( settings )
1601 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.
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 symbolLayerMasks(const QgsVectorLayer *)
Returns all masks that may be defined on symbol layers for a given vector layer.
static QHash< QString, QgsMaskedLayers > labelMasks(const QgsVectorLayer *)
Returns masks defined in labeling options of a layer.
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