diff --git a/src/core/maprenderer/qgsmaprendererjob.cpp b/src/core/maprenderer/qgsmaprendererjob.cpp index b3908140f5d1..fcdb0a0dfaca 100644 --- a/src/core/maprenderer/qgsmaprendererjob.cpp +++ b/src/core/maprenderer/qgsmaprendererjob.cpp @@ -600,8 +600,8 @@ std::vector QgsMapRendererJob::prepareJobs( QPainter *painter, Q const QgsElevationShadingRenderer shadingRenderer = mSettings.elevationShadingRenderer(); // if we can use the cache, let's do it and avoid rendering! - if ( !mSettings.testFlag( Qgis::MapSettingsFlag::ForceVectorOutput ) - && mCache && mCache->hasCacheImage( ml->id() ) ) + const bool canUseCache = !mSettings.testFlag( Qgis::MapSettingsFlag::ForceVectorOutput ) && mCache; + if ( canUseCache && mCache->hasCacheImage( ml->id() ) ) { job.cached = true; job.imageInitialized = true; @@ -630,7 +630,7 @@ std::vector QgsMapRendererJob::prepareJobs( QPainter *painter, Q // If we are drawing with an alternative blending mode then we need to render to a separate image // before compositing this on the map. This effectively flattens the layer and prevents // blending occurring between objects on the layer - if ( mCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) ) + if ( canUseCache || ( !painter && !deferredPainterSet ) || ( job.renderer && job.renderer->forceRasterRender() ) ) { // Flattened image for drawing when a blending mode is set job.context()->setPainter( allocateImageAndPainter( ml->id(), job.img, job.context() ) ); @@ -653,7 +653,7 @@ std::vector QgsMapRendererJob::prepareJobs( QPainter *painter, Q if ( ( job.renderer->flags() & Qgis::MapLayerRendererFlag::RenderPartialOutputs ) && ( mSettings.flags() & Qgis::MapSettingsFlag::RenderPartialOutput ) ) { - if ( mCache && ( job.renderer->flags() & Qgis::MapLayerRendererFlag::RenderPartialOutputOverPreviousCachedImage ) && mCache->hasAnyCacheImage( job.layerId + QStringLiteral( "_preview" ) ) ) + if ( canUseCache && ( job.renderer->flags() & Qgis::MapLayerRendererFlag::RenderPartialOutputOverPreviousCachedImage ) && mCache->hasAnyCacheImage( job.layerId + QStringLiteral( "_preview" ) ) ) { const QImage cachedImage = mCache->transformedCacheImage( job.layerId + QStringLiteral( "_preview" ), mSettings.mapToPixel() ); if ( !cachedImage.isNull() ) diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index 89fe664299fa..fa41e5177416 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -1535,8 +1535,10 @@ bool QgsTemplatedLineSymbolLayerBase::canCauseArtifactsBetweenAdjacentTiles() co || ( mPlacements & Qgis::MarkerLinePlacement::SegmentCenter ); } -void QgsTemplatedLineSymbolLayerBase::startFeatureRender( const QgsFeature &, QgsRenderContext & ) +void QgsTemplatedLineSymbolLayerBase::startFeatureRender( const QgsFeature &, QgsRenderContext &context ) { + installMasks( context, true ); + mRenderingFeature = true; mHasRenderedFirstPart = false; } @@ -1545,7 +1547,10 @@ void QgsTemplatedLineSymbolLayerBase::stopFeatureRender( const QgsFeature &featu { mRenderingFeature = false; if ( mPlaceOnEveryPart || !( mPlacements & Qgis::MarkerLinePlacement::LastVertex ) ) + { + removeMasks( context, true ); return; + } const double prevOpacity = subSymbol()->opacity(); subSymbol()->setOpacity( prevOpacity * mFeatureSymbolOpacity ); @@ -1554,6 +1559,8 @@ void QgsTemplatedLineSymbolLayerBase::stopFeatureRender( const QgsFeature &featu renderSymbol( mFinalVertex, &feature, context, -1, mCurrentFeatureIsSelected ); mFeatureSymbolOpacity = 1; subSymbol()->setOpacity( prevOpacity ); + + removeMasks( context, true ); } void QgsTemplatedLineSymbolLayerBase::copyTemplateSymbolProperties( QgsTemplatedLineSymbolLayerBase *destLayer ) const @@ -4099,4 +4106,3 @@ Qgis::RenderUnit QgsFilledLineSymbolLayer::outputUnit() const } return Qgis::RenderUnit::Unknown; } - diff --git a/tests/src/python/test_selective_masking.py b/tests/src/python/test_selective_masking.py index 44aa7cb2b621..e417e8fe5392 100644 --- a/tests/src/python/test_selective_masking.py +++ b/tests/src/python/test_selective_masking.py @@ -26,13 +26,16 @@ QgsLayoutItemMap, QgsLayoutItemPage, QgsLayoutSize, + QgsLineSymbol, QgsMapRendererCache, QgsMapRendererCustomPainterJob, QgsMapRendererParallelJob, QgsMapRendererSequentialJob, QgsMapSettings, + QgsMarkerLineSymbolLayer, QgsMarkerSymbol, QgsMaskMarkerSymbolLayer, + QgsNullSymbolRenderer, QgsOuterGlowEffect, QgsPalLayerSettings, QgsPathResolver, @@ -1346,6 +1349,32 @@ def test_layout_export_svg_marker_masking(self): # no rasters self.check_layout_export("layout_export_svg_marker_masking", 0, [self.points_layer, self.lines_layer]) + def test_markerline_masked(self): + """ + Test a layout export where a QgsMarkerLineSymbolLayer is masked + """ + + sl = QgsMarkerLineSymbolLayer(True, 7) + circle_symbol = QgsMarkerSymbol.createSimple({'size': '3'}) + sl.setSubSymbol(circle_symbol) + + symbol = QgsLineSymbol.createSimple({}) + symbol.changeSymbolLayer(0, sl) + self.lines_layer.setRenderer(QgsSingleSymbolRenderer(symbol)) + self.polys_layer.setRenderer(QgsNullSymbolRenderer()) + + label_settings = self.polys_layer.labeling().settings() + fmt = label_settings.format() + # enable a mask + fmt.mask().setEnabled(True) + fmt.mask().setSize(4.0) + # and mask other symbol layers underneath + fmt.mask().setMaskedSymbolLayers([QgsSymbolLayerReference(self.lines_layer.id(), sl.id())]) + label_settings.setFormat(fmt) + self.polys_layer.labeling().setSettings(label_settings) + + self.check_layout_export("layout_export_markerline_masked", 0, [self.polys_layer, self.lines_layer]) + class TestSelectiveMaskingQPainterPathBackend(QgisTestCase, SelectiveMaskingTestBase): """ diff --git a/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked.png b/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked.png new file mode 100644 index 000000000000..fc57d6cac9ff Binary files /dev/null and b/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked_mask.png b/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked_mask.png new file mode 100644 index 000000000000..6ea2f0008eeb Binary files /dev/null and b/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked_mask.png differ