19 #include <QElapsedTimer>
21 #include <QtConcurrentMap>
56 LayerRenderJob &LayerRenderJob::operator=( LayerRenderJob &&other )
58 mContext = std::move( other.mContext );
63 renderer = other.renderer;
64 other.renderer =
nullptr;
66 imageInitialized = other.imageInitialized;
67 blendMode = other.blendMode;
68 opacity = other.opacity;
69 cached = other.cached;
71 completed = other.completed;
72 renderingTime = other.renderingTime;
73 estimatedRenderingTime = other.estimatedRenderingTime ;
74 errors = other.errors;
75 layerId = other.layerId;
77 maskPaintDevice = std::move( other.maskPaintDevice );
79 firstPassJob = other.firstPassJob;
80 other.firstPassJob =
nullptr;
82 picture = std::move( other.picture );
84 maskJobs = other.maskJobs;
86 maskRequiresLayerRasterization = other.maskRequiresLayerRasterization;
91 LayerRenderJob::LayerRenderJob( LayerRenderJob &&other )
92 : imageInitialized( other.imageInitialized )
93 , blendMode( other.blendMode )
94 , opacity( other.opacity )
95 , cached( other.cached )
96 , layer( other.layer )
97 , completed( other.completed )
98 , renderingTime( other.renderingTime )
99 , estimatedRenderingTime( other.estimatedRenderingTime )
100 , errors( other.errors )
101 , layerId( other.layerId )
102 , maskRequiresLayerRasterization( other.maskRequiresLayerRasterization )
103 , maskJobs( other.maskJobs )
105 mContext = std::move( other.mContext );
110 renderer = other.renderer;
111 other.renderer =
nullptr;
113 maskPaintDevice = std::move( other.maskPaintDevice );
115 firstPassJob = other.firstPassJob;
116 other.firstPassJob =
nullptr;
118 picture = std::move( other.picture );
121 bool LayerRenderJob::imageCanBeComposed()
const
123 if ( imageInitialized )
127 return renderer->isReadyToCompose();
141 : mSettings( settings )
187 return mLabelingEngineFeedback;
192 QHash<QgsMapLayer *, int> result;
195 if (
auto &&lKey = it.key() )
196 result.insert( lKey, it.value() );
216 QSet< QgsMapLayer * > labeledLayers;
223 switch ( ml->type() )
289 static const double SPLIT_COORD = 180.0;
301 QgsDebugMsgLevel( QStringLiteral(
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
304 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
305 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
327 Qgis::TransformDirection::Reverse );
331 Qgis::TransformDirection::Reverse );
339 if ( ll.
x() > ur.
x() )
368 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
377 QgsDebugMsg( QStringLiteral(
"Transform error caught" ) );
378 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
379 r2 =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
386 QImage *QgsMapRendererJob::allocateImage( QString layerId )
393 if ( image->isNull() )
402 QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image,
const QgsRenderContext *context )
404 QPainter *painter =
nullptr;
405 image = allocateImage( layerId );
408 painter =
new QPainter( image );
414 QgsMapRendererJob::PictureAndPainter QgsMapRendererJob::allocatePictureAndPainter(
const QgsRenderContext *context )
416 std::unique_ptr<QPicture> picture = std::make_unique<QPicture>();
417 QPainter *painter =
new QPainter( picture.get() );
419 return { std::move( picture ), painter };
424 std::vector< LayerRenderJob > layerJobs;
433 Q_UNUSED( cacheValid )
434 QgsDebugMsgLevel( QStringLiteral(
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
439 while ( li.hasPrevious() )
443 QgsDebugMsgLevel( QStringLiteral(
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
460 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
466 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's time range" ), 3 );
472 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's z range" ), 3 );
481 bool haveExtentInLayerCrs =
true;
484 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
489 mErrors.append( Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
500 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
506 layerJobs.emplace_back( LayerRenderJob() );
507 LayerRenderJob &job = layerJobs.back();
509 job.layerId = ml->
id();
513 if ( !ml->
customProperty( QStringLiteral(
"_noset_layer_expression_context" ) ).toBool() )
515 job.context()->setPainter( painter );
516 job.context()->setLabelingEngine( labelingEngine2 );
517 job.context()->setLabelSink(
labelSink() );
518 job.context()->setCoordinateTransform( ct );
519 job.context()->setExtent( r1 );
520 if ( !haveExtentInLayerCrs )
523 if ( mFeatureFilterProvider )
524 job.context()->setFeatureFilterProvider( mFeatureFilterProvider );
541 job.imageInitialized =
true;
544 job.renderer =
nullptr;
545 job.context()->setPainter(
nullptr );
550 QElapsedTimer layerTime;
556 job.context()->setFeedback( job.renderer->feedback() );
562 if (
mCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) )
565 job.context()->setPainter( allocateImageAndPainter( ml->
id(), job.img, job.context() ) );
569 job.renderer =
nullptr;
570 layerJobs.pop_back();
575 job.renderingTime = layerTime.elapsed();
583 std::vector< LayerRenderJob > secondPassJobs;
586 QHash<QString, LayerRenderJob *> layerJobMapping;
589 QMap<QString, bool> maskLayerHasEffects;
590 QMap<int, bool> labelHasEffects;
598 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_,
bool hasEffects_ ):
599 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ), hasEffects( hasEffects_ ) {}
604 QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;
611 for ( LayerRenderJob &job : firstPassJobs )
613 layerJobMapping[job.layerId] = &job;
618 for ( LayerRenderJob &job : firstPassJobs )
625 auto collectMasks = [&](
QgsMaskedLayers * masks, QString sourceLayerId, QString ruleId = QString(),
int labelMaskId = -1 )
627 bool hasEffects =
false;
628 for (
auto it = masks->begin(); it != masks->end(); ++it )
630 auto lit = maskedSymbolLayers.find( it.key() );
631 if ( lit == maskedSymbolLayers.end() )
633 maskedSymbolLayers[it.key()] = qMakePair( it.value().symbolLayerIds, QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId, it.value().hasEffects ) );
637 if ( lit->first != it.value().symbolLayerIds )
639 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() ) );
642 lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId, hasEffects ) );
644 hasEffects |= it.value().hasEffects;
646 if ( ! masks->isEmpty() && labelMaskId == -1 )
647 maskLayerHasEffects[ sourceLayerId ] = hasEffects;
652 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
654 QString labelRule = it.key();
660 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
662 const QString sourceLayerId = mit.key();
664 if ( !layerJobMapping.contains( sourceLayerId ) )
667 usableMasks.insert( sourceLayerId, mit.value() );
670 if ( usableMasks.empty() )
674 QSet<QgsSymbolLayerReference> slRefs;
675 bool hasEffects =
false;
676 for (
auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
678 const QString sourceLayerId = mit.key();
680 if ( !layerJobMapping.contains( sourceLayerId ) )
683 for (
auto slIt = mit.value().symbolLayerIds.begin(); slIt != mit.value().symbolLayerIds.end(); slIt++ )
688 hasEffects |= mit.value().hasEffects;
691 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
692 labelHasEffects[ labelMaskId ] = hasEffects;
695 collectMasks( &usableMasks, vl->
id(), labelRule, labelMaskId );
700 collectMasks( &symbolLayerMasks, vl->
id() );
703 if ( maskedSymbolLayers.isEmpty() )
704 return secondPassJobs;
707 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
709 QPaintDevice *maskPaintDevice =
nullptr;
710 QPainter *maskPainter =
nullptr;
711 if ( forceVector && !labelHasEffects[ maskId ] )
715 maskPainter =
new QPainter( maskPaintDevice );
720 QImage *maskImage =
nullptr;
721 maskPainter = allocateImageAndPainter( QStringLiteral(
"label mask" ), maskImage, &labelJob.context );
722 maskImage->fill( 0 );
723 maskPaintDevice = maskImage;
726 labelJob.context.setMaskPainter( maskPainter, maskId );
727 labelJob.maskPainters.push_back( std::unique_ptr<QPainter>( maskPainter ) );
728 labelJob.maskPaintDevices.push_back( std::unique_ptr<QPaintDevice>( maskPaintDevice ) );
730 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
741 if ( !labelJob.img && !forceVector )
743 labelJob.img = allocateImage( QStringLiteral(
"labels" ) );
745 else if ( !labelJob.picture && forceVector )
747 labelJob.picture.reset(
new QPicture() );
751 for ( LayerRenderJob &job : firstPassJobs )
753 job.maskRequiresLayerRasterization =
false;
755 auto it = maskedSymbolLayers.find( job.layerId );
756 if ( it != maskedSymbolLayers.end() )
758 const QList<MaskSource> &sourceList = it->second;
759 for (
const MaskSource &source : sourceList )
761 job.maskRequiresLayerRasterization |= source.hasEffects;
766 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization || ( job.renderer && job.renderer->forceRasterRender() );
767 if ( isRasterRendering && !job.img )
769 job.context()->setPainter( allocateImageAndPainter( job.layerId, job.img, job.context() ) );
771 else if ( !isRasterRendering && !job.picture )
773 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job.context() );
774 job.picture = std::move( pictureAndPainter.first );
775 job.context()->setPainter( pictureAndPainter.second );
778 job.renderer = job.layer->createMapRenderer( *( job.context() ) );
782 if ( maskLayerHasEffects.contains( job.layerId ) )
784 QPaintDevice *maskPaintDevice =
nullptr;
785 QPainter *maskPainter =
nullptr;
786 if ( forceVector && !maskLayerHasEffects[ job.layerId ] )
790 maskPainter =
new QPainter( maskPaintDevice );
795 QImage *maskImage =
nullptr;
796 maskPainter = allocateImageAndPainter( job.layerId, maskImage, job.context() );
797 maskImage->fill( 0 );
798 maskPaintDevice = maskImage;
801 job.context()->setMaskPainter( maskPainter );
802 job.maskPainter.reset( maskPainter );
803 job.maskPaintDevice.reset( maskPaintDevice );
807 for ( LayerRenderJob &job : firstPassJobs )
811 auto it = maskedSymbolLayers.find( job.layerId );
812 if ( it == maskedSymbolLayers.end() )
815 QList<MaskSource> &sourceList = it->second;
816 const QSet<QgsSymbolLayerId> &symbolList = it->first;
818 secondPassJobs.emplace_back( LayerRenderJob() );
819 LayerRenderJob &job2 = secondPassJobs.back();
821 job2.maskRequiresLayerRasterization = job.maskRequiresLayerRasterization;
824 for ( MaskSource &source : sourceList )
826 if ( source.labelMaskId != -1 )
827 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
829 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
833 job2.setContext( std::make_unique< QgsRenderContext >( *job.context() ) );
835 job2.layer = job.layer;
836 job2.layerId = job.layerId;
839 job2.firstPassJob = &job;
841 if ( !forceVector || job2.maskRequiresLayerRasterization )
843 job2.context()->setPainter( allocateImageAndPainter( job.layerId, job2.img, job2.context() ) );
847 PictureAndPainter pictureAndPainter = allocatePictureAndPainter( job2.context() );
848 job2.picture = std::move( pictureAndPainter.first );
849 job2.context()->setPainter( pictureAndPainter.second );
852 if ( ! job2.img && ! job2.picture )
854 secondPassJobs.pop_back();
861 job2.renderer = mapRenderer;
864 job2.context()->setFeedback( job2.renderer->feedback() );
872 return secondPassJobs;
880 for ( LayerRenderJob &job : secondPassJobs )
882 if ( job.maskRequiresLayerRasterization )
888 for (
const QPair<LayerRenderJob *, int> &p : std::as_const( job.maskJobs ) )
890 QPainter *maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[p.second].get();
891 QPainterPath path =
static_cast<QgsMaskPaintDevice *
>( maskPainter->device() )->maskPainterPath();
892 for (
const QgsSymbolLayer *symbolLayer : job.context()->disabledSymbolLayers() )
894 job.context()->addSymbolLayerClipPath( symbolLayer, path );
898 job.context()->setDisabledSymbolLayers( QSet<const QgsSymbolLayer *>() );
906 job.context.setPainter( painter );
907 job.context.setLabelingEngine( labelingEngine2 );
908 job.context.setFeedback( mLabelingEngineFeedback );
912 job.context.setExtent( r1 );
914 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
917 job.context.setCoordinateTransform( ct );
931 job.context.setPainter(
nullptr );
935 if ( canUseLabelCache && (
mCache || !painter ) )
937 job.img = allocateImage( QStringLiteral(
"labels" ) );
947 for ( LayerRenderJob &job : jobs )
951 delete job.context()->painter();
952 job.context()->setPainter(
nullptr );
954 if (
mCache && !job.cached && job.completed && job.layer )
956 QgsDebugMsgLevel( QStringLiteral(
"caching image for %1" ).arg( job.layerId ), 2 );
967 delete job.context()->painter();
968 job.context()->setPainter(
nullptr );
969 job.picture.reset(
nullptr );
974 const QStringList
errors = job.renderer->errors();
975 for (
const QString &message :
errors )
976 mErrors.append( Error( job.renderer->layerId(), message ) );
981 job.renderer =
nullptr;
987 job.maskPainter.reset(
nullptr );
988 job.maskPaintDevice.reset(
nullptr );
996 for ( LayerRenderJob &job : jobs )
1000 delete job.context()->painter();
1001 job.context()->setPainter(
nullptr );
1009 delete job.context()->painter();
1010 job.context()->setPainter(
nullptr );
1015 delete job.renderer;
1016 job.renderer =
nullptr;
1030 if (
mCache && !job.cached && !job.context.renderingStopped() )
1041 job.picture.reset(
nullptr );
1042 job.maskPainters.clear();
1043 job.maskPaintDevices.clear();
1047 #define DEBUG_RENDERING 0
1050 const std::vector<LayerRenderJob> &jobs,
1051 const LabelRenderJob &labelJob,
1057 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1058 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
1061 QPainter painter( &image );
1066 for (
const LayerRenderJob &job : jobs )
1068 if ( job.layer && job.layer->customProperty( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool() )
1075 painter.setCompositionMode( job.blendMode );
1076 painter.setOpacity( job.opacity );
1079 img.save( QString(
"/tmp/final_%1.png" ).arg( i ) );
1083 painter.drawImage( 0, 0, img );
1089 if ( labelJob.img && labelJob.complete )
1091 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1092 painter.setOpacity( 1.0 );
1093 painter.drawImage( 0, 0, *labelJob.img );
1101 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
1102 painter.setOpacity( 1.0 );
1103 painter.drawImage( 0, 0, labelCacheImage );
1107 for (
const LayerRenderJob &job : jobs )
1109 if ( !job.layer || !job.layer->customProperty( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool() )
1116 painter.setCompositionMode( job.blendMode );
1117 painter.setOpacity( job.opacity );
1119 painter.drawImage( 0, 0, img );
1124 image.save(
"/tmp/final.png" );
1131 const LayerRenderJob &job,
1135 if ( job.imageCanBeComposed() )
1137 Q_ASSERT( job.img );
1142 if ( cache && cache->
hasAnyCacheImage( job.layerId + QStringLiteral(
"_preview" ) ) )
1154 for ( LayerRenderJob &job : secondPassJobs )
1156 const bool isRasterRendering = !forceVector || job.maskRequiresLayerRasterization;
1159 if ( isRasterRendering && job.maskJobs.size() > 1 )
1161 QPainter *maskPainter =
nullptr;
1162 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
1164 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1167 maskPainter = p.first ? p.first->maskPainter.get() : labelJob.maskPainters[ p.second ].get();
1171 maskPainter->drawImage( 0, 0, *maskImage );
1176 if ( ! job.maskJobs.isEmpty() )
1179 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
1180 if ( isRasterRendering )
1182 QImage *maskImage =
static_cast<QImage *
>( p.first ? p.first->maskPaintDevice.get() : labelJob.maskPaintDevices[p.second].get() );
1185 QPainter *painter = job.context()->painter();
1187 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
1192 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
1193 QVector<QRgb> mswTable;
1194 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
1195 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
1196 maskBinAlpha.setColorTable( mswTable );
1197 painter->drawImage( 0, 0, maskBinAlpha );
1201 QPainter tempPainter;
1204 QPainter *painter1 = job.firstPassJob->context()->painter();
1207 tempPainter.begin( job.firstPassJob->img );
1208 painter1 = &tempPainter;
1212 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
1213 painter1->drawImage( 0, 0, *maskImage );
1216 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
1217 painter1->drawImage( 0, 0, *job.img );
1222 job.firstPassJob->picture = std::move( job.picture );
1223 job.picture =
nullptr;
1229 void QgsMapRendererJob::logRenderingTime(
const std::vector< LayerRenderJob > &jobs,
const std::vector< LayerRenderJob > &secondPassJobs,
const LabelRenderJob &labelJob )
1234 QMultiMap<int, QString> elapsed;
1235 for (
const LayerRenderJob &job : jobs )
1236 elapsed.insert( job.renderingTime, job.layerId );
1237 for (
const LayerRenderJob &job : secondPassJobs )
1238 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
1240 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
1242 QList<int> tt( elapsed.uniqueKeys() );
1243 std::sort( tt.begin(), tt.end(), std::greater<int>() );
1244 for (
int t : std::as_const( tt ) )
1246 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String(
", " ) ) ), tr(
"Rendering" ) );
1259 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
1263 if ( labelingEngine2 )
1265 labelingEngine2->
run( renderContext );
1268 QgsDebugMsgLevel( QStringLiteral(
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
1273 Q_UNUSED( settings )
1275 drawLabeling( renderContext, labelingEngine2, painter );