51#include <QElapsedTimer>
56#include <QtConcurrentMap>
58#include "moc_qgsmaprendererjob.cpp"
60using namespace Qt::StringLiterals;
71LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
76 mContext = std::move( other.mContext );
81 renderer = other.renderer;
82 other.renderer =
nullptr;
84 previewRenderImage = other.previewRenderImage;
85 other.previewRenderImage =
nullptr;
87 imageInitialized = other.imageInitialized;
88 previewRenderImageInitialized = other.previewRenderImageInitialized;
90 blendMode = other.blendMode;
91 opacity = other.opacity;
92 cached = other.cached;
94 renderAboveLabels = other.renderAboveLabels;
95 completed = other.completed;
96 renderingTime = other.renderingTime;
97 estimatedRenderingTime = other.estimatedRenderingTime ;
98 errors = other.errors;
99 layerId = other.layerId;
101 maskPaintDevice = std::move( other.maskPaintDevice );
103 firstPassJob = other.firstPassJob;
104 other.firstPassJob =
nullptr;
106 picture = std::move( other.picture );
108 maskJobs = other.maskJobs;
110 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
112 elevationMap = other.elevationMap;
113 maskPainter = std::move( other.maskPainter );
118LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
119 : imageInitialized( other.imageInitialized )
120 , previewRenderImageInitialized( other.previewRenderImageInitialized )
121 , blendMode( other.blendMode )
122 , opacity( other.opacity )
123 , cached( other.cached )
124 , renderAboveLabels( other.renderAboveLabels )
125 , layer( other.layer )
126 , completed( other.completed )
127 , renderingTime( other.renderingTime )
128 , estimatedRenderingTime( other.estimatedRenderingTime )
129 , errors( other.errors )
130 , layerId( other.layerId )
131 , maskPainter( nullptr )
132 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
133 , maskJobs( other.maskJobs )
135 mContext = std::move( other.mContext );
140 previewRenderImage = other.previewRenderImage;
141 other.previewRenderImage =
nullptr;
143 renderer = other.renderer;
144 other.renderer =
nullptr;
146 elevationMap = other.elevationMap;
147 other.elevationMap =
nullptr;
149 maskPaintDevice = std::move( other.maskPaintDevice );
151 firstPassJob = other.firstPassJob;
152 other.firstPassJob =
nullptr;
154 picture = std::move( other.picture );
157bool LayerRenderJob::imageCanBeComposed()
const
159 if ( imageInitialized )
163 return renderer->isReadyToCompose();
177 : mSettings( settings )
190 mErrors.append( QgsMapRendererJob::Error( QString(), tr(
"Invalid map settings" ) ) );
223 return mLabelingEngineFeedback;
228 QHash<QgsMapLayer *, int> result;
231 if (
auto &&lKey = it.key() )
232 result.insert( lKey, it.value() );
252 QSet< QgsMapLayer * > labeledLayers;
253 const QList<QgsMapLayer *> layers =
mSettings.layers();
254 for ( QgsMapLayer *ml : layers )
259 switch ( ml->type() )
263 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
273 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
283 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
316 bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
328 const QList<QgsMapLayer *> layers =
mSettings.layers();
329 for ( QgsMapLayer *ml : layers )
334 switch ( ml->type() )
338 QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
348 QgsMeshLayer *l = qobject_cast< QgsMeshLayer *>( ml );
358 QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml );
389 QgsCoordinateTransform approxTransform = ct;
402 static const double SPLIT_COORD = 180.0;
417 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
418 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
432 extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 );
452 if ( ll.
x() > ur.
x() )
481 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
488 catch ( QgsCsException &e )
491 extent = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
492 r2 = QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
499QImage *QgsMapRendererJob::allocateImage( QString layerId )
501 QImage *image =
new QImage(
mSettings.deviceOutputSize(),
503 image->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
504 image->setDotsPerMeterX( 1000 *
mSettings.outputDpi() / 25.4 );
505 image->setDotsPerMeterY( 1000 *
mSettings.outputDpi() / 25.4 );
506 if ( image->isNull() )
508 mErrors.append(
Error( layerId, tr(
"Insufficient memory for image %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
515QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
517 auto elevationMap = std::make_unique<QgsElevationMap>(
mSettings.deviceOutputSize(),
mSettings.devicePixelRatio() );
518 if ( !elevationMap->isValid() )
520 mErrors.append(
Error( layerId, tr(
"Insufficient memory for elevation map %1x%2" ).arg(
mSettings.outputSize().width() ).arg(
mSettings.outputSize().height() ) ) );
523 return elevationMap.release();
526QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
528 QPainter *painter =
nullptr;
529 image = allocateImage( layerId );
532 painter =
new QPainter( image );
538QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
540 auto picture = std::make_unique<QPicture>();
541 QPainter *painter =
new QPainter( picture.get() );
543 return { std::move( picture ), painter };
548 std::vector< LayerRenderJob > layerJobs;
551 QListIterator<QgsMapLayer *> li(
mSettings.layers() );
557 Q_UNUSED( cacheValid )
563 while ( li.hasPrevious() )
565 QgsMapLayer *ml = li.previous();
567 QgsDebugMsgLevel( u
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6"_s
579 mErrors.append(
Error( ml->
id(), QString(
"Layer %1 is invalid, skipped" ).arg( ml->
name() ) ) );
585 QgsDebugMsgLevel( u
"Layer not rendered because it is not within the defined visibility scale range"_s, 3 );
591 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's time range"_s, 3 );
597 QgsDebugMsgLevel( u
"Layer not rendered because it is not visible within the map's z range"_s, 3 );
601 QgsRectangle r1 =
mSettings.visibleExtent(), r2;
603 QgsCoordinateTransform ct;
606 bool haveExtentInLayerCrs =
true;
609 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
614 mErrors.append(
Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
618 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
625 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
627 mCache->clearCacheImage( ml->
id() );
631 layerJobs.emplace_back( LayerRenderJob() );
632 LayerRenderJob &job = layerJobs.back();
634 job.layerId = ml->
id();
635 job.renderAboveLabels = ml->
customProperty( u
"rendering/renderAboveLabels"_s ).toBool();
639 if ( !ml->
customProperty( u
"_noset_layer_expression_context"_s ).toBool() )
641 job.context()->setPainter( painter );
642 job.context()->setLabelingEngine( labelingEngine2 );
643 job.context()->setLabelSink(
labelSink() );
644 job.context()->setCoordinateTransform( ct );
645 job.context()->setExtent( r1 );
651 if ( mFeatureFilterProvider )
653 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
656 QgsMapLayerStyleOverride styleOverride( ml );
657 if (
mSettings.layerStyleOverrides().contains( ml->
id() ) )
658 styleOverride.setOverrideStyle(
mSettings.layerStyleOverrides().value( ml->
id() ) );
665 QgsRasterLayer *rl = qobject_cast< QgsRasterLayer * >( ml );
680 const QgsElevationShadingRenderer shadingRenderer =
mSettings.elevationShadingRenderer();
685 if ( canUseCache &&
mCache->hasCacheImage( ml->
id() ) )
688 job.imageInitialized =
true;
689 job.img =
new QImage(
mCache->cacheImage( ml->
id() ) );
695 job.img->setDevicePixelRatio(
static_cast<qreal
>(
mSettings.devicePixelRatio() ) );
696 job.renderer =
nullptr;
697 job.context()->setPainter(
nullptr );
702 QElapsedTimer layerTime;
707 job.renderer->setLayerRenderingTimeHint( job.estimatedRenderingTime );
708 job.context()->setFeedback( job.renderer->feedback() );
719 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
722 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
726 job.renderer =
nullptr;
727 layerJobs.pop_back();
736 job.elevationMap = allocateElevationMap( ml->
id() );
737 job.context()->setElevationMap( job.elevationMap );
744 const QImage cachedImage =
mCache->transformedCacheImage( job.layerId + u
"_preview"_s,
mSettings.mapToPixel() );
745 if ( !cachedImage.isNull() )
747 job.previewRenderImage =
new QImage( cachedImage );
748 job.previewRenderImageInitialized =
true;
749 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage ) );
750 job.context()->setPainterFlagsUsingContext( painter );
753 if ( !job.previewRenderImage )
755 job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
756 job.previewRenderImageInitialized =
false;
759 if ( !job.previewRenderImage )
761 delete job.context()->previewRenderPainter();
762 job.context()->setPreviewRenderPainter(
nullptr );
766 job.renderingTime = layerTime.elapsed();
774 std::vector< LayerRenderJob > secondPassJobs;
777 QHash<QString, LayerRenderJob *> layerJobMapping;
780 QMap<QString, bool> maskLayerHasEffects;
781 QMap<int, bool> labelHasEffects;
789 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ ):
790 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
795 QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
802 for ( LayerRenderJob &job : firstPassJobs )
804 layerJobMapping[job.layerId] = &job;
809 for ( LayerRenderJob &job : firstPassJobs )
811 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
816 auto collectMasks = [&](
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(),
int labelMaskId = -1 )
818 bool hasEffects =
false;
819 for (
auto it = masks->begin(); it != masks->end(); ++it )
821 auto lit = maskedSymbolLayers.find( it.key() );
822 if ( lit == maskedSymbolLayers.end() )
824 maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
828 if ( lit->first != it.value().symbolLayerIds )
830 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() ) );
833 lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
835 hasEffects |= it.value().hasEffects;
837 if ( ! masks->isEmpty() && labelMaskId == -1 )
838 maskLayerHasEffects[ sourceLayerId ] = hasEffects;
843 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
845 QString labelRule = it.key();
851 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
853 const QString sourceLayerId = mit.key();
855 if ( !layerJobMapping.contains( sourceLayerId ) )
858 usableMasks.insert( sourceLayerId, mit.value() );
861 if ( usableMasks.empty() )
865 QSet<QgsSymbolLayerReference> slRefs;
866 bool hasEffects =
false;
867 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
869 const QString sourceLayerId = mit.key();
871 if ( !layerJobMapping.contains( sourceLayerId ) )
874 for (
const QString &symbolLayerId : mit.value().symbolLayerIds )
875 slRefs.insert( QgsSymbolLayerReference( sourceLayerId, symbolLayerId ) );
877 hasEffects |= mit.value().hasEffects;
880 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
881 labelHasEffects[ labelMaskId ] = hasEffects;
884 collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
889 collectMasks( &symbolLayerMasks, vl->
id() );
892 if ( maskedSymbolLayers.isEmpty() )
893 return secondPassJobs;
896 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
898 QPaintDevice *maskPaintDevice =
nullptr;
899 QPainter *maskPainter =
nullptr;
900 if ( forceVector && !labelHasEffects[ maskId ] )
903 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
904 geomPaintDevice->setStrokedPathSegments( 4 );
905 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
906 maskPaintDevice = geomPaintDevice.release();
907 maskPainter =
new QPainter( maskPaintDevice );
912 QImage *maskImage =
nullptr;
913 maskPainter = allocateImageAndPainter( u
"label mask"_s, maskImage, &labelJob.context );
916 maskImage->fill( 0 );
917 maskPaintDevice = maskImage;
920 labelJob.context.setMaskPainter( maskPainter, maskId );
921 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
922 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
924 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
939 const bool canUseImage = !forceVector && !hasNonDefaultComposition;
940 if ( !labelJob.img && canUseImage )
942 labelJob.img = allocateImage( u
"labels"_s );
944 else if ( !labelJob.picture && !canUseImage )
946 labelJob.picture = std::make_unique<QPicture>( );
950 for ( LayerRenderJob &job : firstPassJobs )
952 job.maskRequiresLayerRasterization =
false;
954 auto it = maskedSymbolLayers.find( job.layerId );
955 if ( it != maskedSymbolLayers.end() )
957 const QList<MaskSource> &sourceList = it->second;
958 for (
const MaskSource &source : sourceList )
960 job.maskRequiresLayerRasterization |= source.hasEffects;
965 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
966 if ( isRasterRendering && !job.img )
968 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
970 else if ( !isRasterRendering && !job.picture )
972 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
973 job.picture = std::move( pictureAndPainter.first );
974 if ( job.context()->painter()->hasClipping() )
977 pictureAndPainter.second->setClipping(
true );
978 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
980 job.context()->setPainter( pictureAndPainter.second );
983 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
987 if ( maskLayerHasEffects.contains( job.layerId ) )
989 QPaintDevice *maskPaintDevice =
nullptr;
990 QPainter *maskPainter =
nullptr;
991 if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
994 auto geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
995 geomPaintDevice->setStrokedPathSegments( 4 );
996 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
997 maskPaintDevice = geomPaintDevice.release();
998 maskPainter =
new QPainter( maskPaintDevice );
1003 QImage *maskImage =
nullptr;
1004 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
1005 maskImage->fill( 0 );
1006 maskPaintDevice = maskImage;
1009 job.context()->setMaskPainter( maskPainter );
1010 job.maskPainter.reset( maskPainter );
1011 job.maskPaintDevice.reset( maskPaintDevice );
1015 for ( LayerRenderJob &job : firstPassJobs )
1017 QgsMapLayer *ml = job.layer;
1019 auto it = maskedSymbolLayers.find( job.layerId );
1020 if ( it == maskedSymbolLayers.end() )
1023 QList<MaskSource> &sourceList = it->second;
1024 const QSet<QString> symbolList = it->first;
1026 secondPassJobs.emplace_back( LayerRenderJob() );
1027 LayerRenderJob &job2 = secondPassJobs.back();
1029 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
1032 for ( MaskSource &source : sourceList )
1034 if ( source.labelMaskId != -1 )
1035 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
1037 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
1041 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
1043 job2.layer = job.layer;
1044 job2.renderAboveLabels = job.renderAboveLabels;
1045 job2.layerId = job.layerId;
1048 job2.firstPassJob = &job;
1050 if ( !forceVector || job2.maskRequiresLayerRasterization )
1052 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
1056 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
1057 if ( job.context()->painter()->hasClipping() )
1060 pictureAndPainter.second->setClipping(
true );
1061 pictureAndPainter.second->setClipPath( job.context()->painter()->clipPath() );
1063 job2.picture = std::move( pictureAndPainter.first );
1064 job2.context()->setPainter( pictureAndPainter.second );
1067 if ( ! job2.img && ! job2.picture )
1069 secondPassJobs.pop_back();
1075 QgsVectorLayerRenderer *mapRenderer =
static_cast<QgsVectorLayerRenderer *
>( ml->
createMapRenderer( *job2.context() ) );
1076 job2.renderer = mapRenderer;
1077 if ( job2.renderer )
1079 job2.context()->setFeedback( job2.renderer->feedback() );
1084 job2.context()->setDisabledSymbolLayersV2( symbolList );
1087 return secondPassJobs;
1092 QList<QPointer<QgsMapLayer> > res = _qgis_listRawToQPointer( engine->
participatingLayers() );
1096 if ( !res.contains( it ) )
1108 for ( LayerRenderJob &job : secondPassJobs )
1110 if ( job.maskRequiresLayerRasterization )
1116 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1118 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1120 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1121 if ( QgsGeometryPaintDevice *geometryDevice =
dynamic_cast<QgsGeometryPaintDevice *
>( maskPainter->device() ) )
1123 QgsGeometry geometry( geometryDevice->geometry().clone() );
1125#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
1132 for (
const QString &symbolLayerId : layers )
1134 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1139 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1147 job.context.setPainter( painter );
1148 job.context.setLabelingEngine( labelingEngine2 );
1149 job.context.setFeedback( mLabelingEngineFeedback );
1150 if ( labelingEngine2 )
1151 job.context.labelingEngine()->prepare( job.context );
1153 QgsRectangle r1 =
mSettings.visibleExtent();
1155 job.context.setExtent( r1 );
1157 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1158 QgsCoordinateTransform ct;
1160 job.context.setCoordinateTransform( ct );
1171 job.complete =
true;
1173 Q_ASSERT( job.img->devicePixelRatio() ==
mSettings.devicePixelRatio() );
1174 job.context.setPainter(
nullptr );
1178 if ( canUseLabelCache && (
mCache || !painter ) )
1180 job.img = allocateImage( u
"labels"_s );
1190 for ( LayerRenderJob &job : jobs )
1194 delete job.context()->painter();
1195 job.context()->setPainter(
nullptr );
1197 if (
mCache && !job.cached && job.completed && job.layer )
1200 mCache->setCacheImageWithParameters( job.layerId, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1201 mCache->setCacheImageWithParameters( job.layerId + u
"_preview"_s, *job.img,
mSettings.visibleExtent(),
mSettings.mapToPixel(), QList< QgsMapLayer * >() << job.layer );
1208 if ( job.previewRenderImage )
1210 delete job.context()->previewRenderPainter();
1211 job.context()->setPreviewRenderPainter(
nullptr );
1212 delete job.previewRenderImage;
1213 job.previewRenderImage =
nullptr;
1216 if ( job.elevationMap )
1218 job.context()->setElevationMap(
nullptr );
1219 if (
mCache && !job.cached && job.completed && job.layer )
1221 QgsDebugMsgLevel( u
"caching elevation map for %1"_s.arg( job.layerId ), 2 );
1222 mCache->setCacheImageWithParameters(
1224 job.elevationMap->rawElevationImage(),
1227 QList< QgsMapLayer * >() << job.layer );
1228 mCache->setCacheImageWithParameters(
1230 job.elevationMap->rawElevationImage(),
1233 QList< QgsMapLayer * >() << job.layer );
1236 delete job.elevationMap;
1237 job.elevationMap =
nullptr;
1242 delete job.context()->painter();
1243 job.context()->setPainter(
nullptr );
1244 job.picture.reset(
nullptr );
1249 const QStringList
errors = job.renderer->errors();
1250 for (
const QString &message :
errors )
1251 mErrors.append(
Error( job.renderer->layerId(), message ) );
1253 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1255 delete job.renderer;
1256 job.renderer =
nullptr;
1262 job.maskPainter.reset(
nullptr );
1263 job.maskPaintDevice.reset(
nullptr );
1271 for ( LayerRenderJob &job : jobs )
1275 delete job.context()->painter();
1276 job.context()->setPainter(
nullptr );
1282 if ( job.previewRenderImage )
1284 delete job.context()->previewRenderPainter();
1285 job.context()->setPreviewRenderPainter(
nullptr );
1286 delete job.previewRenderImage;
1287 job.previewRenderImage =
nullptr;
1292 delete job.context()->painter();
1293 job.context()->setPainter(
nullptr );
1298 delete job.renderer;
1299 job.renderer =
nullptr;
1313 if (
mCache && !job.cached && !job.context.renderingStopped() )
1324 job.picture.reset(
nullptr );
1325 job.maskPainters.clear();
1326 job.maskPaintDevices.clear();
1330#define DEBUG_RENDERING 0
1333 const std::vector<LayerRenderJob> &jobs,
1334 const LabelRenderJob &labelJob,
1340 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1341 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1345 std::unique_ptr<QgsElevationMap> mainElevationMap;
1346 if ( mapShadingRenderer.
isActive() )
1349 QPainter painter( &image );
1354 for (
const LayerRenderJob &job : jobs )
1356 if ( job.renderAboveLabels )
1363 painter.setCompositionMode( job.blendMode );
1364 painter.setOpacity( job.opacity );
1366 if ( mainElevationMap )
1369 if ( layerElevationMap.
isValid() )
1375 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1379 painter.drawImage( 0, 0, img );
1382 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1390 if ( labelJob.img && labelJob.complete )
1392 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1393 painter.setOpacity( 1.0 );
1394 painter.drawImage( 0, 0, *labelJob.img );
1396 else if ( labelJob.picture && labelJob.complete )
1398 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1399 painter.setOpacity( 1.0 );
1408 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1409 painter.setOpacity( 1.0 );
1410 painter.drawImage( 0, 0, labelCacheImage );
1414 for (
const LayerRenderJob &job : jobs )
1416 if ( !job.renderAboveLabels )
1423 painter.setCompositionMode( job.blendMode );
1424 painter.setOpacity( job.opacity );
1426 painter.drawImage( 0, 0, img );
1431 image.save(
"/tmp/final.png" );
1438 const LayerRenderJob &job,
1442 if ( job.imageCanBeComposed() )
1444 if ( job.previewRenderImage && !job.completed )
1445 return *job.previewRenderImage;
1447 Q_ASSERT( job.img );
1463 if ( job.imageCanBeComposed() && job.elevationMap )
1465 return *job.elevationMap;
1472 return QgsElevationMap();
1479 for ( LayerRenderJob &job : secondPassJobs )
1481 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1484 if ( isRasterRendering && job.maskJobs.size() > 1 )
1486 QPainter *maskPainter =
nullptr;
1487 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1489 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1492 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1496 maskPainter->drawImage( 0, 0, *maskImage );
1501 if ( ! job.maskJobs.isEmpty() )
1504 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1505 if ( isRasterRendering )
1507 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1510 QPainter *painter = job.context()->painter();
1512 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1517 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1518 QVector<QRgb> mswTable;
1519 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1520 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1521 maskBinAlpha.setColorTable( mswTable );
1522 painter->drawImage( 0, 0, maskBinAlpha );
1526 QPainter tempPainter;
1529 QPainter *painter1 = job.firstPassJob->context()->painter();
1532 tempPainter.begin( job.firstPassJob->img );
1533 painter1 = &tempPainter;
1537 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1538 painter1->drawImage( 0, 0, *maskImage );
1541 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1542 painter1->drawImage( 0, 0, *job.img );
1547 job.firstPassJob->picture = std::move( job.picture );
1548 job.picture =
nullptr;
1559 QMultiMap<int, QString> elapsed;
1560 for (
const LayerRenderJob &job : jobs )
1561 elapsed.insert( job.renderingTime, job.layerId );
1562 for (
const LayerRenderJob &job : secondPassJobs )
1563 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1565 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1567 QList<int> tt( elapsed.uniqueKeys() );
1568 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1569 for (
int t : std::as_const( tt ) )
1571 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join(
", "_L1 ) ), tr(
"Rendering" ) );
1580 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1583 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), u
"rendering"_s );
1590 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1594 if ( labelingEngine2 )
1596 labelingEngine2->
run( renderContext );
1599 QgsDebugMsgLevel( u
"Draw labeling took (seconds): %1"_s.arg( t.elapsed() / 1000. ), 2 );
1604 Q_UNUSED( settings )
1606 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