19 #include <QElapsedTimer>
21 #include <QtConcurrentMap>
54 : mSettings( settings )
78 QHash<QgsMapLayer *, int> result;
81 if (
auto &&lKey = it.key() )
82 result.insert( lKey, it.value() );
97 QSet< QgsMapLayer * > labeledLayers;
104 switch ( ml->type() )
168 static const double SPLIT_COORD = 180.0;
180 QgsDebugMsgLevel( QStringLiteral(
"\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" )
183 .arg( std::fabs( 1.0 - extent2.
width() / extent.
width() ) )
184 .arg( std::fabs( 1.0 - extent2.
height() / extent.
height() ) )
218 if ( ll.
x() > ur.
x() )
247 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
256 QgsDebugMsg( QStringLiteral(
"Transform error caught" ) );
257 extent =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
258 r2 =
QgsRectangle( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max() );
265 QImage *QgsMapRendererJob::allocateImage( QString layerId )
270 if ( image->isNull() )
279 QPainter *QgsMapRendererJob::allocateImageAndPainter( QString layerId, QImage *&image )
281 QPainter *painter =
nullptr;
282 image = allocateImage( layerId );
285 painter =
new QPainter( image );
287 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
296 LayerRenderJobs layerJobs;
305 Q_UNUSED( cacheValid )
306 QgsDebugMsgLevel( QStringLiteral(
"CACHE VALID: %1" ).arg( cacheValid ), 4 );
311 while ( li.hasPrevious() )
315 QgsDebugMsgLevel( QStringLiteral(
"layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 blendmode:%5 isValid:%6" )
332 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not within the defined visibility scale range" ), 3 );
338 QgsDebugMsgLevel( QStringLiteral(
"Layer not rendered because it is not visible within the map's time range" ), 3 );
347 bool haveExtentInLayerCrs =
true;
350 haveExtentInLayerCrs = reprojectToLayerExtent( ml, ct, r1, r2 );
355 mErrors.append( Error( ml->
id(), tr(
"There was a problem transforming the layer's extent. Layer skipped." ) ) );
366 if ( ( vl && vl->
isEditable() ) || requiresLabeling )
372 layerJobs.append( LayerRenderJob() );
373 LayerRenderJob &job = layerJobs.last();
377 job.layerId = ml->
id();
378 job.renderingTime = -1;
382 job.context.setPainter( painter );
383 job.context.setLabelingEngine( labelingEngine2 );
384 job.context.setCoordinateTransform( ct );
385 job.context.setExtent( r1 );
386 if ( !haveExtentInLayerCrs )
389 if ( mFeatureFilterProvider )
390 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
404 job.opacity = al->opacity();
411 job.imageInitialized =
true;
414 job.renderer =
nullptr;
415 job.context.setPainter(
nullptr );
422 if (
mCache || ( !painter && !deferredPainterSet ) || needTemporaryImage( ml ) )
425 job.context.setPainter( allocateImageAndPainter( ml->
id(), job.img ) );
428 layerJobs.removeLast();
433 QElapsedTimer layerTime;
436 job.renderingTime = layerTime.elapsed();
444 LayerRenderJobs secondPassJobs;
447 QHash<QString, LayerRenderJob *> layerJobMapping;
450 QSet<QString> layerHasMask;
457 MaskSource(
const QString &layerId_,
const QString &labelRuleId_,
int labelMaskId_ ):
458 layerId( layerId_ ), labelRuleId( labelRuleId_ ), labelMaskId( labelMaskId_ ) {}
463 QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;
465 for ( LayerRenderJob &job : firstPassJobs )
471 layerJobMapping[job.layer->id()] = &job;
474 auto collectMasks = [&]( QHash<QString, QSet<QgsSymbolLayerId>> *masks, QString sourceLayerId, QString ruleId = QString(),
int labelMaskId = -1 )
476 for (
auto it = masks->begin(); it != masks->end(); ++it )
478 auto lit = maskedSymbolLayers.find( it.key() );
479 if ( lit == maskedSymbolLayers.end() )
481 maskedSymbolLayers[it.key()] = qMakePair( it.value(), QList<MaskSource>() << MaskSource( sourceLayerId, ruleId, labelMaskId ) );
485 if ( lit->first != it.value() )
487 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() ) );
490 lit->second.push_back( MaskSource( sourceLayerId, ruleId, labelMaskId ) );
493 if ( ! masks->isEmpty() )
494 layerHasMask.insert( sourceLayerId );
499 for (
auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
501 QString labelRule = it.key();
502 QHash<QString, QSet<QgsSymbolLayerId>> masks = it.value();
505 QSet<QgsSymbolLayerReference> slRefs;
506 for (
auto mit = masks.begin(); mit != masks.end(); mit++ )
508 for (
auto slIt = mit.value().begin(); slIt != mit.value().end(); slIt++ )
514 int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->
id(), it.key(), slRefs );
517 collectMasks( &masks, vl->
id(), labelRule, labelMaskId );
525 if ( maskedSymbolLayers.isEmpty() )
526 return secondPassJobs;
530 for ( LayerRenderJob &job : firstPassJobs )
534 if ( job.img ==
nullptr )
536 job.context.setPainter( allocateImageAndPainter( ml->
id(), job.img ) );
538 if ( layerHasMask.contains( ml->
id() ) )
541 job.context.setMaskPainter( allocateImageAndPainter( ml->
id(), job.maskImage ) );
542 job.maskImage->fill( 0 );
547 if ( labelJob.img ==
nullptr )
549 labelJob.img = allocateImage( QStringLiteral(
"labels" ) );
553 for (
int maskId = 0; maskId < labelJob.maskIdProvider.size(); maskId++ )
556 labelJob.context.setMaskPainter( allocateImageAndPainter( QStringLiteral(
"label mask" ), maskImage ), maskId );
557 maskImage->fill( 0 );
558 labelJob.maskImages.push_back( maskImage );
560 labelJob.context.setMaskIdProvider( &labelJob.maskIdProvider );
563 for ( LayerRenderJob &job : firstPassJobs )
567 auto it = maskedSymbolLayers.find( ml->
id() );
568 if ( it == maskedSymbolLayers.end() )
571 QList<MaskSource> &sourceList = it->second;
572 const QSet<QgsSymbolLayerId> &symbolList = it->first;
575 secondPassJobs.append( LayerRenderJob() );
576 LayerRenderJob &job2 = secondPassJobs.last();
579 job2.firstPassJob = &job;
580 QgsVectorLayer *vl1 = qobject_cast<QgsVectorLayer *>( job.layer );
583 job2.context.setMaskPainter(
nullptr );
584 job2.context.setPainter( allocateImageAndPainter( vl1->
id(), job2.img ) );
587 secondPassJobs.removeLast();
592 for ( MaskSource &source : sourceList )
594 if ( source.labelMaskId != -1 )
595 job2.maskJobs.push_back( qMakePair(
nullptr, source.labelMaskId ) );
597 job2.maskJobs.push_back( qMakePair( layerJobMapping[source.layerId], -1 ) );
603 job2.renderer = mapRenderer;
610 return secondPassJobs;
617 job.context.setPainter( painter );
618 job.context.setLabelingEngine( labelingEngine2 );
620 job.context.setFeatureFilterProvider( mFeatureFilterProvider );
630 job.context.setPainter(
nullptr );
634 if ( canUseLabelCache && (
mCache || !painter ) )
636 job.img = allocateImage( QStringLiteral(
"labels" ) );
646 for ( LayerRenderJobs::iterator it = jobs.begin(); it != jobs.end(); ++it )
648 LayerRenderJob &job = *it;
651 delete job.context.painter();
652 job.context.setPainter(
nullptr );
654 if (
mCache && !job.cached && !job.context.renderingStopped() && job.layer )
656 QgsDebugMsgLevel( QStringLiteral(
"caching image for %1" ).arg( job.layerId ), 2 );
667 delete job.context.maskPainter();
668 job.context.setMaskPainter(
nullptr );
669 delete job.maskImage;
674 const auto constErrors = job.renderer->errors();
675 for (
const QString &message : constErrors )
676 mErrors.append( Error( job.renderer->layerId(), message ) );
679 job.renderer =
nullptr;
691 for (
auto &job : jobs )
695 delete job.context.painter();
696 job.context.setPainter(
nullptr );
705 job.renderer =
nullptr;
719 if (
mCache && !job.cached && !job.context.renderingStopped() )
729 for (
int maskId = 0; maskId < job.maskImages.size(); maskId++ )
731 delete job.context.maskPainter( maskId );
732 job.context.setMaskPainter(
nullptr, maskId );
733 delete job.maskImages[maskId];
738 #define DEBUG_RENDERING 0
744 image.setDotsPerMeterX(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
745 image.setDotsPerMeterY(
static_cast<int>( settings.
outputDpi() * 39.37 ) );
748 QPainter painter( &image );
753 for ( LayerRenderJobs::const_iterator it = jobs.constBegin(); it != jobs.constEnd(); ++it )
755 const LayerRenderJob &job = *it;
757 if ( job.layer && job.layer->customProperty( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool() )
760 if ( !job.imageInitialized )
763 painter.setCompositionMode( job.blendMode );
764 painter.setOpacity( job.opacity );
767 job.img->save( QString(
"/tmp/final_%1.png" ).arg( i ) );
772 painter.drawImage( 0, 0, *job.img );
778 if ( labelJob.img && labelJob.complete )
780 painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
781 painter.setOpacity( 1.0 );
782 painter.drawImage( 0, 0, *labelJob.img );
786 for ( LayerRenderJobs::const_iterator it = jobs.constBegin(); it != jobs.constEnd(); ++it )
788 const LayerRenderJob &job = *it;
790 if ( !job.layer || !job.layer->customProperty( QStringLiteral(
"rendering/renderAboveLabels" ) ).toBool() )
793 if ( !job.imageInitialized )
796 painter.setCompositionMode( job.blendMode );
797 painter.setOpacity( job.opacity );
801 painter.drawImage( 0, 0, *job.img );
806 image.save(
"/tmp/final.png" );
817 for ( LayerRenderJob &job : secondPassJobs )
821 job.img->save( QString(
"/tmp/second_%1.png" ).arg( i ) );
826 if ( job.maskJobs.size() > 1 )
828 QPainter *maskPainter =
nullptr;
829 for ( QPair<LayerRenderJob *, int> p : job.maskJobs )
831 QImage *maskImage = p.first ? p.first->maskImage : labelJob.maskImages[p.second];
833 maskImage->save( QString(
"/tmp/mask_%1_%2.png" ).arg( i ).arg( mask++ ) );
837 maskPainter = p.first ? p.first->context.maskPainter() : labelJob.context.maskPainter( p.second );
841 maskPainter->drawImage( 0, 0, *maskImage );
846 if ( ! job.maskJobs.isEmpty() )
849 QPair<LayerRenderJob *, int> p = *job.maskJobs.begin();
850 QImage *maskImage = p.first ? p.first->maskImage : labelJob.maskImages[p.second];
852 maskImage->save( QString(
"/tmp/mask_%1.png" ).arg( i ) );
856 QPainter *painter = job.context.painter();
857 painter->setCompositionMode( QPainter::CompositionMode_DestinationIn );
862 QImage maskBinAlpha = maskImage->createMaskFromColor( 0 );
863 QVector<QRgb> mswTable;
864 mswTable.push_back( qRgba( 0, 0, 0, 255 ) );
865 mswTable.push_back( qRgba( 0, 0, 0, 0 ) );
866 maskBinAlpha.setColorTable( mswTable );
867 painter->drawImage( 0, 0, maskBinAlpha );
869 job.img->save( QString(
"/tmp/second_%1_a.png" ).arg( i ) );
874 QPainter tempPainter;
877 QPainter *painter1 = job.firstPassJob->context.painter();
880 tempPainter.begin( job.firstPassJob->img );
881 painter1 = &tempPainter;
884 job.firstPassJob->img->save( QString(
"/tmp/second_%1_first_pass_1.png" ).arg( i ) );
887 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOut );
888 painter1->drawImage( 0, 0, *maskImage );
891 job.firstPassJob->img->save( QString(
"/tmp/second_%1_first_pass_2.png" ).arg( i ) );
894 painter1->setCompositionMode( QPainter::CompositionMode_DestinationOver );
895 painter1->drawImage( 0, 0, *job.img );
897 job.img->save( QString(
"/tmp/second_%1_b.png" ).arg( i ) );
898 if ( job.firstPassJob )
899 job.firstPassJob->img->save( QString(
"/tmp/second_%1_first_pass_3.png" ).arg( i ) );
909 if ( !settings.
value( QStringLiteral(
"Map/logCanvasRefreshEvent" ),
false ).toBool() )
912 QMultiMap<int, QString> elapsed;
913 const auto constJobs = jobs;
914 for (
const LayerRenderJob &job : constJobs )
915 elapsed.insert( job.renderingTime, job.layerId );
916 const auto constSecondPassJobs = secondPassJobs;
917 for (
const LayerRenderJob &job : constSecondPassJobs )
918 elapsed.insert( job.renderingTime, job.layerId + QString(
" (second pass)" ) );
920 elapsed.insert( labelJob.renderingTime, tr(
"Labeling" ) );
922 QList<int> tt( elapsed.uniqueKeys() );
923 std::sort( tt.begin(), tt.end(), std::greater<int>() );
924 const auto constTt = tt;
925 for (
int t : constTt )
927 QgsMessageLog::logMessage( tr(
"%1 ms: %2" ).arg( t ).arg( QStringList( elapsed.values( t ) ).join( QLatin1String(
", " ) ) ), tr(
"Rendering" ) );
932 bool QgsMapRendererJob::needTemporaryImage(
QgsMapLayer *ml )
934 switch ( ml->
type() )
945 ( ( vl->
blendMode() != QPainter::CompositionMode_SourceOver )
992 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
996 if ( labelingEngine2 )
998 labelingEngine2->
run( renderContext );
1001 QgsDebugMsgLevel( QStringLiteral(
"Draw labeling took (seconds): %1" ).arg( t.elapsed() / 1000. ), 2 );
1006 Q_UNUSED( settings )
1008 drawLabeling( renderContext, labelingEngine2, painter );