diff --git a/python/PyQt6/core/auto_additions/qgis.py b/python/PyQt6/core/auto_additions/qgis.py index 6b45e2385117..fbd0ce5c41e1 100644 --- a/python/PyQt6/core/auto_additions/qgis.py +++ b/python/PyQt6/core/auto_additions/qgis.py @@ -634,7 +634,10 @@ QgsSymbol.DynamicRotation = Qgis.SymbolRenderHint.DynamicRotation QgsSymbol.DynamicRotation.is_monkey_patched = True QgsSymbol.DynamicRotation.__doc__ = "Rotation of symbol may be changed during rendering and symbol should not be cached" -Qgis.SymbolRenderHint.__doc__ = "Flags controlling behavior of symbols during rendering\n\n.. versionadded:: 3.20\n\n" + '* ``DynamicRotation``: ' + Qgis.SymbolRenderHint.DynamicRotation.__doc__ +QgsSymbol.IsSymbolLayerSubSymbol = Qgis.SymbolRenderHint.IsSymbolLayerSubSymbol +QgsSymbol.IsSymbolLayerSubSymbol.is_monkey_patched = True +QgsSymbol.IsSymbolLayerSubSymbol.__doc__ = "Symbol is being rendered as a sub-symbol of a QgsSymbolLayer (since QGIS 3.38)" +Qgis.SymbolRenderHint.__doc__ = "Flags controlling behavior of symbols during rendering\n\n.. versionadded:: 3.20\n\n" + '* ``DynamicRotation``: ' + Qgis.SymbolRenderHint.DynamicRotation.__doc__ + '\n' + '* ``IsSymbolLayerSubSymbol``: ' + Qgis.SymbolRenderHint.IsSymbolLayerSubSymbol.__doc__ # -- Qgis.SymbolRenderHint.baseClass = Qgis Qgis.SymbolRenderHints = lambda flags=0: Qgis.SymbolRenderHint(flags) @@ -669,7 +672,8 @@ SymbolPreviewFlags = Qgis # dirty hack since SIP seems to introduce the flags in module # monkey patching scoped based enum Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ = "If present, indicates that features should never be clipped to the map extent during rendering" -Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. note::\n\n These differ from Qgis.SymbolLayerUserFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ +Qgis.SymbolLayerFlag.CanCalculateMaskGeometryPerFeature.__doc__ = "If present, indicates that mask geometry can safely be calculated per feature for the symbol layer. This avoids using the entire symbol layer's mask geometry for every feature rendered, considerably simplifying vector exports and resulting in much smaller file sizes. (Since QGIS 3.38)" +Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. note::\n\n These differ from Qgis.SymbolLayerUserFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ + '\n' + '* ``CanCalculateMaskGeometryPerFeature``: ' + Qgis.SymbolLayerFlag.CanCalculateMaskGeometryPerFeature.__doc__ # -- Qgis.SymbolLayerFlag.baseClass = Qgis Qgis.SymbolLayerFlags = lambda flags=0: Qgis.SymbolLayerFlag(flags) @@ -2308,7 +2312,10 @@ QgsMapSettings.RecordProfile = Qgis.MapSettingsFlag.RecordProfile QgsMapSettings.RecordProfile.is_monkey_patched = True QgsMapSettings.RecordProfile.__doc__ = "Enable run-time profiling while rendering (since QGIS 3.34)" -Qgis.MapSettingsFlag.__doc__ = "Flags which adjust the way maps are rendered.\n\n.. versionadded:: 3.22\n\n" + '* ``Antialiasing``: ' + Qgis.MapSettingsFlag.Antialiasing.__doc__ + '\n' + '* ``DrawEditingInfo``: ' + Qgis.MapSettingsFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.MapSettingsFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.MapSettingsFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``DrawLabeling``: ' + Qgis.MapSettingsFlag.DrawLabeling.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.MapSettingsFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.MapSettingsFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.MapSettingsFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.MapSettingsFlag.RenderMapTile.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.MapSettingsFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.MapSettingsFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.MapSettingsFlag.RenderBlocking.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.MapSettingsFlag.LosslessImageRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.MapSettingsFlag.Render3DMap.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.MapSettingsFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.MapSettingsFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``ForceRasterMasks``: ' + Qgis.MapSettingsFlag.ForceRasterMasks.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.MapSettingsFlag.RecordProfile.__doc__ +QgsMapSettings.AlwaysUseGlobalMasks = Qgis.MapSettingsFlag.AlwaysUseGlobalMasks +QgsMapSettings.AlwaysUseGlobalMasks.is_monkey_patched = True +QgsMapSettings.AlwaysUseGlobalMasks.__doc__ = "When applying clipping paths for selective masking, always use global (\"entire map\") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38)" +Qgis.MapSettingsFlag.__doc__ = "Flags which adjust the way maps are rendered.\n\n.. versionadded:: 3.22\n\n" + '* ``Antialiasing``: ' + Qgis.MapSettingsFlag.Antialiasing.__doc__ + '\n' + '* ``DrawEditingInfo``: ' + Qgis.MapSettingsFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.MapSettingsFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.MapSettingsFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``DrawLabeling``: ' + Qgis.MapSettingsFlag.DrawLabeling.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.MapSettingsFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.MapSettingsFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.MapSettingsFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.MapSettingsFlag.RenderMapTile.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.MapSettingsFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.MapSettingsFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.MapSettingsFlag.RenderBlocking.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.MapSettingsFlag.LosslessImageRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.MapSettingsFlag.Render3DMap.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.MapSettingsFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.MapSettingsFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``ForceRasterMasks``: ' + Qgis.MapSettingsFlag.ForceRasterMasks.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.MapSettingsFlag.RecordProfile.__doc__ + '\n' + '* ``AlwaysUseGlobalMasks``: ' + Qgis.MapSettingsFlag.AlwaysUseGlobalMasks.__doc__ # -- Qgis.MapSettingsFlags = lambda flags=0: Qgis.MapSettingsFlag(flags) QgsMapSettings.Flags = Qgis.MapSettingsFlags @@ -2377,7 +2384,10 @@ QgsRenderContext.RecordProfile = Qgis.RenderContextFlag.RecordProfile QgsRenderContext.RecordProfile.is_monkey_patched = True QgsRenderContext.RecordProfile.__doc__ = "Enable run-time profiling while rendering (since QGIS 3.34)" -Qgis.RenderContextFlag.__doc__ = "Flags which affect rendering operations.\n\n.. versionadded:: 3.22\n\n" + '* ``DrawEditingInfo``: ' + Qgis.RenderContextFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.RenderContextFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.RenderContextFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.RenderContextFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.RenderContextFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.RenderContextFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.RenderContextFlag.RenderMapTile.__doc__ + '\n' + '* ``Antialiasing``: ' + Qgis.RenderContextFlag.Antialiasing.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.RenderContextFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.RenderContextFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.RenderContextFlag.RenderBlocking.__doc__ + '\n' + '* ``RenderSymbolPreview``: ' + Qgis.RenderContextFlag.RenderSymbolPreview.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.RenderContextFlag.LosslessImageRendering.__doc__ + '\n' + '* ``ApplyScalingWorkaroundForTextRendering``: ' + Qgis.RenderContextFlag.ApplyScalingWorkaroundForTextRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.RenderContextFlag.Render3DMap.__doc__ + '\n' + '* ``ApplyClipAfterReprojection``: ' + Qgis.RenderContextFlag.ApplyClipAfterReprojection.__doc__ + '\n' + '* ``RenderingSubSymbol``: ' + Qgis.RenderContextFlag.RenderingSubSymbol.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.RenderContextFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.RenderContextFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.RenderContextFlag.RecordProfile.__doc__ +QgsRenderContext.AlwaysUseGlobalMasks = Qgis.RenderContextFlag.AlwaysUseGlobalMasks +QgsRenderContext.AlwaysUseGlobalMasks.is_monkey_patched = True +QgsRenderContext.AlwaysUseGlobalMasks.__doc__ = "When applying clipping paths for selective masking, always use global (\"entire map\") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38)" +Qgis.RenderContextFlag.__doc__ = "Flags which affect rendering operations.\n\n.. versionadded:: 3.22\n\n" + '* ``DrawEditingInfo``: ' + Qgis.RenderContextFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.RenderContextFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.RenderContextFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.RenderContextFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.RenderContextFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.RenderContextFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.RenderContextFlag.RenderMapTile.__doc__ + '\n' + '* ``Antialiasing``: ' + Qgis.RenderContextFlag.Antialiasing.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.RenderContextFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.RenderContextFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.RenderContextFlag.RenderBlocking.__doc__ + '\n' + '* ``RenderSymbolPreview``: ' + Qgis.RenderContextFlag.RenderSymbolPreview.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.RenderContextFlag.LosslessImageRendering.__doc__ + '\n' + '* ``ApplyScalingWorkaroundForTextRendering``: ' + Qgis.RenderContextFlag.ApplyScalingWorkaroundForTextRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.RenderContextFlag.Render3DMap.__doc__ + '\n' + '* ``ApplyClipAfterReprojection``: ' + Qgis.RenderContextFlag.ApplyClipAfterReprojection.__doc__ + '\n' + '* ``RenderingSubSymbol``: ' + Qgis.RenderContextFlag.RenderingSubSymbol.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.RenderContextFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.RenderContextFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.RenderContextFlag.RecordProfile.__doc__ + '\n' + '* ``AlwaysUseGlobalMasks``: ' + Qgis.RenderContextFlag.AlwaysUseGlobalMasks.__doc__ # -- Qgis.RenderContextFlags = lambda flags=0: Qgis.RenderContextFlag(flags) QgsRenderContext.Flags = Qgis.RenderContextFlags diff --git a/python/PyQt6/core/auto_additions/qgslayoutrendercontext.py b/python/PyQt6/core/auto_additions/qgslayoutrendercontext.py index abd5decf0c9e..443b50e6d12c 100644 --- a/python/PyQt6/core/auto_additions/qgslayoutrendercontext.py +++ b/python/PyQt6/core/auto_additions/qgslayoutrendercontext.py @@ -10,4 +10,5 @@ QgsLayoutRenderContext.FlagRenderLabelsByMapLayer = QgsLayoutRenderContext.Flag.FlagRenderLabelsByMapLayer QgsLayoutRenderContext.FlagLosslessImageRendering = QgsLayoutRenderContext.Flag.FlagLosslessImageRendering QgsLayoutRenderContext.FlagSynchronousLegendGraphics = QgsLayoutRenderContext.Flag.FlagSynchronousLegendGraphics +QgsLayoutRenderContext.FlagAlwaysUseGlobalMasks = QgsLayoutRenderContext.Flag.FlagAlwaysUseGlobalMasks QgsLayoutRenderContext.Flags = lambda flags=0: QgsLayoutRenderContext.Flag(flags) diff --git a/python/PyQt6/core/auto_generated/layout/qgslayoutrendercontext.sip.in b/python/PyQt6/core/auto_generated/layout/qgslayoutrendercontext.sip.in index 8fcc1222baff..dea066d7c48a 100644 --- a/python/PyQt6/core/auto_generated/layout/qgslayoutrendercontext.sip.in +++ b/python/PyQt6/core/auto_generated/layout/qgslayoutrendercontext.sip.in @@ -31,7 +31,8 @@ Stores information relating to the current rendering settings for a layout. FlagDisableTiledRasterLayerRenders, FlagRenderLabelsByMapLayer, FlagLosslessImageRendering, - FlagSynchronousLegendGraphics + FlagSynchronousLegendGraphics, + FlagAlwaysUseGlobalMasks, }; typedef QFlags Flags; diff --git a/python/PyQt6/core/auto_generated/qgis.sip.in b/python/PyQt6/core/auto_generated/qgis.sip.in index 0ddc173a1abc..5a98ae351d69 100644 --- a/python/PyQt6/core/auto_generated/qgis.sip.in +++ b/python/PyQt6/core/auto_generated/qgis.sip.in @@ -362,6 +362,7 @@ The development version enum class SymbolRenderHint /BaseType=IntFlag/ { DynamicRotation, + IsSymbolLayerSubSymbol, }; typedef QFlags SymbolRenderHints; @@ -389,6 +390,7 @@ The development version enum class SymbolLayerFlag /BaseType=IntFlag/ { DisableFeatureClipping, + CanCalculateMaskGeometryPerFeature, }; typedef QFlags SymbolLayerFlags; @@ -1360,6 +1362,7 @@ The development version SkipSymbolRendering, ForceRasterMasks, RecordProfile, + AlwaysUseGlobalMasks, }; typedef QFlags MapSettingsFlags; @@ -1386,6 +1389,7 @@ The development version HighQualityImageTransforms, SkipSymbolRendering, RecordProfile, + AlwaysUseGlobalMasks, }; typedef QFlags RenderContextFlags; diff --git a/python/PyQt6/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in index 56fea467e556..a0db8f4ec749 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in @@ -67,6 +67,8 @@ Creates the symbol layer virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgsfillsymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgsfillsymbollayer.sip.in index 910903ef95b9..0fb1ffc4aa7f 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgsfillsymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgsfillsymbollayer.sip.in @@ -41,25 +41,20 @@ Caller takes ownership of the returned symbol layer. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void startRender( QgsSymbolRenderContext &context ); - virtual void stopRender( QgsSymbolRenderContext &context ); - virtual void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ); - virtual QVariantMap properties() const; - virtual QgsSimpleFillSymbolLayer *clone() const /Factory/; - virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const; - virtual QString ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const; @@ -68,7 +63,6 @@ Caller takes ownership of the returned symbol layer. virtual QColor strokeColor() const; virtual void setStrokeColor( const QColor &strokeColor ); - virtual QColor fillColor() const; virtual void setFillColor( const QColor &color ); @@ -225,6 +219,8 @@ Caller takes ownership of the returned symbol layer. %End + virtual Qgis::SymbolLayerFlags flags() const; + virtual QString layerType() const; virtual void startRender( QgsSymbolRenderContext &context ); @@ -498,6 +494,8 @@ Caller takes ownership of the returned symbol layer. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -915,6 +913,8 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ); virtual void startRender( QgsSymbolRenderContext &context ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgslinesymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgslinesymbollayer.sip.in index 4e2230545a17..690bc7f84d33 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgslinesymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgslinesymbollayer.sip.in @@ -48,6 +48,8 @@ Creates a new QgsSimpleLineSymbolLayer from an SLD XML DOM ``element``. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -1358,6 +1360,8 @@ Set the line opacity. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -1421,6 +1425,8 @@ serialized in the ``properties`` map (corresponding to the output from virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in index 4da6839f841e..9f21fb06eb29 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in @@ -214,6 +214,8 @@ Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); @@ -562,24 +564,20 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void startRender( QgsSymbolRenderContext &context ); - virtual void stopRender( QgsSymbolRenderContext &context ); - virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); - virtual QVariantMap properties() const; virtual bool usesMapUnits() const; - virtual QgsSvgMarkerSymbolLayer *clone() const /Factory/; - virtual void writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const; @@ -773,6 +771,7 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); @@ -953,6 +952,8 @@ required fonts are not available on the system. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in b/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in index fcf655854261..4c0cbc8d89f8 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgssymbollayer.sip.in @@ -663,6 +663,25 @@ This id has to be unique in the whole project Returns symbol layer identifier This id is unique in the whole project +.. versionadded:: 3.30 +%End + + bool installMasks( QgsRenderContext &context, bool recursive, const QRectF &rect = QRectF() ); +%Docstring +When rendering, install masks on ``context`` painter. + +If ``recursive`` is ``True`` masks are installed recursively for all children symbol layers. + +Since QGIS 3.38 the ``rect`` argument can be used to specify a target bounds (in painter coordinates) +for mask geometries. Only mask geometries which intersect ``rect`` will be installed. + +:return: ``True`` if any masks were installed (since QGIS 3.38) + +.. seealso:: :py:func:`prepareMasks` + +.. seealso:: :py:func:`removeMasks` + + .. versionadded:: 3.30 %End @@ -700,18 +719,6 @@ Copies all data defined properties of this layer to another symbol layer. Copies paint effect of this layer to another symbol layer :param destLayer: destination layer -%End - - void installMasks( QgsRenderContext &context, bool recursive ); -%Docstring -When rendering, install masks on ``context`` painter -if ``recursive`` is ``True`` masks are installed recursively for all children symbol layers - -.. seealso:: :py:func:`prepareMasks` - -.. seealso:: :py:func:`removeMasks` - -.. versionadded:: 3.30 %End void removeMasks( QgsRenderContext &context, bool recursive ); diff --git a/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in b/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in index 167897841a4f..4da46e560c18 100644 --- a/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in +++ b/python/PyQt6/core/auto_generated/symbology/qgssymbollayerutils.sip.in @@ -1001,6 +1001,18 @@ Regenerate recursively unique id from ``symbolLayer`` and its children .. versionadded:: 3.30 %End + static QVector< QgsGeometry > collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ); +%Docstring +Returns a list of the symbol layer clip geometries to be used for the symbol layer with the specified +ID. + +The ``bounds`` argument specifies the target bounds (in painter coordinates) for matching geometries. Only mask +geometries which intersect ``bounds`` will be returned. If ``bounds`` is a null QRectF then all clip geometries +for the symbol layer will be returned. + +.. versionadded:: 3.38 +%End + }; diff --git a/python/core/auto_additions/qgis.py b/python/core/auto_additions/qgis.py index 3f15916b53dd..077aa510efcc 100644 --- a/python/core/auto_additions/qgis.py +++ b/python/core/auto_additions/qgis.py @@ -621,7 +621,10 @@ QgsSymbol.DynamicRotation = Qgis.SymbolRenderHint.DynamicRotation QgsSymbol.DynamicRotation.is_monkey_patched = True QgsSymbol.DynamicRotation.__doc__ = "Rotation of symbol may be changed during rendering and symbol should not be cached" -Qgis.SymbolRenderHint.__doc__ = "Flags controlling behavior of symbols during rendering\n\n.. versionadded:: 3.20\n\n" + '* ``DynamicRotation``: ' + Qgis.SymbolRenderHint.DynamicRotation.__doc__ +QgsSymbol.IsSymbolLayerSubSymbol = Qgis.SymbolRenderHint.IsSymbolLayerSubSymbol +QgsSymbol.IsSymbolLayerSubSymbol.is_monkey_patched = True +QgsSymbol.IsSymbolLayerSubSymbol.__doc__ = "Symbol is being rendered as a sub-symbol of a QgsSymbolLayer (since QGIS 3.38)" +Qgis.SymbolRenderHint.__doc__ = "Flags controlling behavior of symbols during rendering\n\n.. versionadded:: 3.20\n\n" + '* ``DynamicRotation``: ' + Qgis.SymbolRenderHint.DynamicRotation.__doc__ + '\n' + '* ``IsSymbolLayerSubSymbol``: ' + Qgis.SymbolRenderHint.IsSymbolLayerSubSymbol.__doc__ # -- Qgis.SymbolRenderHint.baseClass = Qgis QgsSymbol.RenderHints = Qgis.SymbolRenderHints @@ -653,7 +656,8 @@ SymbolPreviewFlags = Qgis # dirty hack since SIP seems to introduce the flags in module # monkey patching scoped based enum Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ = "If present, indicates that features should never be clipped to the map extent during rendering" -Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. note::\n\n These differ from Qgis.SymbolLayerUserFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ +Qgis.SymbolLayerFlag.CanCalculateMaskGeometryPerFeature.__doc__ = "If present, indicates that mask geometry can safely be calculated per feature for the symbol layer. This avoids using the entire symbol layer's mask geometry for every feature rendered, considerably simplifying vector exports and resulting in much smaller file sizes. (Since QGIS 3.38)" +Qgis.SymbolLayerFlag.__doc__ = "Flags controlling behavior of symbol layers\n\n.. note::\n\n These differ from Qgis.SymbolLayerUserFlag in that Qgis.SymbolLayerFlag flags are used to reflect the inbuilt properties\n of a symbol layer type, whereas Qgis.SymbolLayerUserFlag are optional, user controlled flags which can be toggled\n for a symbol layer.\n\n.. versionadded:: 3.22\n\n" + '* ``DisableFeatureClipping``: ' + Qgis.SymbolLayerFlag.DisableFeatureClipping.__doc__ + '\n' + '* ``CanCalculateMaskGeometryPerFeature``: ' + Qgis.SymbolLayerFlag.CanCalculateMaskGeometryPerFeature.__doc__ # -- Qgis.SymbolLayerFlag.baseClass = Qgis Qgis.SymbolLayerFlags.baseClass = Qgis @@ -2266,7 +2270,10 @@ QgsMapSettings.RecordProfile = Qgis.MapSettingsFlag.RecordProfile QgsMapSettings.RecordProfile.is_monkey_patched = True QgsMapSettings.RecordProfile.__doc__ = "Enable run-time profiling while rendering (since QGIS 3.34)" -Qgis.MapSettingsFlag.__doc__ = "Flags which adjust the way maps are rendered.\n\n.. versionadded:: 3.22\n\n" + '* ``Antialiasing``: ' + Qgis.MapSettingsFlag.Antialiasing.__doc__ + '\n' + '* ``DrawEditingInfo``: ' + Qgis.MapSettingsFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.MapSettingsFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.MapSettingsFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``DrawLabeling``: ' + Qgis.MapSettingsFlag.DrawLabeling.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.MapSettingsFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.MapSettingsFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.MapSettingsFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.MapSettingsFlag.RenderMapTile.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.MapSettingsFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.MapSettingsFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.MapSettingsFlag.RenderBlocking.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.MapSettingsFlag.LosslessImageRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.MapSettingsFlag.Render3DMap.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.MapSettingsFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.MapSettingsFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``ForceRasterMasks``: ' + Qgis.MapSettingsFlag.ForceRasterMasks.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.MapSettingsFlag.RecordProfile.__doc__ +QgsMapSettings.AlwaysUseGlobalMasks = Qgis.MapSettingsFlag.AlwaysUseGlobalMasks +QgsMapSettings.AlwaysUseGlobalMasks.is_monkey_patched = True +QgsMapSettings.AlwaysUseGlobalMasks.__doc__ = "When applying clipping paths for selective masking, always use global (\"entire map\") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38)" +Qgis.MapSettingsFlag.__doc__ = "Flags which adjust the way maps are rendered.\n\n.. versionadded:: 3.22\n\n" + '* ``Antialiasing``: ' + Qgis.MapSettingsFlag.Antialiasing.__doc__ + '\n' + '* ``DrawEditingInfo``: ' + Qgis.MapSettingsFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.MapSettingsFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.MapSettingsFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``DrawLabeling``: ' + Qgis.MapSettingsFlag.DrawLabeling.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.MapSettingsFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.MapSettingsFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.MapSettingsFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.MapSettingsFlag.RenderMapTile.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.MapSettingsFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.MapSettingsFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.MapSettingsFlag.RenderBlocking.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.MapSettingsFlag.LosslessImageRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.MapSettingsFlag.Render3DMap.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.MapSettingsFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.MapSettingsFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``ForceRasterMasks``: ' + Qgis.MapSettingsFlag.ForceRasterMasks.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.MapSettingsFlag.RecordProfile.__doc__ + '\n' + '* ``AlwaysUseGlobalMasks``: ' + Qgis.MapSettingsFlag.AlwaysUseGlobalMasks.__doc__ # -- QgsMapSettings.Flags = Qgis.MapSettingsFlags Qgis.MapSettingsFlag.baseClass = Qgis @@ -2334,7 +2341,10 @@ QgsRenderContext.RecordProfile = Qgis.RenderContextFlag.RecordProfile QgsRenderContext.RecordProfile.is_monkey_patched = True QgsRenderContext.RecordProfile.__doc__ = "Enable run-time profiling while rendering (since QGIS 3.34)" -Qgis.RenderContextFlag.__doc__ = "Flags which affect rendering operations.\n\n.. versionadded:: 3.22\n\n" + '* ``DrawEditingInfo``: ' + Qgis.RenderContextFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.RenderContextFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.RenderContextFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.RenderContextFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.RenderContextFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.RenderContextFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.RenderContextFlag.RenderMapTile.__doc__ + '\n' + '* ``Antialiasing``: ' + Qgis.RenderContextFlag.Antialiasing.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.RenderContextFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.RenderContextFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.RenderContextFlag.RenderBlocking.__doc__ + '\n' + '* ``RenderSymbolPreview``: ' + Qgis.RenderContextFlag.RenderSymbolPreview.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.RenderContextFlag.LosslessImageRendering.__doc__ + '\n' + '* ``ApplyScalingWorkaroundForTextRendering``: ' + Qgis.RenderContextFlag.ApplyScalingWorkaroundForTextRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.RenderContextFlag.Render3DMap.__doc__ + '\n' + '* ``ApplyClipAfterReprojection``: ' + Qgis.RenderContextFlag.ApplyClipAfterReprojection.__doc__ + '\n' + '* ``RenderingSubSymbol``: ' + Qgis.RenderContextFlag.RenderingSubSymbol.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.RenderContextFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.RenderContextFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.RenderContextFlag.RecordProfile.__doc__ +QgsRenderContext.AlwaysUseGlobalMasks = Qgis.RenderContextFlag.AlwaysUseGlobalMasks +QgsRenderContext.AlwaysUseGlobalMasks.is_monkey_patched = True +QgsRenderContext.AlwaysUseGlobalMasks.__doc__ = "When applying clipping paths for selective masking, always use global (\"entire map\") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38)" +Qgis.RenderContextFlag.__doc__ = "Flags which affect rendering operations.\n\n.. versionadded:: 3.22\n\n" + '* ``DrawEditingInfo``: ' + Qgis.RenderContextFlag.DrawEditingInfo.__doc__ + '\n' + '* ``ForceVectorOutput``: ' + Qgis.RenderContextFlag.ForceVectorOutput.__doc__ + '\n' + '* ``UseAdvancedEffects``: ' + Qgis.RenderContextFlag.UseAdvancedEffects.__doc__ + '\n' + '* ``UseRenderingOptimization``: ' + Qgis.RenderContextFlag.UseRenderingOptimization.__doc__ + '\n' + '* ``DrawSelection``: ' + Qgis.RenderContextFlag.DrawSelection.__doc__ + '\n' + '* ``DrawSymbolBounds``: ' + Qgis.RenderContextFlag.DrawSymbolBounds.__doc__ + '\n' + '* ``RenderMapTile``: ' + Qgis.RenderContextFlag.RenderMapTile.__doc__ + '\n' + '* ``Antialiasing``: ' + Qgis.RenderContextFlag.Antialiasing.__doc__ + '\n' + '* ``RenderPartialOutput``: ' + Qgis.RenderContextFlag.RenderPartialOutput.__doc__ + '\n' + '* ``RenderPreviewJob``: ' + Qgis.RenderContextFlag.RenderPreviewJob.__doc__ + '\n' + '* ``RenderBlocking``: ' + Qgis.RenderContextFlag.RenderBlocking.__doc__ + '\n' + '* ``RenderSymbolPreview``: ' + Qgis.RenderContextFlag.RenderSymbolPreview.__doc__ + '\n' + '* ``LosslessImageRendering``: ' + Qgis.RenderContextFlag.LosslessImageRendering.__doc__ + '\n' + '* ``ApplyScalingWorkaroundForTextRendering``: ' + Qgis.RenderContextFlag.ApplyScalingWorkaroundForTextRendering.__doc__ + '\n' + '* ``Render3DMap``: ' + Qgis.RenderContextFlag.Render3DMap.__doc__ + '\n' + '* ``ApplyClipAfterReprojection``: ' + Qgis.RenderContextFlag.ApplyClipAfterReprojection.__doc__ + '\n' + '* ``RenderingSubSymbol``: ' + Qgis.RenderContextFlag.RenderingSubSymbol.__doc__ + '\n' + '* ``HighQualityImageTransforms``: ' + Qgis.RenderContextFlag.HighQualityImageTransforms.__doc__ + '\n' + '* ``SkipSymbolRendering``: ' + Qgis.RenderContextFlag.SkipSymbolRendering.__doc__ + '\n' + '* ``RecordProfile``: ' + Qgis.RenderContextFlag.RecordProfile.__doc__ + '\n' + '* ``AlwaysUseGlobalMasks``: ' + Qgis.RenderContextFlag.AlwaysUseGlobalMasks.__doc__ # -- QgsRenderContext.Flags = Qgis.RenderContextFlags Qgis.RenderContextFlag.baseClass = Qgis diff --git a/python/core/auto_generated/layout/qgslayoutrendercontext.sip.in b/python/core/auto_generated/layout/qgslayoutrendercontext.sip.in index 38079a9d8665..2a4257bc1627 100644 --- a/python/core/auto_generated/layout/qgslayoutrendercontext.sip.in +++ b/python/core/auto_generated/layout/qgslayoutrendercontext.sip.in @@ -31,7 +31,8 @@ Stores information relating to the current rendering settings for a layout. FlagDisableTiledRasterLayerRenders, FlagRenderLabelsByMapLayer, FlagLosslessImageRendering, - FlagSynchronousLegendGraphics + FlagSynchronousLegendGraphics, + FlagAlwaysUseGlobalMasks, }; typedef QFlags Flags; diff --git a/python/core/auto_generated/qgis.sip.in b/python/core/auto_generated/qgis.sip.in index 65e2c997f4cc..c3c073a12eb6 100644 --- a/python/core/auto_generated/qgis.sip.in +++ b/python/core/auto_generated/qgis.sip.in @@ -362,6 +362,7 @@ The development version enum class SymbolRenderHint { DynamicRotation, + IsSymbolLayerSubSymbol, }; typedef QFlags SymbolRenderHints; @@ -389,6 +390,7 @@ The development version enum class SymbolLayerFlag { DisableFeatureClipping, + CanCalculateMaskGeometryPerFeature, }; typedef QFlags SymbolLayerFlags; @@ -1360,6 +1362,7 @@ The development version SkipSymbolRendering, ForceRasterMasks, RecordProfile, + AlwaysUseGlobalMasks, }; typedef QFlags MapSettingsFlags; @@ -1386,6 +1389,7 @@ The development version HighQualityImageTransforms, SkipSymbolRendering, RecordProfile, + AlwaysUseGlobalMasks, }; typedef QFlags RenderContextFlags; diff --git a/python/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in b/python/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in index 9d3af4bcdf20..80f04eabbbb7 100644 --- a/python/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgsellipsesymbollayer.sip.in @@ -67,6 +67,8 @@ Creates the symbol layer virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); diff --git a/python/core/auto_generated/symbology/qgsfillsymbollayer.sip.in b/python/core/auto_generated/symbology/qgsfillsymbollayer.sip.in index 910903ef95b9..0fb1ffc4aa7f 100644 --- a/python/core/auto_generated/symbology/qgsfillsymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgsfillsymbollayer.sip.in @@ -41,25 +41,20 @@ Caller takes ownership of the returned symbol layer. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void startRender( QgsSymbolRenderContext &context ); - virtual void stopRender( QgsSymbolRenderContext &context ); - virtual void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ); - virtual QVariantMap properties() const; - virtual QgsSimpleFillSymbolLayer *clone() const /Factory/; - virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const; - virtual QString ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const; @@ -68,7 +63,6 @@ Caller takes ownership of the returned symbol layer. virtual QColor strokeColor() const; virtual void setStrokeColor( const QColor &strokeColor ); - virtual QColor fillColor() const; virtual void setFillColor( const QColor &color ); @@ -225,6 +219,8 @@ Caller takes ownership of the returned symbol layer. %End + virtual Qgis::SymbolLayerFlags flags() const; + virtual QString layerType() const; virtual void startRender( QgsSymbolRenderContext &context ); @@ -498,6 +494,8 @@ Caller takes ownership of the returned symbol layer. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -915,6 +913,8 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ); virtual void startRender( QgsSymbolRenderContext &context ); diff --git a/python/core/auto_generated/symbology/qgslinesymbollayer.sip.in b/python/core/auto_generated/symbology/qgslinesymbollayer.sip.in index 4e2230545a17..690bc7f84d33 100644 --- a/python/core/auto_generated/symbology/qgslinesymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgslinesymbollayer.sip.in @@ -48,6 +48,8 @@ Creates a new QgsSimpleLineSymbolLayer from an SLD XML DOM ``element``. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -1358,6 +1360,8 @@ Set the line opacity. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); @@ -1421,6 +1425,8 @@ serialized in the ``properties`` map (corresponding to the output from virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void stopRender( QgsSymbolRenderContext &context ); diff --git a/python/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in b/python/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in index 4da6839f841e..9f21fb06eb29 100644 --- a/python/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgsmarkersymbollayer.sip.in @@ -214,6 +214,8 @@ Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); @@ -562,24 +564,20 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void startRender( QgsSymbolRenderContext &context ); - virtual void stopRender( QgsSymbolRenderContext &context ); - virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); - virtual QVariantMap properties() const; virtual bool usesMapUnits() const; - virtual QgsSvgMarkerSymbolLayer *clone() const /Factory/; - virtual void writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const; @@ -773,6 +771,7 @@ Used internally when reading/writing symbols. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; virtual void renderPoint( QPointF point, QgsSymbolRenderContext &context ); @@ -953,6 +952,8 @@ required fonts are not available on the system. virtual QString layerType() const; + virtual Qgis::SymbolLayerFlags flags() const; + virtual void startRender( QgsSymbolRenderContext &context ); diff --git a/python/core/auto_generated/symbology/qgssymbollayer.sip.in b/python/core/auto_generated/symbology/qgssymbollayer.sip.in index aa4f1ed80d6a..14450dbcbfc1 100644 --- a/python/core/auto_generated/symbology/qgssymbollayer.sip.in +++ b/python/core/auto_generated/symbology/qgssymbollayer.sip.in @@ -663,6 +663,25 @@ This id has to be unique in the whole project Returns symbol layer identifier This id is unique in the whole project +.. versionadded:: 3.30 +%End + + bool installMasks( QgsRenderContext &context, bool recursive, const QRectF &rect = QRectF() ); +%Docstring +When rendering, install masks on ``context`` painter. + +If ``recursive`` is ``True`` masks are installed recursively for all children symbol layers. + +Since QGIS 3.38 the ``rect`` argument can be used to specify a target bounds (in painter coordinates) +for mask geometries. Only mask geometries which intersect ``rect`` will be installed. + +:return: ``True`` if any masks were installed (since QGIS 3.38) + +.. seealso:: :py:func:`prepareMasks` + +.. seealso:: :py:func:`removeMasks` + + .. versionadded:: 3.30 %End @@ -700,18 +719,6 @@ Copies all data defined properties of this layer to another symbol layer. Copies paint effect of this layer to another symbol layer :param destLayer: destination layer -%End - - void installMasks( QgsRenderContext &context, bool recursive ); -%Docstring -When rendering, install masks on ``context`` painter -if ``recursive`` is ``True`` masks are installed recursively for all children symbol layers - -.. seealso:: :py:func:`prepareMasks` - -.. seealso:: :py:func:`removeMasks` - -.. versionadded:: 3.30 %End void removeMasks( QgsRenderContext &context, bool recursive ); diff --git a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in index 167897841a4f..4da46e560c18 100644 --- a/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in +++ b/python/core/auto_generated/symbology/qgssymbollayerutils.sip.in @@ -1001,6 +1001,18 @@ Regenerate recursively unique id from ``symbolLayer`` and its children .. versionadded:: 3.30 %End + static QVector< QgsGeometry > collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ); +%Docstring +Returns a list of the symbol layer clip geometries to be used for the symbol layer with the specified +ID. + +The ``bounds`` argument specifies the target bounds (in painter coordinates) for matching geometries. Only mask +geometries which intersect ``bounds`` will be returned. If ``bounds`` is a null QRectF then all clip geometries +for the symbol layer will be returned. + +.. versionadded:: 3.38 +%End + }; diff --git a/src/core/layout/qgslayoutitemmap.cpp b/src/core/layout/qgslayoutitemmap.cpp index 1da98a1148ff..0a7d4b3f71bf 100644 --- a/src/core/layout/qgslayoutitemmap.cpp +++ b/src/core/layout/qgslayoutitemmap.cpp @@ -1746,6 +1746,7 @@ QgsMapSettings QgsLayoutItemMap::mapSettings( const QgsRectangle &extent, QSizeF jobMapSettings.setFlag( Qgis::MapSettingsFlag::DrawSelection, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagDrawSelection ); jobMapSettings.setFlag( Qgis::MapSettingsFlag::RenderPartialOutput, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagDisableTiledRasterLayerRenders ); jobMapSettings.setFlag( Qgis::MapSettingsFlag::UseAdvancedEffects, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagUseAdvancedEffects ); + jobMapSettings.setFlag( Qgis::MapSettingsFlag::AlwaysUseGlobalMasks, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagAlwaysUseGlobalMasks ); jobMapSettings.setTransformContext( mLayout->project()->transformContext() ); jobMapSettings.setPathResolver( mLayout->project()->pathResolver() ); diff --git a/src/core/layout/qgslayoutrendercontext.h b/src/core/layout/qgslayoutrendercontext.h index 6c1e1f3bd522..fb4ac1511417 100644 --- a/src/core/layout/qgslayoutrendercontext.h +++ b/src/core/layout/qgslayoutrendercontext.h @@ -53,7 +53,8 @@ class CORE_EXPORT QgsLayoutRenderContext : public QObject FlagDisableTiledRasterLayerRenders = 1 << 8, //!< If set, then raster layers will not be drawn as separate tiles. This may improve the appearance in exported files, at the cost of much higher memory usage during exports. FlagRenderLabelsByMapLayer = 1 << 9, //!< When rendering map items to multi-layered exports, render labels belonging to different layers into separate export layers FlagLosslessImageRendering = 1 << 10, //!< Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some destination devices (e.g. PDF). - FlagSynchronousLegendGraphics = 1 << 11 //!< Query legend graphics synchronously. + FlagSynchronousLegendGraphics = 1 << 11, //!< Query legend graphics synchronously. + FlagAlwaysUseGlobalMasks = 1 << 12, //!< When applying clipping paths for selective masking, always use global ("entire map") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex layout exports in all current Qt versions. This flag only applies to vector layout exports. (Since QGIS 3.38) }; Q_DECLARE_FLAGS( Flags, Flag ) diff --git a/src/core/qgis.h b/src/core/qgis.h index a42b247e4589..b9de4cebba1f 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -577,7 +577,8 @@ class CORE_EXPORT Qgis */ enum class SymbolRenderHint SIP_MONKEYPATCH_SCOPEENUM_UNNEST( QgsSymbol, RenderHint ) : int SIP_ENUM_BASETYPE( IntFlag ) { - DynamicRotation = 2, //!< Rotation of symbol may be changed during rendering and symbol should not be cached + DynamicRotation = 1 << 1, //!< Rotation of symbol may be changed during rendering and symbol should not be cached + IsSymbolLayerSubSymbol = 1 << 2, //!< Symbol is being rendered as a sub-symbol of a QgsSymbolLayer (since QGIS 3.38) }; Q_ENUM( SymbolRenderHint ) //! Symbol render hints @@ -636,6 +637,7 @@ class CORE_EXPORT Qgis enum class SymbolLayerFlag : int SIP_ENUM_BASETYPE( IntFlag ) { DisableFeatureClipping = 1 << 0, //!< If present, indicates that features should never be clipped to the map extent during rendering + CanCalculateMaskGeometryPerFeature = 1 << 1, //!< If present, indicates that mask geometry can safely be calculated per feature for the symbol layer. This avoids using the entire symbol layer's mask geometry for every feature rendered, considerably simplifying vector exports and resulting in much smaller file sizes. (Since QGIS 3.38) }; Q_ENUM( SymbolLayerFlag ) //! Symbol layer flags @@ -2334,6 +2336,7 @@ class CORE_EXPORT Qgis SkipSymbolRendering = 0x8000, //!< Disable symbol rendering while still drawing labels if enabled (since QGIS 3.24) ForceRasterMasks = 0x10000, //!< Force symbol masking to be applied using a raster method. This is considerably faster when compared to the vector method, but results in a inferior quality output. (since QGIS 3.26.1) RecordProfile = 0x20000, //!< Enable run-time profiling while rendering (since QGIS 3.34) + AlwaysUseGlobalMasks = 0x40000, //!< When applying clipping paths for selective masking, always use global ("entire map") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38) }; //! Map settings flags Q_DECLARE_FLAGS( MapSettingsFlags, MapSettingsFlag ) SIP_MONKEYPATCH_FLAGS_UNNEST( QgsMapSettings, Flags ) @@ -2367,6 +2370,7 @@ class CORE_EXPORT Qgis HighQualityImageTransforms = 0x20000, //!< Enable high quality image transformations, which results in better appearance of scaled or rotated raster components of a map (since QGIS 3.24) SkipSymbolRendering = 0x40000, //!< Disable symbol rendering while still drawing labels if enabled (since QGIS 3.24) RecordProfile = 0x80000, //!< Enable run-time profiling while rendering (since QGIS 3.34) + AlwaysUseGlobalMasks = 0x100000, //!< When applying clipping paths for selective masking, always use global ("entire map") paths, instead of calculating local clipping paths per rendered feature. This results in considerably more complex vector exports in all current Qt versions. This flag only applies to vector map exports. (Since QGIS 3.38) }; //! Render context flags Q_DECLARE_FLAGS( RenderContextFlags, RenderContextFlag ) SIP_MONKEYPATCH_FLAGS_UNNEST( QgsRenderContext, Flags ) diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp index 61a84d0b2d87..7d01a3bf96c3 100644 --- a/src/core/qgsrendercontext.cpp +++ b/src/core/qgsrendercontext.cpp @@ -260,6 +260,7 @@ QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings &mapSet ctx.setFlag( Qgis::RenderContextFlag::HighQualityImageTransforms, mapSettings.testFlag( Qgis::MapSettingsFlag::HighQualityImageTransforms ) ); ctx.setFlag( Qgis::RenderContextFlag::SkipSymbolRendering, mapSettings.testFlag( Qgis::MapSettingsFlag::SkipSymbolRendering ) ); ctx.setFlag( Qgis::RenderContextFlag::RecordProfile, mapSettings.testFlag( Qgis::MapSettingsFlag::RecordProfile ) ); + ctx.setFlag( Qgis::RenderContextFlag::AlwaysUseGlobalMasks, mapSettings.testFlag( Qgis::MapSettingsFlag::AlwaysUseGlobalMasks ) ); ctx.setScaleFactor( mapSettings.outputDpi() / 25.4 ); // = pixels per mm ctx.setDpiTarget( mapSettings.dpiTarget() >= 0.0 ? mapSettings.dpiTarget() : -1.0 ); ctx.setRendererScale( mapSettings.scale() ); diff --git a/src/core/symbology/qgsarrowsymbollayer.cpp b/src/core/symbology/qgsarrowsymbollayer.cpp index fccc1dfb91f8..478a92e54588 100644 --- a/src/core/symbology/qgsarrowsymbollayer.cpp +++ b/src/core/symbology/qgsarrowsymbollayer.cpp @@ -215,7 +215,9 @@ void QgsArrowSymbolLayer::startRender( QgsSymbolRenderContext &context ) mComputedHeadType = headType(); mComputedArrowType = arrowType(); - mSymbol->startRender( context.renderContext() ); + mSymbol->setRenderHints( mSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); + + mSymbol->startRender( context.renderContext(), context.fields() ); } void QgsArrowSymbolLayer::stopRender( QgsSymbolRenderContext &context ) diff --git a/src/core/symbology/qgsellipsesymbollayer.cpp b/src/core/symbology/qgsellipsesymbollayer.cpp index 8a3149637a00..c9a8ea527d5d 100644 --- a/src/core/symbology/qgsellipsesymbollayer.cpp +++ b/src/core/symbology/qgsellipsesymbollayer.cpp @@ -348,6 +348,11 @@ QString QgsEllipseSymbolLayer::layerType() const return QStringLiteral( "EllipseMarker" ); } +Qgis::SymbolLayerFlags QgsEllipseSymbolLayer::flags() const +{ + return QgsMarkerSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsEllipseSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions diff --git a/src/core/symbology/qgsellipsesymbollayer.h b/src/core/symbology/qgsellipsesymbollayer.h index 97e96c86ec51..0f13045c2e32 100644 --- a/src/core/symbology/qgsellipsesymbollayer.h +++ b/src/core/symbology/qgsellipsesymbollayer.h @@ -72,6 +72,7 @@ class CORE_EXPORT QgsEllipseSymbolLayer: public QgsMarkerSymbolLayer void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override; QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; QgsEllipseSymbolLayer *clone() const override SIP_FACTORY; diff --git a/src/core/symbology/qgsfillsymbollayer.cpp b/src/core/symbology/qgsfillsymbollayer.cpp index b3250740676b..a70abb1e8063 100644 --- a/src/core/symbology/qgsfillsymbollayer.cpp +++ b/src/core/symbology/qgsfillsymbollayer.cpp @@ -258,6 +258,11 @@ QString QgsSimpleFillSymbolLayer::layerType() const return QStringLiteral( "SimpleFill" ); } +Qgis::SymbolLayerFlags QgsSimpleFillSymbolLayer::flags() const +{ + return QgsFillSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsSimpleFillSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QColor fillColor = mColor; @@ -691,6 +696,11 @@ QgsSymbolLayer *QgsGradientFillSymbolLayer::create( const QVariantMap &props ) return sl.release(); } +Qgis::SymbolLayerFlags QgsGradientFillSymbolLayer::flags() const +{ + return QgsFillSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsGradientFillSymbolLayer::setColorRamp( QgsColorRamp *ramp ) { delete mGradientRamp; @@ -1208,6 +1218,11 @@ QString QgsShapeburstFillSymbolLayer::layerType() const return QStringLiteral( "ShapeburstFill" ); } +Qgis::SymbolLayerFlags QgsShapeburstFillSymbolLayer::flags() const +{ + return QgsFillSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsShapeburstFillSymbolLayer::setColorRamp( QgsColorRamp *ramp ) { if ( mGradientRamp.get() == ramp ) @@ -2148,6 +2163,7 @@ void QgsSVGFillSymbolLayer::startRender( QgsSymbolRenderContext &context ) if ( mStroke ) { + mStroke->setRenderHints( mStroke->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mStroke->startRender( context.renderContext(), context.fields() ); } } @@ -3118,6 +3134,7 @@ bool QgsLinePatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext & lineRenderContext.setFlag( Qgis::RenderContextFlag::RenderingSubSymbol ); lineRenderContext.setDisabledSymbolLayersV2( context.renderContext().disabledSymbolLayersV2() ); + fillLineSymbol->setRenderHints( fillLineSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); fillLineSymbol->startRender( lineRenderContext, context.fields() ); QVector polygons; @@ -3176,6 +3193,7 @@ void QgsLinePatternFillSymbolLayer::startRender( QgsSymbolRenderContext &context if ( mRenderUsingLines && mFillLineSymbol ) { + mFillLineSymbol->setRenderHints( mFillLineSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mFillLineSymbol->startRender( context.renderContext(), context.fields() ); mFillLineSymbolRenderStarted = true; } @@ -3202,6 +3220,7 @@ void QgsLinePatternFillSymbolLayer::renderPolygon( const QPolygonF &points, cons if ( !mFillLineSymbolRenderStarted && mFillLineSymbol ) { + mFillLineSymbol->setRenderHints( mFillLineSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mFillLineSymbol->startRender( context.renderContext(), context.fields() ); mFillLineSymbolRenderStarted = true; } @@ -3880,6 +3899,7 @@ bool QgsPointPatternFillSymbolLayer::applyPattern( const QgsSymbolRenderContext pointRenderContext.setExpressionContext( context.renderContext().expressionContext() ); pointRenderContext.setFlag( Qgis::RenderContextFlag::RenderingSubSymbol ); + mMarkerSymbol->setRenderHints( mMarkerSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mMarkerSymbol->startRender( pointRenderContext, context.fields() ); //render points on distance grid @@ -3957,7 +3977,8 @@ void QgsPointPatternFillSymbolLayer::startRender( QgsSymbolRenderContext &contex if ( mRenderUsingMarkers && mMarkerSymbol ) { - mMarkerSymbol->startRender( context.renderContext() ); + mMarkerSymbol->setRenderHints( mMarkerSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); + mMarkerSymbol->startRender( context.renderContext(), context.fields() ); mMarkerSymbolRenderStarted = true; } } @@ -4001,7 +4022,8 @@ void QgsPointPatternFillSymbolLayer::renderPolygon( const QPolygonF &points, con if ( !mMarkerSymbolRenderStarted && mMarkerSymbol ) { - mMarkerSymbol->startRender( context.renderContext() ); + mMarkerSymbol->setRenderHints( mMarkerSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); + mMarkerSymbol->startRender( context.renderContext(), context.fields() ); mMarkerSymbolRenderStarted = true; } @@ -4793,6 +4815,7 @@ QColor QgsCentroidFillSymbolLayer::color() const void QgsCentroidFillSymbolLayer::startRender( QgsSymbolRenderContext &context ) { + mMarker->setRenderHints( mMarker->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mMarker->startRender( context.renderContext(), context.fields() ); } @@ -5208,6 +5231,11 @@ QString QgsRasterFillSymbolLayer::layerType() const return QStringLiteral( "RasterFill" ); } +Qgis::SymbolLayerFlags QgsRasterFillSymbolLayer::flags() const +{ + return QgsImageFillSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsRasterFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ) { QPainter *p = context.renderContext().painter(); @@ -5544,6 +5572,7 @@ QColor QgsRandomMarkerFillSymbolLayer::color() const void QgsRandomMarkerFillSymbolLayer::startRender( QgsSymbolRenderContext &context ) { + mMarker->setRenderHints( mMarker->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mMarker->startRender( context.renderContext(), context.fields() ); } diff --git a/src/core/symbology/qgsfillsymbollayer.h b/src/core/symbology/qgsfillsymbollayer.h index 5dc3a697c2da..af32b7877a6f 100644 --- a/src/core/symbology/qgsfillsymbollayer.h +++ b/src/core/symbology/qgsfillsymbollayer.h @@ -66,19 +66,13 @@ class CORE_EXPORT QgsSimpleFillSymbolLayer : public QgsFillSymbolLayer // implemented from base classes QString layerType() const override; - + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; - void stopRender( QgsSymbolRenderContext &context ) override; - void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ) override; - QVariantMap properties() const override; - QgsSimpleFillSymbolLayer *clone() const override SIP_FACTORY; - void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const override; - QString ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const override; Qt::BrushStyle brushStyle() const { return mBrushStyle; } @@ -86,7 +80,6 @@ class CORE_EXPORT QgsSimpleFillSymbolLayer : public QgsFillSymbolLayer QColor strokeColor() const override { return mStrokeColor; } void setStrokeColor( const QColor &strokeColor ) override { mStrokeColor = strokeColor; } - QColor fillColor() const override { return color(); } void setFillColor( const QColor &color ) override { setColor( color ); } @@ -238,6 +231,7 @@ class CORE_EXPORT QgsGradientFillSymbolLayer : public QgsFillSymbolLayer // implemented from base classes + Qgis::SymbolLayerFlags flags() const override; QString layerType() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; @@ -522,6 +516,7 @@ class CORE_EXPORT QgsShapeburstFillSymbolLayer : public QgsFillSymbolLayer // implemented from base classes QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ) override; @@ -888,6 +883,7 @@ class CORE_EXPORT QgsRasterFillSymbolLayer: public QgsImageFillSymbolLayer // implemented from base classes QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void renderPolygon( const QPolygonF &points, const QVector *rings, QgsSymbolRenderContext &context ) override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; diff --git a/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp b/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp index a7090aa2b866..dc9d3dac189b 100644 --- a/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp +++ b/src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp @@ -98,7 +98,9 @@ void QgsGeometryGeneratorSymbolLayer::startRender( QgsSymbolRenderContext &conte { mExpression->prepare( &context.renderContext().expressionContext() ); - subSymbol()->startRender( context.renderContext() ); + subSymbol()->setRenderHints( subSymbol()->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); + + subSymbol()->startRender( context.renderContext(), context.fields() ); } void QgsGeometryGeneratorSymbolLayer::stopRender( QgsSymbolRenderContext &context ) diff --git a/src/core/symbology/qgsinterpolatedlinerenderer.cpp b/src/core/symbology/qgsinterpolatedlinerenderer.cpp index 3a0a9bcb466a..945603deca65 100644 --- a/src/core/symbology/qgsinterpolatedlinerenderer.cpp +++ b/src/core/symbology/qgsinterpolatedlinerenderer.cpp @@ -868,7 +868,9 @@ QgsSymbolLayer *QgsInterpolatedLineSymbolLayer::create( const QVariantMap &prope Qgis::SymbolLayerFlags QgsInterpolatedLineSymbolLayer::flags() const { - return Qgis::SymbolLayerFlag::DisableFeatureClipping; + return QgsLineSymbolLayer::flags() + | Qgis::SymbolLayerFlag::DisableFeatureClipping + | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; } QVariantMap QgsInterpolatedLineSymbolLayer::properties() const diff --git a/src/core/symbology/qgslinesymbollayer.cpp b/src/core/symbology/qgslinesymbollayer.cpp index fa41e5177416..cf48a1f5616c 100644 --- a/src/core/symbology/qgslinesymbollayer.cpp +++ b/src/core/symbology/qgslinesymbollayer.cpp @@ -231,6 +231,11 @@ QString QgsSimpleLineSymbolLayer::layerType() const return QStringLiteral( "SimpleLine" ); } +Qgis::SymbolLayerFlags QgsSimpleLineSymbolLayer::flags() const +{ + return QgsLineSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsSimpleLineSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QColor penColor = mColor; @@ -2470,7 +2475,7 @@ QColor QgsMarkerLineSymbolLayer::color() const void QgsMarkerLineSymbolLayer::startRender( QgsSymbolRenderContext &context ) { // if being rotated, it gets initialized with every line segment - Qgis::SymbolRenderHints hints = Qgis::SymbolRenderHints(); + Qgis::SymbolRenderHints hints = Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol; if ( rotateSymbols() ) hints |= Qgis::SymbolRenderHint::DynamicRotation; mMarker->setRenderHints( hints ); @@ -2796,7 +2801,7 @@ QString QgsHashedLineSymbolLayer::layerType() const void QgsHashedLineSymbolLayer::startRender( QgsSymbolRenderContext &context ) { // if being rotated, it gets initialized with every line segment - Qgis::SymbolRenderHints hints = Qgis::SymbolRenderHints(); + Qgis::SymbolRenderHints hints = Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol; if ( rotateSymbols() ) hints |= Qgis::SymbolRenderHint::DynamicRotation; mHashSymbol->setRenderHints( hints ); @@ -3438,6 +3443,11 @@ QString QgsRasterLineSymbolLayer::layerType() const return QStringLiteral( "RasterLine" ); } +Qgis::SymbolLayerFlags QgsRasterLineSymbolLayer::flags() const +{ + return QgsAbstractBrushedLineSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsRasterLineSymbolLayer::startRender( QgsSymbolRenderContext &context ) { double scaledHeight = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale ); @@ -3683,6 +3693,11 @@ QString QgsLineburstSymbolLayer::layerType() const return QStringLiteral( "Lineburst" ); } +Qgis::SymbolLayerFlags QgsLineburstSymbolLayer::flags() const +{ + return QgsAbstractBrushedLineSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsLineburstSymbolLayer::startRender( QgsSymbolRenderContext & ) { } @@ -3876,6 +3891,7 @@ void QgsFilledLineSymbolLayer::startRender( QgsSymbolRenderContext &context ) { if ( mFill ) { + mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mFill->startRender( context.renderContext(), context.fields() ); } } diff --git a/src/core/symbology/qgslinesymbollayer.h b/src/core/symbology/qgslinesymbollayer.h index 7ed31618cf61..8f1189c88bc9 100644 --- a/src/core/symbology/qgslinesymbollayer.h +++ b/src/core/symbology/qgslinesymbollayer.h @@ -71,6 +71,7 @@ class CORE_EXPORT QgsSimpleLineSymbolLayer : public QgsLineSymbolLayer static QgsSymbolLayer *createFromSld( QDomElement &element ) SIP_FACTORY; QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) override; @@ -1249,6 +1250,7 @@ class CORE_EXPORT QgsRasterLineSymbolLayer : public QgsAbstractBrushedLineSymbol void setOpacity( double opacity ) { mOpacity = opacity; } QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) override; @@ -1300,6 +1302,7 @@ class CORE_EXPORT QgsLineburstSymbolLayer : public QgsAbstractBrushedLineSymbolL static QgsSymbolLayer *create( const QVariantMap &properties = QVariantMap() ) SIP_FACTORY; QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void stopRender( QgsSymbolRenderContext &context ) override; void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) override; diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index 2a373c60770b..7bc65562deea 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -1089,6 +1089,11 @@ QString QgsSimpleMarkerSymbolLayer::layerType() const return QStringLiteral( "SimpleMarker" ); } +Qgis::SymbolLayerFlags QgsSimpleMarkerSymbolLayer::flags() const +{ + return QgsSimpleMarkerSymbolLayerBase::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsSimpleMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QgsSimpleMarkerSymbolLayerBase::startRender( context ); @@ -1931,6 +1936,7 @@ void QgsFilledMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context ) { if ( mFill ) { + mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mFill->startRender( context.renderContext(), context.fields() ); } @@ -2313,6 +2319,11 @@ QString QgsSvgMarkerSymbolLayer::layerType() const return QStringLiteral( "SvgMarker" ); } +Qgis::SymbolLayerFlags QgsSvgMarkerSymbolLayer::flags() const +{ + return QgsMarkerSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsSvgMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions @@ -3137,6 +3148,11 @@ QString QgsRasterMarkerSymbolLayer::layerType() const return QStringLiteral( "RasterMarker" ); } +Qgis::SymbolLayerFlags QgsRasterMarkerSymbolLayer::flags() const +{ + return QgsMarkerSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsRasterMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext &context ) { QPainter *p = context.renderContext().painter(); @@ -3535,6 +3551,11 @@ QString QgsFontMarkerSymbolLayer::layerType() const return QStringLiteral( "FontMarker" ); } +Qgis::SymbolLayerFlags QgsFontMarkerSymbolLayer::flags() const +{ + return QgsMarkerSymbolLayer::flags() | Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature; +} + void QgsFontMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context ) { QColor brushColor = mColor; diff --git a/src/core/symbology/qgsmarkersymbollayer.h b/src/core/symbology/qgsmarkersymbollayer.h index 38fabd2385e3..b07ec4c29003 100644 --- a/src/core/symbology/qgsmarkersymbollayer.h +++ b/src/core/symbology/qgsmarkersymbollayer.h @@ -215,6 +215,7 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayer : public QgsSimpleMarkerSymbolLayer // reimplemented from base classes QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override; QVariantMap properties() const override; @@ -513,18 +514,13 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer // implemented from base classes QString layerType() const override; - + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; - void stopRender( QgsSymbolRenderContext &context ) override; - void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override; - QVariantMap properties() const override; bool usesMapUnits() const override; - QgsSvgMarkerSymbolLayer *clone() const override SIP_FACTORY; - void writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const override; /** @@ -705,7 +701,7 @@ class CORE_EXPORT QgsRasterMarkerSymbolLayer : public QgsMarkerSymbolLayer // implemented from base classes QString layerType() const override; - + Qgis::SymbolLayerFlags flags() const override; void renderPoint( QPointF point, QgsSymbolRenderContext &context ) override; QVariantMap properties() const override; QgsRasterMarkerSymbolLayer *clone() const override SIP_FACTORY; @@ -885,6 +881,7 @@ class CORE_EXPORT QgsFontMarkerSymbolLayer : public QgsMarkerSymbolLayer // implemented from base classes QString layerType() const override; + Qgis::SymbolLayerFlags flags() const override; void startRender( QgsSymbolRenderContext &context ) override; diff --git a/src/core/symbology/qgssymbol.cpp b/src/core/symbology/qgssymbol.cpp index ae26a6b516a9..7efebe820228 100644 --- a/src/core/symbology/qgssymbol.cpp +++ b/src/core/symbology/qgssymbol.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -868,7 +869,27 @@ void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields continue; layer->prepareExpressions( symbolContext ); - layer->prepareMasks( symbolContext ); + + // We prepare "entire map" clip masks in advance only in certain circumstances. These are non-optimal, + // because the entire map mask will be applied once for every feature rendered, resulting in overly complex + // clipping paths with paths which fall well outside of the map area that is actually being drawn on for the + // feature. These circumstances are: + // 1. If we are rendering a sub symbol. The current logic relating to calculating per-feature masks + // is not designed to handle sub symbol rendering where layers from the subsymbol have their own set of + // clipping paths, so we just fallback to the non-optimal approach always for these cases. + // TODO: + // - we could add another special condition here to check whether the subsymbol actually does have unique + // clipping paths in its symbol layers, or whether they are identical to the parent symbol layer's clipping paths. + // 2. When the symbol layer type doesn't explicitly state that it's compatible with per-feature mask geometries + // 3. When the older clipping mask approach using QPainterPaths is being used. (This last condition can be + // safely removed when the older QPainterPath backend is removed.) + // 4. When per feature mask geometry is explicitly disabled for the render context + // In other circumstances we do NOT prepare masks in advance, and instead calculate them in renderFeature(). + if ( mRenderHints.testFlag( Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ) + || context.testFlag( Qgis::RenderContextFlag::AlwaysUseGlobalMasks ) + || !layer->flags().testFlag( Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature ) + || !context.symbolLayerClipPaths( layer->id() ).isEmpty() ) + layer->prepareMasks( symbolContext ); layer->startRender( symbolContext ); } } @@ -1633,6 +1654,9 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont if ( needsExpressionContext ) mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) ); + const bool maskGeometriesDisabledForSymbol = context.testFlag( Qgis::RenderContextFlag::AlwaysUseGlobalMasks ) + && !mRenderHints.testFlag( Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); + for ( const int symbolLayerIndex : layers ) { QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex ); @@ -1642,6 +1666,23 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont if ( needsExpressionContext ) mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) ); + // if this symbol layer has associated clip masks, we need to render it to a QPicture first so that we can + // determine the actual rendered bounds of the symbol. We'll then use that to retrieve the clip masks we need + // to apply when painting the symbol via this QPicture. + const bool hasClipGeometries = !maskGeometriesDisabledForSymbol + && symbolLayer->flags().testFlag( Qgis::SymbolLayerFlag::CanCalculateMaskGeometryPerFeature ) + && context.symbolLayerHasClipGeometries( symbolLayer->id() ); + QPainter *previousPainter = nullptr; + std::unique_ptr< QPicture > renderedPicture; + std::unique_ptr< QPainter > picturePainter; + if ( hasClipGeometries ) + { + previousPainter = context.painter(); + renderedPicture = std::make_unique< QPicture >(); + picturePainter = std::make_unique< QPainter >( renderedPicture.get() ); + context.setPainter( picturePainter.get() ); + } + symbolLayer->startFeatureRender( feature, context ); switch ( mType ) @@ -1710,6 +1751,32 @@ void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &cont } symbolLayer->stopFeatureRender( feature, context ); + + if ( hasClipGeometries ) + { + // restore previous painter + context.setPainter( previousPainter ); + picturePainter->end(); + picturePainter.reset(); + + // determine actual rendered bounds of symbol layer, and then buffer out a little to be safe + QRectF maximalBounds = renderedPicture->boundingRect(); + constexpr double BOUNDS_MARGIN = 0.05; + maximalBounds.adjust( -maximalBounds.width() * BOUNDS_MARGIN, -maximalBounds.height() * BOUNDS_MARGIN, maximalBounds.width() * BOUNDS_MARGIN, maximalBounds.height() * BOUNDS_MARGIN ); + + const bool hadClipping = context.painter()->hasClipping(); + const QPainterPath oldClipPath = hadClipping ? context.painter()->clipPath() : QPainterPath(); + + const bool isMasked = symbolLayer->installMasks( context, false, maximalBounds ); + + context.painter()->drawPicture( QPointF( 0, 0 ), *renderedPicture ); + + if ( isMasked ) + { + context.painter()->setClipPath( oldClipPath ); + context.painter()->setClipping( hadClipping ); + } + } } // step 4 - handle post processing steps diff --git a/src/core/symbology/qgssymbollayer.cpp b/src/core/symbology/qgssymbollayer.cpp index 037c37271f47..87eafd7b2952 100644 --- a/src/core/symbology/qgssymbollayer.cpp +++ b/src/core/symbology/qgssymbollayer.cpp @@ -953,20 +953,20 @@ double QgsMarkerSymbolLayer::dxfAngle( QgsSymbolRenderContext &context ) const return angle; } -void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context ) +QPainterPath generateClipPath( const QgsRenderContext &renderContext, const QString &id, const QRectF *rect, bool &foundGeometries ) { - mClipPath.clear(); - - const QgsRenderContext &renderContext = context.renderContext(); - - const QVector clipGeometries = renderContext.symbolLayerClipGeometries( id() ); + foundGeometries = false; + const QVector clipGeometries = rect + ? QgsSymbolLayerUtils::collectSymbolLayerClipGeometries( renderContext, id, *rect ) + : renderContext.symbolLayerClipGeometries( id ); if ( !clipGeometries.empty() ) { + foundGeometries = true; QgsGeometry mergedGeom = QgsGeometry::unaryUnion( clipGeometries ); - if ( context.renderContext().maskSettings().simplifyTolerance() > 0 ) + if ( renderContext.maskSettings().simplifyTolerance() > 0 ) { QgsGeos geos( mergedGeom.constGet() ); - mergedGeom = QgsGeometry( geos.simplify( context.renderContext().maskSettings().simplifyTolerance() ) ); + mergedGeom = QgsGeometry( geos.simplify( renderContext.maskSettings().simplifyTolerance() ) ); } #if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<10 // structure would be better, but too old GEOS @@ -976,15 +976,29 @@ void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context ) #endif if ( !mergedGeom.isEmpty() ) { - const QgsGeometry exterior = QgsGeometry::fromRect( + const QgsGeometry exterior = rect + ? QgsGeometry::fromRect( *rect ) + : QgsGeometry::fromRect( QgsRectangle( 0, 0, renderContext.outputSize().width(), renderContext.outputSize().height() ) ); const QgsGeometry maskGeom = exterior.difference( mergedGeom ); - mClipPath = maskGeom.constGet()->asQPainterPath(); + if ( !maskGeom.isNull() ) + { + return maskGeom.constGet()->asQPainterPath(); + } } } - else + return QPainterPath(); +} + +void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context ) +{ + const QgsRenderContext &renderContext = context.renderContext(); + + bool foundGeometries = false; + mClipPath = generateClipPath( renderContext, id(), nullptr, foundGeometries ); + if ( !foundGeometries ) { const QList clipPaths = renderContext.symbolLayerClipPaths( id() ); if ( !clipPaths.isEmpty() ) @@ -1006,20 +1020,35 @@ void QgsSymbolLayer::prepareMasks( const QgsSymbolRenderContext &context ) } } -void QgsSymbolLayer::installMasks( QgsRenderContext &context, bool recursive ) +bool QgsSymbolLayer::installMasks( QgsRenderContext &context, bool recursive, const QRectF &rect ) { + bool res = false; if ( !mClipPath.isEmpty() ) { context.painter()->save(); context.painter()->setClipPath( mClipPath, Qt::IntersectClip ); + res = true; + } + else if ( rect.isValid() ) + { + // find just the clip geometries within the area the symbol layer will be drawn over + bool foundGeometries = false; + const QPainterPath clipPath = generateClipPath( context, id(), &rect, foundGeometries ); + if ( !clipPath.isEmpty() ) + { + context.painter()->setClipPath( clipPath, context.painter()->clipPath().isEmpty() ? Qt::ReplaceClip : Qt::IntersectClip ); + res = true; + } } if ( QgsSymbol *lSubSymbol = recursive ? subSymbol() : nullptr ) { const QList layers = lSubSymbol->symbolLayers(); for ( QgsSymbolLayer *sl : layers ) - sl->installMasks( context, true ); + res = sl->installMasks( context, true ) || res; } + + return res; } void QgsSymbolLayer::removeMasks( QgsRenderContext &context, bool recursive ) diff --git a/src/core/symbology/qgssymbollayer.h b/src/core/symbology/qgssymbollayer.h index bb2ec73e9dc9..fae4bf17cf59 100644 --- a/src/core/symbology/qgssymbollayer.h +++ b/src/core/symbology/qgssymbollayer.h @@ -653,6 +653,23 @@ class CORE_EXPORT QgsSymbolLayer */ QString id() const; + /** + * When rendering, install masks on \a context painter. + * + * If \a recursive is TRUE masks are installed recursively for all children symbol layers. + * + * Since QGIS 3.38 the \a rect argument can be used to specify a target bounds (in painter coordinates) + * for mask geometries. Only mask geometries which intersect ``rect`` will be installed. + * + * \returns TRUE if any masks were installed (since QGIS 3.38) + * + * \see prepareMasks() + * \see removeMasks() + * + * \since QGIS 3.30 + */ + bool installMasks( QgsRenderContext &context, bool recursive, const QRectF &rect = QRectF() ); + protected: /** @@ -707,15 +724,6 @@ class CORE_EXPORT QgsSymbolLayer */ void copyPaintEffect( QgsSymbolLayer *destLayer ) const; - /** - * When rendering, install masks on \a context painter - * if \a recursive is TRUE masks are installed recursively for all children symbol layers - * \see prepareMasks() - * \see removeMasks() - * \since QGIS 3.30 - */ - void installMasks( QgsRenderContext &context, bool recursive ); - /** * When rendering, remove previously installed masks from \a context painter * if \a recursive is TRUE masks are removed recursively for all children symbol layers diff --git a/src/core/symbology/qgssymbollayerutils.cpp b/src/core/symbology/qgssymbollayerutils.cpp index 7985d43c40f3..6d04614cca28 100644 --- a/src/core/symbology/qgssymbollayerutils.cpp +++ b/src/core/symbology/qgssymbollayerutils.cpp @@ -5418,6 +5418,26 @@ void QgsSymbolLayerUtils::resetSymbolLayerIds( QgsSymbolLayer *symbolLayer ) changeSymbolLayerIds( symbolLayer, []() { return QUuid::createUuid().toString(); } ); } +QVector QgsSymbolLayerUtils::collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ) +{ + QVector clipGeometries = context.symbolLayerClipGeometries( symbolLayerId ); + if ( clipGeometries.empty() ) + return {}; + + if ( bounds.isNull() ) + return clipGeometries; + + const QgsRectangle boundsRect = QgsRectangle( bounds ); + + clipGeometries.erase( + std::remove_if( clipGeometries.begin(), clipGeometries.end(), [&boundsRect]( const QgsGeometry & geometry ) + { + return !geometry.boundingBoxIntersects( boundsRect ); + } ), clipGeometries.end() ); + + return clipGeometries; +} + void QgsSymbolLayerUtils::resetSymbolLayerIds( QgsSymbol *symbol ) { changeSymbolLayerIds( symbol, []() { return QUuid::createUuid().toString(); } ); diff --git a/src/core/symbology/qgssymbollayerutils.h b/src/core/symbology/qgssymbollayerutils.h index e20ea9777b09..89f50d28e5a9 100644 --- a/src/core/symbology/qgssymbollayerutils.h +++ b/src/core/symbology/qgssymbollayerutils.h @@ -929,6 +929,18 @@ class CORE_EXPORT QgsSymbolLayerUtils */ static void resetSymbolLayerIds( QgsSymbolLayer *symbolLayer ); + /** + * Returns a list of the symbol layer clip geometries to be used for the symbol layer with the specified + * ID. + * + * The \a bounds argument specifies the target bounds (in painter coordinates) for matching geometries. Only mask + * geometries which intersect \a bounds will be returned. If \a bounds is a null QRectF then all clip geometries + * for the symbol layer will be returned. + * + * \since QGIS 3.38 + */ + static QVector< QgsGeometry > collectSymbolLayerClipGeometries( const QgsRenderContext &context, const QString &symbolLayerId, const QRectF &bounds ); + ///@cond PRIVATE #ifndef SIP_RUN static QgsProperty rotateWholeSymbol( double additionalRotation, const QgsProperty &property ) diff --git a/src/core/symbology/qgsvectorfieldsymbollayer.cpp b/src/core/symbology/qgsvectorfieldsymbollayer.cpp index 105a0eede77f..f1f667f8af37 100644 --- a/src/core/symbology/qgsvectorfieldsymbollayer.cpp +++ b/src/core/symbology/qgsvectorfieldsymbollayer.cpp @@ -223,6 +223,7 @@ void QgsVectorFieldSymbolLayer::startRender( QgsSymbolRenderContext &context ) { if ( mLineSymbol ) { + mLineSymbol->setRenderHints( mLineSymbol->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol ); mLineSymbol->startRender( context.renderContext(), context.fields() ); } diff --git a/tests/src/python/test_qgssymbollayerutils.py b/tests/src/python/test_qgssymbollayerutils.py index b916f75605b7..3969d25a3ffe 100644 --- a/tests/src/python/test_qgssymbollayerutils.py +++ b/tests/src/python/test_qgssymbollayerutils.py @@ -11,7 +11,15 @@ import math -from qgis.PyQt.QtCore import QDir, QMimeData, QPointF, QSize, QSizeF, Qt +from qgis.PyQt.QtCore import ( + QDir, + QMimeData, + QPointF, + QSize, + QSizeF, + Qt, + QRectF +) from qgis.PyQt.QtXml import QDomDocument, QDomElement from qgis.PyQt.QtGui import QColor, QImage, QPolygonF from qgis.core import ( @@ -33,7 +41,9 @@ QgsSymbolLayer, QgsSymbolLayerUtils, QgsUnitTypes, - QgsVectorLayer + QgsVectorLayer, + QgsRenderContext, + QgsGeometry ) import unittest from qgis.testing import start_app, QgisTestCase @@ -821,6 +831,72 @@ def test_font_marker_load(self): font_marker = QgsSymbolLayerUtils.loadSymbol(elem, QgsReadWriteContext()) self.assertEqual(font_marker.symbolLayers()[0].character(), "()") + def test_collect_symbol_layer_clip_geometries(self): + """ + Test logic relating to symbol layer clip geometries. + """ + rc = QgsRenderContext() + self.assertFalse(QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(0, 0, 10, 10) + )) + rc.addSymbolLayerClipGeometry('x', + QgsGeometry.fromWkt( + 'Polygon(( 0 0, 1 0 , 1 1 , 0 1, 0 0 ))')) + self.assertFalse(QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'y', QRectF(0, 0, 10, 10) + )) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(0, 0, 10, 10) + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))']) + rc.addSymbolLayerClipGeometry('x', QgsGeometry.fromWkt( + 'Polygon(( 20 0, 21 0 , 21 1 , 20 1, 20 0 ))')) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(0, 0, 10, 10) + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))']) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(15, 0, 10, 10) + )], + ['Polygon ((20 0, 21 0, 21 1, 20 1, 20 0))']) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(0, 0, 25, 10) + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))', + 'Polygon ((20 0, 21 0, 21 1, 20 1, 20 0))']) + + # null rect + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF() + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))', + 'Polygon ((20 0, 21 0, 21 1, 20 1, 20 0))']) + + rc.addSymbolLayerClipGeometry('y', + QgsGeometry.fromWkt( + 'Polygon(( 0 0, 2 0 , 2 1 , 0 1, 0 0 ))')) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF(0, 0, 25, 10) + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))', + 'Polygon ((20 0, 21 0, 21 1, 20 1, 20 0))']) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'y', QRectF(0, 0, 25, 10) + )], ['Polygon ((0 0, 2 0, 2 1, 0 1, 0 0))']) + + # null rect + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'x', QRectF() + )], ['Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))', + 'Polygon ((20 0, 21 0, 21 1, 20 1, 20 0))']) + self.assertCountEqual([g.asWkt() for g in + QgsSymbolLayerUtils.collectSymbolLayerClipGeometries( + rc, 'y', QRectF() + )], ['Polygon ((0 0, 2 0, 2 1, 0 1, 0 0))']) + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/control_images/selective_masking/layout_export/layout_export_mask.png b/tests/testdata/control_images/selective_masking/layout_export/layout_export_mask.png index f2939a7b9bd4..19bae4092352 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export/layout_export_mask.png and b/tests/testdata/control_images/selective_masking/layout_export/layout_export_mask.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_2_sources_masking/layout_export_2_sources_masking_mask.png b/tests/testdata/control_images/selective_masking/layout_export_2_sources_masking/layout_export_2_sources_masking_mask.png index fc07e35d0789..3bd9d9c2ea56 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_2_sources_masking/layout_export_2_sources_masking_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_2_sources_masking/layout_export_2_sources_masking_mask.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_force_raster_render/layout_export_force_raster_render_mask.png b/tests/testdata/control_images/selective_masking/layout_export_force_raster_render/layout_export_force_raster_render_mask.png index 13b856af0091..ba4ed65a2044 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_force_raster_render/layout_export_force_raster_render_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_force_raster_render/layout_export_force_raster_render_mask.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_marker_masking/layout_export_marker_masking_mask.png b/tests/testdata/control_images/selective_masking/layout_export_marker_masking/layout_export_marker_masking_mask.png index f2b5977b2470..987381ec264b 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_marker_masking/layout_export_marker_masking_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_marker_masking/layout_export_marker_masking_mask.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 index 6ea2f0008eeb..518545a06f5b 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_markerline_masked/layout_export_markerline_masked_mask.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_svg_marker_masking/layout_export_svg_marker_masking_mask.png b/tests/testdata/control_images/selective_masking/layout_export_svg_marker_masking/layout_export_svg_marker_masking_mask.png index 941926f6d5a1..43f2ed23de07 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_svg_marker_masking/layout_export_svg_marker_masking_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_svg_marker_masking/layout_export_svg_marker_masking_mask.png differ diff --git a/tests/testdata/control_images/selective_masking/layout_export_w_raster/layout_export_w_raster_mask.png b/tests/testdata/control_images/selective_masking/layout_export_w_raster/layout_export_w_raster_mask.png index d1eef4a9e148..b4c8531e9e3d 100644 Binary files a/tests/testdata/control_images/selective_masking/layout_export_w_raster/layout_export_w_raster_mask.png and b/tests/testdata/control_images/selective_masking/layout_export_w_raster/layout_export_w_raster_mask.png differ