19#include <QElapsedTimer>
21#include <QtConcurrentMap>
63LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
65 mContext = std::move( other.mContext );
70 renderer = other.renderer;
71 other.renderer =
nullptr;
73 previewRenderImage = other.previewRenderImage;
74 other.previewRenderImage =
nullptr;
76 imageInitialized = other.imageInitialized;
77 previewRenderImageInitialized = other.previewRenderImageInitialized;
79 blendMode = other.blendMode;
80 opacity = other.opacity;
81 cached = other.cached;
83 renderAboveLabels = other.renderAboveLabels;
84 completed = other.completed;
85 renderingTime = other.renderingTime;
86 estimatedRenderingTime = other.estimatedRenderingTime ;
87 errors = other.errors;
88 layerId = other.layerId;
90 maskPaintDevice = std::move( other.maskPaintDevice );
92 firstPassJob = other.firstPassJob;
93 other.firstPassJob =
nullptr;
95 picture = std::move( other.picture );
97 maskJobs = other.maskJobs;
99 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
101 elevationMap = other.elevationMap;
102 maskPainter = std::move( other.maskPainter );
107LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
108 : imageInitialized( other.imageInitialized )
109 , previewRenderImageInitialized( other.previewRenderImageInitialized )
110 , blendMode( other.blendMode )
111 , opacity( other.opacity )
112 , cached( other.cached )
113 , renderAboveLabels( other.renderAboveLabels )
114 , layer( other.layer )
115 , completed( other.completed )
116 , renderingTime( other.renderingTime )
117 , estimatedRenderingTime( other.estimatedRenderingTime )
118 , errors( other.errors )
119 , layerId( other.layerId )
120 , maskPainter( nullptr )
121 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
122 , maskJobs( other.maskJobs )
124 mContext = std::move( other.mContext );
129 previewRenderImage = other.previewRenderImage;
130 other.previewRenderImage =
nullptr;
132 renderer = other.renderer;
133 other.renderer =
nullptr;
135 elevationMap = other.elevationMap;
136 other.elevationMap =
nullptr;
138 maskPaintDevice = std::move( other.maskPaintDevice );
140 firstPassJob = other.firstPassJob;
141 other.firstPassJob =
nullptr;
143 picture = std::move( other.picture );
146bool LayerRenderJob::imageCanBeComposed()
const
148 if ( imageInitialized )
152 return renderer->isReadyToCompose();
166 : mSettings( settings )
212 return mLabelingEngineFeedback;
217 QHash<QgsMapLayer *, int> result;
220 if (
auto &&lKey = it.key() )
221 result.insert( lKey, it.value() );
241 QSet< QgsMapLayer * > labeledLayers;
248 switch ( ml->type() )
296 bool canUseCache = canCache && QSet< QgsMapLayer * >( labelDependentLayers.begin(), labelDependentLayers.end() ) == labeledLayers;
325 static const double SPLIT_COORD = 180.0;
337 QgsDebugMsgLevel( QStringLiteral(
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
340 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
341 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
375 if ( ll.
x() > ur.
x() )
404 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
414 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
415 r2 =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
422QImage *QgsMapRendererJob::allocateImage( QString layerId )
429 if ( image->isNull() )
438QgsElevationMap *QgsMapRendererJob::allocateElevationMap( QString layerId )
441 if ( !elevationMap->isValid() )
446 return elevationMap.release();
449QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
451 QPainter *painter =
nullptr;
452 image = allocateImage( layerId );
455 painter =
new QPainter( image );
461QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
463 std::unique_ptr<QPicture> picture = std::make_unique<QPicture>();
464 QPainter *painter =
new QPainter( picture.get() );
466 return { std::move( picture ), painter };
471 std::vector< LayerRenderJob > layerJobs;
480 Q_UNUSED( cacheValid )
481 QgsDebugMsgLevel( QStringLiteral(
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
486 while ( li.hasPrevious() )
490 QgsDebugMsgLevel( QStringLiteral(
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
507 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
513 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's time range" ), 3 );
519 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's z range" ), 3 );
528 bool haveExtentInLayerCrs =
true;
531 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
536 mErrors.append( Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
547 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
553 layerJobs.emplace_back( LayerRenderJob() );
554 LayerRenderJob &job = layerJobs.back();
556 job.layerId = ml->
id();
557 job.renderAboveLabels = ml->
customProperty( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool();
561 if ( !ml->
customProperty( QStringLiteral(
"_noset_layer_expression_context" ) ).toBool() )
563 job.context()->setPainter( painter );
564 job.context()->setLabelingEngine( labelingEngine2 );
565 job.context()->setLabelSink(
labelSink() );
566 job.context()->setCoordinateTransform( ct );
567 job.context()->setExtent( r1 );
573 if ( mFeatureFilterProvider )
574 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
607 job.imageInitialized =
true;
615 job.renderer =
nullptr;
616 job.context()->setPainter(
nullptr );
621 QElapsedTimer layerTime;
627 job.context()->setFeedback( job.renderer->feedback() );
633 if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
636 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
640 job.renderer =
nullptr;
641 layerJobs.pop_back();
650 job.elevationMap = allocateElevationMap( ml->
id() );
651 job.context()->setElevationMap( job.elevationMap );
659 if ( !cachedImage.isNull() )
661 job.previewRenderImage =
new QImage( cachedImage );
662 job.previewRenderImageInitialized =
true;
663 job.context()->setPreviewRenderPainter(
new QPainter( job.previewRenderImage ) );
664 job.context()->setPainterFlagsUsingContext( painter );
667 if ( !job.previewRenderImage )
669 job.context()->setPreviewRenderPainter( allocateImageAndPainter( ml->
id(), job.previewRenderImage, job.context() ) );
670 job.previewRenderImageInitialized =
false;
673 if ( !job.previewRenderImage )
675 delete job.context()->previewRenderPainter();
676 job.context()->setPreviewRenderPainter(
nullptr );
680 job.renderingTime = layerTime.elapsed();
688 std::vector< LayerRenderJob > secondPassJobs;
691 QHash<QString, LayerRenderJob *> layerJobMapping;
694 QMap<QString, bool> maskLayerHasEffects;
695 QMap<int, bool> labelHasEffects;
703 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ ):
704 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
709 QHash<QString, QPair<QSet<QString>, QList<MaskSource>>> maskedSymbolLayers;
716 for ( LayerRenderJob &job : firstPassJobs )
718 layerJobMapping[job.layerId] = &job;
723 for ( LayerRenderJob &job : firstPassJobs )
730 auto collectMasks = [&](
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(),
int labelMaskId = -1 )
732 bool hasEffects =
false;
733 for (
auto it = masks->begin(); it != masks->end(); ++it )
735 auto lit = maskedSymbolLayers.find( it.key() );
736 if ( lit == maskedSymbolLayers.end() )
738 maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
742 if ( lit->first != it.value().symbolLayerIds )
744 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() ) );
747 lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
749 hasEffects |= it.value().hasEffects;
751 if ( ! masks->isEmpty() && labelMaskId == -1 )
752 maskLayerHasEffects[ sourceLayerId ] = hasEffects;
757 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
759 QString labelRule = it.key();
765 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
767 const QString sourceLayerId = mit.key();
769 if ( !layerJobMapping.contains( sourceLayerId ) )
772 usableMasks.insert( sourceLayerId, mit.value() );
775 if ( usableMasks.empty() )
779 QSet<QgsSymbolLayerReference> slRefs;
780 bool hasEffects =
false;
781 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
783 const QString sourceLayerId = mit.key();
785 if ( !layerJobMapping.contains( sourceLayerId ) )
788 for (
const QString &symbolLayerId : mit.value().symbolLayerIds )
791 hasEffects |= mit.value().hasEffects;
794 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
795 labelHasEffects[ labelMaskId ] = hasEffects;
798 collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
803 collectMasks( &symbolLayerMasks, vl->
id() );
806 if ( maskedSymbolLayers.isEmpty() )
807 return secondPassJobs;
810 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
812 QPaintDevice *maskPaintDevice =
nullptr;
813 QPainter *maskPainter =
nullptr;
814 if ( forceVector && !labelHasEffects[ maskId ] )
817 std::unique_ptr< QgsGeometryPaintDevice > geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >(
true );
818 geomPaintDevice->setStrokedPathSegments( 4 );
819 geomPaintDevice->setSimplificationTolerance( labelJob.context.maskSettings().simplifyTolerance() );
820 maskPaintDevice = geomPaintDevice.release();
821 maskPainter =
new QPainter( maskPaintDevice );
826 QImage *maskImage =
nullptr;
827 maskPainter = allocateImageAndPainter( QStringLiteral(
"label mask" ), maskImage, &labelJob.context );
828 maskImage->fill( 0 );
829 maskPaintDevice = maskImage;
832 labelJob.context.setMaskPainter( maskPainter, maskId );
833 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
834 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
836 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
847 if ( !labelJob.img && !forceVector )
849 labelJob.img = allocateImage( QStringLiteral(
"labels" ) );
851 else if ( !labelJob.picture && forceVector )
853 labelJob.picture.reset(
new QPicture() );
857 for ( LayerRenderJob &job : firstPassJobs )
859 job.maskRequiresLayerRasterization =
false;
861 auto it = maskedSymbolLayers.find( job.layerId );
862 if ( it != maskedSymbolLayers.end() )
864 const QList<MaskSource> &sourceList = it->second;
865 for (
const MaskSource &source : sourceList )
867 job.maskRequiresLayerRasterization |= source.hasEffects;
872 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
873 if ( isRasterRendering && !job.img )
875 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
877 else if ( !isRasterRendering && !job.picture )
879 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
880 job.picture = std::move( pictureAndPainter.first );
881 job.context()->setPainter( pictureAndPainter.second );
884 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
888 if ( maskLayerHasEffects.contains( job.layerId ) )
890 QPaintDevice *maskPaintDevice =
nullptr;
891 QPainter *maskPainter =
nullptr;
892 if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
895 std::unique_ptr< QgsGeometryPaintDevice > geomPaintDevice = std::make_unique< QgsGeometryPaintDevice >( );
896 geomPaintDevice->setStrokedPathSegments( 4 );
897 geomPaintDevice->setSimplificationTolerance( job.context()->maskSettings().simplifyTolerance() );
898 maskPaintDevice = geomPaintDevice.release();
899 maskPainter =
new QPainter( maskPaintDevice );
904 QImage *maskImage =
nullptr;
905 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
906 maskImage->fill( 0 );
907 maskPaintDevice = maskImage;
910 job.context()->setMaskPainter( maskPainter );
911 job.maskPainter.reset( maskPainter );
912 job.maskPaintDevice.reset( maskPaintDevice );
916 for ( LayerRenderJob &job : firstPassJobs )
920 auto it = maskedSymbolLayers.find( job.layerId );
921 if ( it == maskedSymbolLayers.end() )
924 QList<MaskSource> &sourceList = it->second;
925 const QSet<QString> symbolList = it->first;
927 secondPassJobs.emplace_back( LayerRenderJob() );
928 LayerRenderJob &job2 = secondPassJobs.back();
930 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
933 for ( MaskSource &source : sourceList )
935 if ( source.labelMaskId != -1 )
936 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
938 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
942 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
944 job2.layer = job.layer;
945 job2.renderAboveLabels = job.renderAboveLabels;
946 job2.layerId = job.layerId;
949 job2.firstPassJob = &job;
951 if ( !forceVector || job2.maskRequiresLayerRasterization )
953 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
957 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
958 job2.picture = std::move( pictureAndPainter.first );
959 job2.context()->setPainter( pictureAndPainter.second );
962 if ( ! job2.img && ! job2.picture )
964 secondPassJobs.pop_back();
971 job2.renderer = mapRenderer;
974 job2.context()->setFeedback( job2.renderer->feedback() );
979 job2.context()->setDisabledSymbolLayersV2( symbolList );
982 return secondPassJobs;
990 for ( LayerRenderJob &job : secondPassJobs )
992 if ( job.maskRequiresLayerRasterization )
998 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
1000 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
1002 const QSet<QString> layers = job.context()->disabledSymbolLayersV2();
1005 QgsGeometry geometry( geometryDevice->geometry().clone() );
1007#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10
1014 for (
const QString &symbolLayerId : layers )
1016 job.context()->addSymbolLayerClipGeometry( symbolLayerId, geometry );
1021 job.context()->setDisabledSymbolLayersV2( QSet<QString>() );
1029 job.context.setPainter( painter );
1030 job.context.setLabelingEngine( labelingEngine2 );
1031 job.context.setFeedback( mLabelingEngineFeedback );
1035 job.context.setExtent( r1 );
1037 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
1040 job.context.setCoordinateTransform( ct );
1051 job.complete =
true;
1054 job.context.setPainter(
nullptr );
1058 if ( canUseLabelCache && (
mCache || !painter ) )
1060 job.img = allocateImage( QStringLiteral(
"labels" ) );
1070 for ( LayerRenderJob &job : jobs )
1074 delete job.context()->painter();
1075 job.context()->setPainter(
nullptr );
1077 if (
mCache && !job.cached && job.completed && job.layer )
1079 QgsDebugMsgLevel( QStringLiteral(
"caching image for %1" ).arg( job.layerId ), 2 );
1088 if ( job.previewRenderImage )
1090 delete job.context()->previewRenderPainter();
1091 job.context()->setPreviewRenderPainter(
nullptr );
1092 delete job.previewRenderImage;
1093 job.previewRenderImage =
nullptr;
1096 if ( job.elevationMap )
1098 job.context()->setElevationMap(
nullptr );
1099 if (
mCache && !job.cached && job.completed && job.layer )
1101 QgsDebugMsgLevel( QStringLiteral(
"caching elevation map for %1" ).arg( job.layerId ), 2 );
1104 job.elevationMap->rawElevationImage(),
1107 QList< QgsMapLayer * >() << job.layer );
1110 job.elevationMap->rawElevationImage(),
1113 QList< QgsMapLayer * >() << job.layer );
1116 delete job.elevationMap;
1117 job.elevationMap =
nullptr;
1122 delete job.context()->painter();
1123 job.context()->setPainter(
nullptr );
1124 job.picture.reset(
nullptr );
1129 const QStringList
errors = job.renderer->errors();
1130 for (
const QString &message :
errors )
1131 mErrors.append( Error( job.renderer->layerId(), message ) );
1133 mRenderedItemResults->appendResults( job.renderer->takeRenderedItemDetails(), *job.context() );
1135 delete job.renderer;
1136 job.renderer =
nullptr;
1142 job.maskPainter.reset(
nullptr );
1143 job.maskPaintDevice.reset(
nullptr );
1151 for ( LayerRenderJob &job : jobs )
1155 delete job.context()->painter();
1156 job.context()->setPainter(
nullptr );
1162 if ( job.previewRenderImage )
1164 delete job.context()->previewRenderPainter();
1165 job.context()->setPreviewRenderPainter(
nullptr );
1166 delete job.previewRenderImage;
1167 job.previewRenderImage =
nullptr;
1172 delete job.context()->painter();
1173 job.context()->setPainter(
nullptr );
1178 delete job.renderer;
1179 job.renderer =
nullptr;
1193 if (
mCache && !job.cached && !job.context.renderingStopped() )
1204 job.picture.reset(
nullptr );
1205 job.maskPainters.clear();
1206 job.maskPaintDevices.clear();
1210#define DEBUG_RENDERING 0
1213 const std::vector<LayerRenderJob> &jobs,
1214 const LabelRenderJob &labelJob,
1220 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1221 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1225 std::unique_ptr<QgsElevationMap> mainElevationMap;
1226 if ( mapShadingRenderer.
isActive() )
1229 QPainter painter( &image );
1234 for (
const LayerRenderJob &job : jobs )
1236 if ( job.renderAboveLabels )
1243 painter.setCompositionMode( job.blendMode );
1244 painter.setOpacity( job.opacity );
1246 if ( mainElevationMap )
1249 if ( layerElevationMap.
isValid() )
1255 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1259 painter.drawImage( 0, 0, img );
1262 if ( mapShadingRenderer.
isActive() && mainElevationMap )
1270 if ( labelJob.img && labelJob.complete )
1272 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1273 painter.setOpacity( 1.0 );
1274 painter.drawImage( 0, 0, *labelJob.img );
1282 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1283 painter.setOpacity( 1.0 );
1284 painter.drawImage( 0, 0, labelCacheImage );
1288 for (
const LayerRenderJob &job : jobs )
1290 if ( !job.renderAboveLabels )
1297 painter.setCompositionMode( job.blendMode );
1298 painter.setOpacity( job.opacity );
1300 painter.drawImage( 0, 0, img );
1305 image.save(
"/tmp/final.png" );
1312 const LayerRenderJob &job,
1316 if ( job.imageCanBeComposed() )
1318 if ( job.previewRenderImage && !job.completed )
1319 return *job.previewRenderImage;
1321 Q_ASSERT( job.img );
1326 if ( cache && cache->
hasAnyCacheImage( job.layerId + QStringLiteral(
"_preview" ) ) )
1337 if ( job.imageCanBeComposed() && job.elevationMap )
1339 return *job.elevationMap;
1353 for ( LayerRenderJob &job : secondPassJobs )
1355 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1358 if ( isRasterRendering && job.maskJobs.size() > 1 )
1360 QPainter *maskPainter =
nullptr;
1361 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1363 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1366 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1370 maskPainter->drawImage( 0, 0, *maskImage );
1375 if ( ! job.maskJobs.isEmpty() )
1378 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1379 if ( isRasterRendering )
1381 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1384 QPainter *painter = job.context()->painter();
1386 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1391 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1392 QVector<QRgb> mswTable;
1393 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1394 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1395 maskBinAlpha.setColorTable( mswTable );
1396 painter->drawImage( 0, 0, maskBinAlpha );
1400 QPainter tempPainter;
1403 QPainter *painter1 = job.firstPassJob->context()->painter();
1406 tempPainter.begin( job.firstPassJob->img );
1407 painter1 = &tempPainter;
1411 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1412 painter1->drawImage( 0, 0, *maskImage );
1415 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1416 painter1->drawImage( 0, 0, *job.img );
1421 job.firstPassJob->picture = std::move( job.picture );
1422 job.picture =
nullptr;
1433 QMultiMap<int, QString> elapsed;
1434 for (
const LayerRenderJob &job : jobs )
1436 for (
const LayerRenderJob &job : secondPassJobs )
1437 elapsed.insert( job.
renderingTime, job.layerId + QString(
" (second pass)" ) );
1439 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1441 QList<int> tt( elapsed.uniqueKeys() );
1442 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1443 for (
int t : std::as_const( tt ) )
1445 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String(
", " ) ) ), tr(
"Rendering" ) );
1454 std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile;
1457 labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"(labeling)" ), QStringLiteral(
"rendering" ) );
1464 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1468 if ( labelingEngine2 )
1470 labelingEngine2->
run( renderContext );
1473 QgsDebugMsgLevel( QStringLiteral(
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
1478 Q_UNUSED( settings )
1480 drawLabeling( renderContext, labelingEngine2, painter );
@ 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.
@ 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 (since QGIS 3.34)
@ 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 requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
virtual bool requiresAdvancedEffects() const =0
Returns true if drawing labels requires advanced effects like composition modes, which could prevent ...
Custom exception class for Coordinate Reference System related exceptions.
bool isInfinite() const
Returns true if the range consists of all possible values.
Stores digital elevation model in a raster image which may get updated as a part of map layer renderi...
bool isValid() const
Returns whether the elevation map is valid.
This class can render elevation shading on an image with different methods (eye dome lighting,...
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.
A paint device which converts everything renderer to a QgsGeometry representation of the rendered sha...
A geometry is the spatial representation of a feature.
QgsFeedback subclass for granular reporting of labeling engine progress.
The QgsLabelingEngine class provides map labeling functionality.
virtual void run(QgsRenderContext &context)=0
Runs the labeling job.
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 void setLayerRenderingTimeHint(int time)
Sets approximate render time (in ms) for the layer to render.
Restore overridden layer style on destruction.
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.
This class is responsible for keeping cache of rendered images resulting from a map rendering job.
bool updateParameters(const QgsRectangle &extent, const QgsMapToPixel &mtp)
Sets extent and scale parameters.
QList< QgsMapLayer * > dependentLayers(const QString &cacheKey) const
Returns a list of map layers on which an image in the cache depends.
bool hasCacheImage(const QString &cacheKey) const
Returns true if the cache contains an image with the specified cacheKey that has the same extent and ...
QImage cacheImage(const QString &cacheKey) const
Returns the cached image for the specified cacheKey.
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 (...
void setCacheImageWithParameters(const QString &cacheKey, const QImage &image, const QgsRectangle &extent, const QgsMapToPixel &mapToPixel, const QList< QgsMapLayer * > &dependentLayers=QList< QgsMapLayer * >())
Set the cached image for a particular cacheKey, using a specific extent and mapToPixel (which may dif...
void clearCacheImage(const QString &cacheKey)
Removes an image from the cache with matching cacheKey.
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)
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.
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).
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.
int renderingTime() const
Returns the total time it took to finish the job (in milliseconds).
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)
The QgsMapSettings class contains configuration for rendering of the map.
QSize deviceOutputSize() const
Returns the device output size of the map render.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
double scale() const
Returns the calculated map scale.
QgsCoordinateTransform layerTransform(const QgsMapLayer *layer) const
Returns the coordinate transform from layer's CRS to destination CRS.
QgsDoubleRange zRange() const
Returns the range of z-values which will be visible in the map.
QColor backgroundColor() const
Returns the background color of the map.
const QgsMapToPixel & mapToPixel() const
float devicePixelRatio() const
Returns the device pixel ratio.
QSize outputSize() const
Returns the size of the resulting map image, in pixels.
QImage::Format outputImageFormat() const
format of internal QImage, default QImage::Format_ARGB32_Premultiplied
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
Qgis::MapSettingsFlags flags() const
Returns combination of flags used for rendering.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
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.
QMap< QString, QString > layerStyleOverrides() const
Returns the map of map layer style overrides (key: layer ID, value: style name) where a different sty...
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
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)
Adds a message to the log instance (and creates it if necessary).
static bool staticWillUseLayer(const QgsMapLayer *layer)
Called to find out whether a specified layer is used for labeling.
A class to represent a 2D point.
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Represents a raster layer.
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.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setXMaximum(double x)
Set the maximum x value.
void grow(double delta)
Grows the rectangle in place by the specified amount.
double height() const
Returns the height of the rectangle.
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.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
A boolean settings entry.
static QgsSettingsTreeNode * sTreeMap
Type used to refer to a specific symbol layer in a symbol of a layer.
const QgsDateTimeRange & temporalRange() const
Returns the datetime range for the object.
bool isTemporal() const
Returns true if the object's temporal range is enabled, and the object will be filtered when renderin...
Implementation of threaded rendering for vector layers.
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.
Represents a vector layer which manages a vector based data sets.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to const labeling configuration.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
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