Skip to content

Commit

Permalink
When determining whether any symbol masking should be applied to a
Browse files Browse the repository at this point in the history
layer, we need to skip any masks which refer to layers which aren't
part of the current map render

These layers can't be used as mask sources, since we aren't rendering
them.

This is a band-aid fix over a deeper problem -- symbol masking settings
are not cleaned up when a layer is removed from a project. So it's
possible to have layers with label settings which contain mask
references to broken layers. These aren't ever shown in the UI, so it's
impossible for a user to determine that the masking is even configured
for the layer and fix it themselves.

At least with the band-aid users won't have to fight with a random
project force-rasterizing all labels (like I just did for the last
2 hours)
  • Loading branch information
nyalldawson committed May 24, 2021
1 parent f16c28b commit ffc500d
Showing 1 changed file with 34 additions and 2 deletions.
36 changes: 34 additions & 2 deletions src/core/maprenderer/qgsmaprendererjob.cpp
Expand Up @@ -494,13 +494,24 @@ LayerRenderJobs QgsMapRendererJob::prepareSecondPassJobs( LayerRenderJobs &first
// and the list of source layers that have a mask
QHash<QString, QPair<QSet<QgsSymbolLayerId>, QList<MaskSource>>> maskedSymbolLayers;

// First up, create a mapping of layer id to jobs. We need this to filter out any masking
// which refers to layers which we aren't rendering as part of this map render
for ( LayerRenderJob &job : firstPassJobs )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
if ( ! vl )
continue;

layerJobMapping[job.layer->id()] = &job;
}

// next, collate a master list of masked layers, skipping over any which refer to layers
// which don't have a corresponding render job
for ( LayerRenderJob &job : firstPassJobs )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( job.layer );
if ( ! vl )
continue;

// lambda function to factor code for both label masks and symbol layer masks
auto collectMasks = [&]( QHash<QString, QSet<QgsSymbolLayerId>> *masks, QString sourceLayerId, QString ruleId = QString(), int labelMaskId = -1 )
Expand Down Expand Up @@ -531,12 +542,33 @@ LayerRenderJobs QgsMapRendererJob::prepareSecondPassJobs( LayerRenderJobs &first
for ( auto it = labelMasks.begin(); it != labelMasks.end(); it++ )
{
QString labelRule = it.key();
// this is a hash of layer id to masks
QHash<QString, QSet<QgsSymbolLayerId>> masks = it.value();

// filter out masks to those which we are actually rendering
QHash<QString, QSet<QgsSymbolLayerId>> usableMasks;
for ( auto mit = masks.begin(); mit != masks.end(); mit++ )
{
const QString sourceLayerId = mit.key();
// if we aren't rendering the source layer as part of this render, we can't process this mask
if ( !layerJobMapping.contains( sourceLayerId ) )
continue;
else
usableMasks.insert( sourceLayerId, mit.value() );
}

if ( usableMasks.empty() )
continue;

// group layers by QSet<QgsSymbolLayerReference>
QSet<QgsSymbolLayerReference> slRefs;
for ( auto mit = masks.begin(); mit != masks.end(); mit++ )
for ( auto mit = usableMasks.begin(); mit != usableMasks.end(); mit++ )
{
const QString sourceLayerId = mit.key();
// if we aren't rendering the source layer as part of this render, we can't process this mask
if ( !layerJobMapping.contains( sourceLayerId ) )
continue;

for ( auto slIt = mit.value().begin(); slIt != mit.value().end(); slIt++ )
{
slRefs.insert( QgsSymbolLayerReference( mit.key(), *slIt ) );
Expand All @@ -546,7 +578,7 @@ LayerRenderJobs QgsMapRendererJob::prepareSecondPassJobs( LayerRenderJobs &first
int labelMaskId = labelJob.maskIdProvider.insertLabelLayer( vl->id(), it.key(), slRefs );

// now collect masks
collectMasks( &masks, vl->id(), labelRule, labelMaskId );
collectMasks( &usableMasks, vl->id(), labelRule, labelMaskId );
}

// collect symbol layer masks
Expand Down

0 comments on commit ffc500d

Please sign in to comment.