Skip to content

Commit

Permalink
Add support for rendering using QgsLegendPatchShape to QgsSymbol::dra…
Browse files Browse the repository at this point in the history
…wPreviewIcon
  • Loading branch information
nyalldawson committed Apr 10, 2020
1 parent e1b2313 commit 40638fe
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 44 deletions.
24 changes: 23 additions & 1 deletion python/core/auto_generated/symbology/qgssymbol.sip.in
Expand Up @@ -272,7 +272,8 @@ layer.
.. seealso:: :py:func:`setColor`
%End

void drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext = 0, bool selected = false, const QgsExpressionContext *expressionContext = 0 );
void drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext = 0, bool selected = false, const QgsExpressionContext *expressionContext = 0,
const QgsLegendPatchShape *patchShape = 0 );
%Docstring
Draws an icon of the symbol that occupies an area given by ``size`` using the specified ``painter``.

Expand All @@ -284,6 +285,7 @@ matches the settings from that context.
:param customContext: the context in which the rendering happens
:param selected: set to ``True`` to render the symbol in a selected state
:param expressionContext: optional custom expression context
:param patchShape: optional patch shape to use for symbol preview. If not specified a default shape will be used instead.

.. seealso:: :py:func:`exportImage`

Expand Down Expand Up @@ -613,6 +615,8 @@ Constructor for QgsSymbolRenderContext
:param mapUnitScale:
%End

~QgsSymbolRenderContext();


QgsRenderContext &renderContext();
%Docstring
Expand Down Expand Up @@ -800,6 +804,24 @@ Set an expression scope for this symbol.
Will take ownership.

:param contextScope: An expression scope for details about this symbol
%End

const QgsLegendPatchShape *patchShape() const;
%Docstring
Returns the symbol patch shape, to use if rendering symbol preview icons.

.. seealso:: :py:func:`setPatchShape`

.. versionadded:: 3.14
%End

void setPatchShape( const QgsLegendPatchShape &shape );
%Docstring
Sets the symbol patch ``shape``, to use if rendering symbol preview icons.

.. seealso:: :py:func:`patchShape`

.. versionadded:: 3.14
%End

private:
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology/qgsgeometrygeneratorsymbollayer.cpp
Expand Up @@ -137,7 +137,7 @@ QgsStringMap QgsGeometryGeneratorSymbolLayer::properties() const
void QgsGeometryGeneratorSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
if ( mSymbol )
mSymbol->drawPreviewIcon( context.renderContext().painter(), size );
mSymbol->drawPreviewIcon( context.renderContext().painter(), size, nullptr, false, nullptr, context.patchShape() );
}

void QgsGeometryGeneratorSymbolLayer::setGeometryExpression( const QString &exp )
Expand Down
45 changes: 36 additions & 9 deletions src/core/symbology/qgssymbol.cpp
Expand Up @@ -50,6 +50,7 @@
#include "qgsapplication.h"
#include "qgsexpressioncontextutils.h"
#include "qgsrenderedfeaturehandlerinterface.h"
#include "qgslegendpatchshape.h"

inline
QgsProperty rotateWholeSymbol( double additionalRotation, const QgsProperty &property )
Expand Down Expand Up @@ -499,7 +500,7 @@ QColor QgsSymbol::color() const
return QColor( 0, 0, 0 );
}

void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext )
void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape )
{
QgsRenderContext *context = customContext;
std::unique_ptr< QgsRenderContext > tempContext;
Expand All @@ -514,6 +515,8 @@ void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext
QgsSymbolRenderContext symbolContext( *context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr );
symbolContext.setSelected( selected );
symbolContext.setOriginalGeometryType( mType == Fill ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::UnknownGeometry );
if ( patchShape )
symbolContext.setPatchShape( *patchShape );

if ( !customContext && expressionContext )
{
Expand All @@ -539,19 +542,31 @@ void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext
QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
if ( lsl )
{
// from QgsFillSymbolLayer::drawPreviewIcon()
QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width() - 1, size.height() - 1 ) );
// from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
// symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!

// hmm... why was this using size -1 ??
const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );

const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( QgsSymbol::Fill, targetSize )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Fill, targetSize );

lsl->startRender( symbolContext );
QgsPaintEffect *effect = lsl->paintEffect();

std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
effectPainter = qgis::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );

for ( const QList< QPolygonF > &poly : polys )
{
QgsEffectPainter p( symbolContext.renderContext(), effect );
lsl->renderPolygonStroke( poly, nullptr, symbolContext );
}
else
{
lsl->renderPolygonStroke( poly, nullptr, symbolContext );
QList< QPolygonF > rings;
for ( int i = 1; i < poly.size(); ++i )
rings << poly.at( i );
lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
}

effectPainter.reset();
lsl->stopRender( symbolContext );
}
}
Expand Down Expand Up @@ -1248,6 +1263,8 @@ QgsSymbolRenderContext::QgsSymbolRenderContext( QgsRenderContext &c, QgsUnitType
{
}

QgsSymbolRenderContext::~QgsSymbolRenderContext() = default;

void QgsSymbolRenderContext::setOriginalValueVariable( const QVariant &value )
{
mRenderContext.expressionContext().setOriginalValueVariable( value );
Expand Down Expand Up @@ -1283,6 +1300,16 @@ void QgsSymbolRenderContext::setExpressionContextScope( QgsExpressionContextScop
mExpressionContextScope.reset( contextScope );
}

const QgsLegendPatchShape *QgsSymbolRenderContext::patchShape() const
{
return mPatchShape.get();
}

void QgsSymbolRenderContext::setPatchShape( const QgsLegendPatchShape &patchShape )
{
mPatchShape.reset( new QgsLegendPatchShape( patchShape ) );
}

///////////////////

QgsMarkerSymbol *QgsMarkerSymbol::createSimple( const QgsStringMap &properties )
Expand Down
24 changes: 23 additions & 1 deletion src/core/symbology/qgssymbol.h
Expand Up @@ -50,6 +50,7 @@ class QgsCurve;
class QgsPolygon;
class QgsExpressionContext;
class QgsPoint;
class QgsLegendPatchShape;

typedef QList<QgsSymbolLayer *> QgsSymbolLayerList;

Expand Down Expand Up @@ -336,13 +337,15 @@ class CORE_EXPORT QgsSymbol
* \param customContext the context in which the rendering happens
* \param selected set to TRUE to render the symbol in a selected state
* \param expressionContext optional custom expression context
* \param patchShape optional patch shape to use for symbol preview. If not specified a default shape will be used instead.
*
* \see exportImage()
* \see asImage()
* \note Parameter selected added in QGIS 3.10
* \since QGIS 2.6
*/
void drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext = nullptr, bool selected = false, const QgsExpressionContext *expressionContext = nullptr );
void drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext = nullptr, bool selected = false, const QgsExpressionContext *expressionContext = nullptr,
const QgsLegendPatchShape *patchShape = nullptr );

/**
* Export the symbol as an image format, to the specified \a path and with the given \a size.
Expand Down Expand Up @@ -694,6 +697,8 @@ class CORE_EXPORT QgsSymbolRenderContext
*/
QgsSymbolRenderContext( QgsRenderContext &c, QgsUnitTypes::RenderUnit u, qreal opacity = 1.0, bool selected = false, QgsSymbol::RenderHints renderHints = nullptr, const QgsFeature *f = nullptr, const QgsFields &fields = QgsFields(), const QgsMapUnitScale &mapUnitScale = QgsMapUnitScale() );

~QgsSymbolRenderContext();

//! QgsSymbolRenderContext cannot be copied.
QgsSymbolRenderContext( const QgsSymbolRenderContext &rh ) = delete;

Expand Down Expand Up @@ -861,6 +866,22 @@ class CORE_EXPORT QgsSymbolRenderContext
*/
void setExpressionContextScope( QgsExpressionContextScope *contextScope SIP_TRANSFER );

/**
* Returns the symbol patch shape, to use if rendering symbol preview icons.
*
* \see setPatchShape()
* \since QGIS 3.14
*/
const QgsLegendPatchShape *patchShape() const;

/**
* Sets the symbol patch \a shape, to use if rendering symbol preview icons.
*
* \see patchShape()
* \since QGIS 3.14
*/
void setPatchShape( const QgsLegendPatchShape &shape );

private:

#ifdef SIP_RUN
Expand All @@ -879,6 +900,7 @@ class CORE_EXPORT QgsSymbolRenderContext
int mGeometryPartCount;
int mGeometryPartNum;
QgsWkbTypes::GeometryType mOriginalGeometryType = QgsWkbTypes::UnknownGeometry;
std::unique_ptr< QgsLegendPatchShape > mPatchShape;
};


Expand Down
68 changes: 40 additions & 28 deletions src/core/symbology/qgssymbollayer.cpp
Expand Up @@ -27,6 +27,8 @@
#include "qgsexpressioncontext.h"
#include "qgssymbollayerutils.h"
#include "qgsapplication.h"
#include "qgsmultipoint.h"
#include "qgslegendpatchshape.h"

#include <QSize>
#include <QPainter>
Expand Down Expand Up @@ -449,15 +451,19 @@ void QgsMarkerSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSi
{
startRender( context );
QgsPaintEffect *effect = paintEffect();

QPolygonF points = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Marker, size ).value( 0 ).value( 0 )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Marker, size ).value( 0 ).value( 0 );

std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
{
QgsEffectPainter p( context.renderContext(), effect );
renderPoint( QPointF( size.width() / 2, size.height() / 2 ), context );
}
else
{
renderPoint( QPointF( size.width() / 2, size.height() / 2 ), context );
}
effectPainter = qgis::make_unique< QgsEffectPainter >( context.renderContext(), effect );

for ( QPointF point : qgis::as_const( points ) )
renderPoint( point, context );

effectPainter.reset();

stopRender( context );
}

Expand Down Expand Up @@ -633,22 +639,20 @@ QgsMapUnitScale QgsLineSymbolLayer::mapUnitScale() const

void QgsLineSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
QPolygonF points;
// we're adding 0.5 to get rid of blurred preview:
// drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
points << QPointF( 0, int( size.height() / 2 ) + 0.5 ) << QPointF( size.width(), int( size.height() / 2 ) + 0.5 );

const QList< QList< QPolygonF > > points = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Line, size )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Line, size );
startRender( context );
QgsPaintEffect *effect = paintEffect();

std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
{
QgsEffectPainter p( context.renderContext(), effect );
renderPolyline( points, context );
}
else
{
renderPolyline( points, context );
}
effectPainter = qgis::make_unique< QgsEffectPainter >( context.renderContext(), effect );

for ( const QList< QPolygonF > &line : points )
renderPolyline( line.value( 0 ), context );

effectPainter.reset();

stopRender( context );
}

Expand Down Expand Up @@ -695,18 +699,26 @@ double QgsLineSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRenderConte

void QgsFillSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width(), size.height() ) );
const QList< QList< QPolygonF > > polys = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Fill, size )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Fill, size );

startRender( context );
QgsPaintEffect *effect = paintEffect();

std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
effectPainter = qgis::make_unique< QgsEffectPainter >( context.renderContext(), effect );

for ( const QList< QPolygonF > &poly : polys )
{
QgsEffectPainter p( context.renderContext(), effect );
renderPolygon( poly, nullptr, context );
}
else
{
renderPolygon( poly, nullptr, context );
QList< QPolygonF > rings;
for ( int i = 1; i < poly.size(); ++i )
rings << poly.at( i );
renderPolygon( poly.value( 0 ), &rings, context );
}

effectPainter.reset();

stopRender( context );
}

Expand Down

0 comments on commit 40638fe

Please sign in to comment.