Skip to content

Commit

Permalink
[feature][callouts] Add blending mode settings
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn authored and nyalldawson committed Apr 26, 2021
1 parent f93b738 commit 9989cff
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 4 deletions.
28 changes: 28 additions & 0 deletions python/core/auto_generated/callouts/qgscallout.sip.in
Expand Up @@ -10,6 +10,7 @@




class QgsCallout
{
%Docstring(signature="appended")
Expand Down Expand Up @@ -66,6 +67,7 @@ relevant symbology elements to render them.
Margins,
WedgeWidth,
CornerRadius,
BlendMode,
};

enum DrawOrder
Expand Down Expand Up @@ -156,6 +158,14 @@ Restores the callout's properties from a DOM element.
The default behavior is the read the DOM contents and call :py:func:`~QgsCallout.readProperties` on the subclass.

.. seealso:: :py:func:`readProperties`
%End

bool containsAdvancedEffects() const;
%Docstring
Returns ``True`` if the callout requires advanced effects such as blend modes, which require
output in raster formats to be fully respected.

.. versionadded:: 3.20
%End

virtual void startRender( QgsRenderContext &context );
Expand Down Expand Up @@ -390,6 +400,24 @@ anchor point.
.. seealso:: :py:func:`encodeLabelAnchorPoint`

.. versionadded:: 3.14
%End

QPainter::CompositionMode blendMode() const;
%Docstring
Returns the blending mode used for drawing callouts.

.. seealso:: :py:func:`setBlendMode`

.. versionadded:: 3.20
%End

void setBlendMode( QPainter::CompositionMode mode );
%Docstring
Sets the blending ``mode`` used for drawing callouts.

.. seealso:: :py:func:`blendMode`

.. versionadded:: 3.20
%End

protected:
Expand Down
8 changes: 8 additions & 0 deletions python/core/auto_generated/labeling/qgspallabeling.sip.in
Expand Up @@ -290,6 +290,14 @@ Finalises the label settings after use.
This must be called after a call to :py:func:`~QgsPalLayerSettings.startRender`, in order to gracefully clean up symbols.

.. versionadded:: 3.10
%End

bool containsAdvancedEffects() const;
%Docstring
Returns ``True`` if any component of the label settings requires advanced effects
such as blend modes, which require output in raster formats to be fully respected.

.. versionadded:: 3.20
%End

static const QgsPropertiesDefinition &propertyDefinitions();
Expand Down
29 changes: 27 additions & 2 deletions src/core/callouts/qgscallout.cpp
Expand Up @@ -28,7 +28,8 @@
#include "qgsgeometryutils.h"
#include "qgscircularstring.h"
#include "qgsshapegenerator.h"
#include <QPainter>
#include "qgspainting.h"

#include <mutex>

QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
Expand Down Expand Up @@ -64,6 +65,7 @@ void QgsCallout::initPropertyDefinitions()
},
{ QgsCallout::WedgeWidth, QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
{ QgsCallout::CornerRadius, QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
{ QgsCallout::BlendMode, QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
};
}

Expand All @@ -78,6 +80,7 @@ QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
props.insert( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
return props;
}
Expand All @@ -87,6 +90,8 @@ void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteCon
mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
mBlendMode = QgsPainting::getCompositionMode(
static_cast< QgsPainting::BlendMode >( props.value( QStringLiteral( "blendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
}

Expand Down Expand Up @@ -122,6 +127,11 @@ void QgsCallout::stopRender( QgsRenderContext & )

}

bool QgsCallout::containsAdvancedEffects() const
{
return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::BlendMode );
}

QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
{
mDataDefinedProperties.prepare( context.expressionContext() );
Expand All @@ -135,8 +145,21 @@ QgsCallout::DrawOrder QgsCallout::drawOrder() const

void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
{
#if 0 // for debugging
QPainter *painter = context.painter();
if ( context.useAdvancedEffects() )
{

QPainter::CompositionMode blendMode = mBlendMode;
if ( dataDefinedProperties().isActive( QgsCallout::BlendMode ) )
{
context.expressionContext().setOriginalValueVariable( QString() );
mBlendMode = QgsSymbolLayerUtils::decodeBlendMode( dataDefinedProperties().valueAsString( QgsCallout::BlendMode, context.expressionContext(), QString() ) );
}

painter->setCompositionMode( blendMode );
}

#if 0 // for debugging
painter->save();
painter->setRenderHint( QPainter::Antialiasing, false );
painter->translate( rect.center() );
Expand All @@ -155,6 +178,8 @@ void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const do
#endif

draw( context, rect, angle, anchor, calloutContext );

painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
}

void QgsCallout::setEnabled( bool enabled )
Expand Down
26 changes: 26 additions & 0 deletions src/core/callouts/qgscallout.h
Expand Up @@ -25,6 +25,8 @@
#include "qgsmapunitscale.h"
#include "qgscalloutposition.h"
#include "qgsmargins.h"

#include <QPainter>
#include <QString>
#include <QRectF>
#include <memory>
Expand Down Expand Up @@ -94,6 +96,7 @@ class CORE_EXPORT QgsCallout
Margins, //!< Margin from text (since QGIS 3.20)
WedgeWidth, //!< Balloon callout wedge width (since QGIS 3.20)
CornerRadius, //!< Balloon callout corner radius (since QGIS 3.20)
BlendMode, //!< Callout blend mode (since QGIS 3.20)
};

//! Options for draw order (stacking) of callouts
Expand Down Expand Up @@ -189,6 +192,13 @@ class CORE_EXPORT QgsCallout
*/
virtual void restoreProperties( const QDomElement &element, const QgsReadWriteContext &context );

/**
* Returns TRUE if the callout requires advanced effects such as blend modes, which require
* output in raster formats to be fully respected.
* \since QGIS 3.20
*/
bool containsAdvancedEffects() const;

/**
* Prepares the callout for rendering on the specified render \a context.
*
Expand Down Expand Up @@ -409,6 +419,20 @@ class CORE_EXPORT QgsCallout
*/
static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint( const QString &name, bool *ok = nullptr );

/**
* Returns the blending mode used for drawing callouts.
* \see setBlendMode()
* \since QGIS 3.20
*/
QPainter::CompositionMode blendMode() const { return mBlendMode; }

/**
* Sets the blending \a mode used for drawing callouts.
* \see blendMode()
* \since QGIS 3.20
*/
void setBlendMode( QPainter::CompositionMode mode ) { mBlendMode = mode; }

protected:

/**
Expand Down Expand Up @@ -465,6 +489,8 @@ class CORE_EXPORT QgsCallout
AnchorPoint mAnchorPoint = PoleOfInaccessibility;
LabelAnchorPoint mLabelAnchorPoint = LabelPointOnExterior;

QPainter::CompositionMode mBlendMode = QPainter::CompositionMode_SourceOver;

//! Property collection for data defined callout settings
QgsPropertyCollection mDataDefinedProperties;

Expand Down
5 changes: 5 additions & 0 deletions src/core/labeling/qgspallabeling.cpp
Expand Up @@ -563,6 +563,11 @@ void QgsPalLayerSettings::stopRender( QgsRenderContext &context )
mRenderStarted = false;
}

bool QgsPalLayerSettings::containsAdvancedEffects() const
{
return mFormat.containsAdvancedEffects() || mCallout->containsAdvancedEffects();
}

QgsPalLayerSettings::~QgsPalLayerSettings()
{
if ( mRenderStarted )
Expand Down
7 changes: 7 additions & 0 deletions src/core/labeling/qgspallabeling.h
Expand Up @@ -396,6 +396,13 @@ class CORE_EXPORT QgsPalLayerSettings
*/
void stopRender( QgsRenderContext &context );

/**
* Returns TRUE if any component of the label settings requires advanced effects
* such as blend modes, which require output in raster formats to be fully respected.
* \since QGIS 3.20
*/
bool containsAdvancedEffects() const;

/**
* Returns the labeling property definitions.
* \since QGIS 3.0
Expand Down
2 changes: 1 addition & 1 deletion src/core/labeling/qgsvectorlayerlabeling.cpp
Expand Up @@ -94,7 +94,7 @@ bool QgsVectorLayerSimpleLabeling::accept( QgsStyleEntityVisitorInterface *visit

bool QgsVectorLayerSimpleLabeling::requiresAdvancedEffects() const
{
return mSettings->format().containsAdvancedEffects();
return mSettings->containsAdvancedEffects();
}

QgsVectorLayerSimpleLabeling *QgsVectorLayerSimpleLabeling::create( const QDomElement &element, const QgsReadWriteContext &context )
Expand Down
32 changes: 32 additions & 0 deletions src/gui/callouts/qgscalloutwidget.cpp
Expand Up @@ -174,6 +174,8 @@ QgsSimpleLineCalloutWidget::QgsSimpleLineCalloutWidget( QgsVectorLayer *vl, QWid
connect( mLabelAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged );

connect( mCalloutLineStyleButton, &QgsSymbolButton::changed, this, &QgsSimpleLineCalloutWidget::lineSymbolChanged );

connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
}

void QgsSimpleLineCalloutWidget::setCallout( QgsCallout *callout )
Expand Down Expand Up @@ -210,12 +212,15 @@ void QgsSimpleLineCalloutWidget::setCallout( QgsCallout *callout )
whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );

whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );

registerDataDefinedButton( mMinCalloutLengthDDBtn, QgsCallout::MinimumCalloutLength );
registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
registerDataDefinedButton( mOffsetFromLabelDDBtn, QgsCallout::OffsetFromLabel );
registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );

registerDataDefinedButton( mOriginXDDBtn, QgsCallout::OriginX );
registerDataDefinedButton( mOriginYDDBtn, QgsCallout::OriginY );
Expand Down Expand Up @@ -296,6 +301,12 @@ void QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged(
emit changed();
}

void QgsSimpleLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
{
mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
emit changed();
}

void QgsSimpleLineCalloutWidget::drawToAllPartsToggled( bool active )
{
mCallout->setDrawCalloutToAllParts( active );
Expand Down Expand Up @@ -384,6 +395,7 @@ QgsCurvedLineCalloutWidget::QgsCurvedLineCalloutWidget( QgsVectorLayer *vl, QWid
emit changed();
} );

connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCurvedLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
}

void QgsCurvedLineCalloutWidget::setCallout( QgsCallout *callout )
Expand Down Expand Up @@ -422,6 +434,8 @@ void QgsCurvedLineCalloutWidget::setCallout( QgsCallout *callout )
whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );

whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );

whileBlocking( mCurvatureSpinBox )->setValue( mCallout->curvature() * 100.0 );
whileBlocking( mCurvatureSlider )->setValue( mCallout->curvature() * 1000.0 );

Expand All @@ -431,6 +445,7 @@ void QgsCurvedLineCalloutWidget::setCallout( QgsCallout *callout )
registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );
registerDataDefinedButton( mCalloutCurvatureDDBtn, QgsCallout::Curvature );
registerDataDefinedButton( mCalloutOrientationDDBtn, QgsCallout::Orientation );

Expand Down Expand Up @@ -513,6 +528,12 @@ void QgsCurvedLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged(
emit changed();
}

void QgsCurvedLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
{
mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
emit changed();
}

void QgsCurvedLineCalloutWidget::drawToAllPartsToggled( bool active )
{
mCallout->setDrawCalloutToAllParts( active );
Expand Down Expand Up @@ -620,6 +641,8 @@ QgsBalloonCalloutWidget::QgsBalloonCalloutWidget( QgsVectorLayer *vl, QWidget *p
mCallout->setCornerRadius( value );
emit changed();
} );

connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsBalloonCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
}

void QgsBalloonCalloutWidget::setCallout( QgsCallout *callout )
Expand Down Expand Up @@ -659,8 +682,11 @@ void QgsBalloonCalloutWidget::setCallout( QgsCallout *callout )

whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );

whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );

registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );

registerDataDefinedButton( mDestXDDBtn, QgsCallout::DestinationX );
registerDataDefinedButton( mDestYDDBtn, QgsCallout::DestinationY );
Expand Down Expand Up @@ -710,5 +736,11 @@ void QgsBalloonCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int inde
emit changed();
}

void QgsBalloonCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
{
mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
emit changed();
}

///@endcond

3 changes: 3 additions & 0 deletions src/gui/callouts/qgscalloutwidget.h
Expand Up @@ -153,6 +153,7 @@ class GUI_EXPORT QgsSimpleLineCalloutWidget : public QgsCalloutWidget, private U
void lineSymbolChanged();
void mAnchorPointComboBox_currentIndexChanged( int index );
void mLabelAnchorPointComboBox_currentIndexChanged( int index );
void mCalloutBlendComboBox_currentIndexChanged( int index );
void drawToAllPartsToggled( bool active );

private:
Expand Down Expand Up @@ -207,6 +208,7 @@ class GUI_EXPORT QgsCurvedLineCalloutWidget : public QgsCalloutWidget, private U
void lineSymbolChanged();
void mAnchorPointComboBox_currentIndexChanged( int index );
void mLabelAnchorPointComboBox_currentIndexChanged( int index );
void mCalloutBlendComboBox_currentIndexChanged( int index );
void drawToAllPartsToggled( bool active );

private:
Expand Down Expand Up @@ -243,6 +245,7 @@ class GUI_EXPORT QgsBalloonCalloutWidget : public QgsCalloutWidget, private Ui::
void offsetFromAnchorChanged();
void fillSymbolChanged();
void mAnchorPointComboBox_currentIndexChanged( int index );
void mCalloutBlendComboBox_currentIndexChanged( int index );

private:
std::unique_ptr< QgsBalloonCallout > mCallout;
Expand Down

0 comments on commit 9989cff

Please sign in to comment.