Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add utility function QgsSymbolLayerUtils::condenseFillAndOutline
Attempts to condense a fill and outline layer, by moving the
outline layer to the fill symbol's stroke if possible.
  • Loading branch information
nyalldawson committed May 6, 2021
1 parent 6991f2c commit be2c538
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 1 deletion.
14 changes: 14 additions & 0 deletions python/core/auto_generated/symbology/qgssymbollayerutils.sip.in
Expand Up @@ -682,6 +682,20 @@ Blurs an image in place, e.g. creating Qt-independent drop shadows
Converts a QColor into a premultiplied ARGB QColor value using a specified alpha value

.. versionadded:: 2.3
%End

static bool condenseFillAndOutline( QgsFillSymbolLayer *fill, QgsLineSymbolLayer *outline );
%Docstring
Attempts to condense a ``fill`` and ``outline`` layer, by moving the outline layer to the
fill symbol's stroke.

This will only be done if the ``outline`` can be transformed into a stroke on the fill layer
losslessly. If so, ``fill`` will be updated in place with the new stroke. Any existing stroke
settings in ``fill`` will be replaced.

Returns ``True`` if the fill and outline were successfully condensed.

.. versionadded:: 3.20
%End

static void sortVariantList( QList<QVariant> &list, Qt::SortOrder order );
Expand Down
47 changes: 47 additions & 0 deletions src/core/symbology/qgssymbollayerutils.cpp
Expand Up @@ -36,6 +36,8 @@
#include "qgsstyleentityvisitor.h"
#include "qgsrenderer.h"
#include "qgsxmlutils.h"
#include "qgsfillsymbollayer.h"
#include "qgslinesymbollayer.h"

#include <QColor>
#include <QFont>
Expand Down Expand Up @@ -3917,6 +3919,51 @@ void QgsSymbolLayerUtils::premultiplyColor( QColor &rgb, int alpha )
}
}

bool QgsSymbolLayerUtils::condenseFillAndOutline( QgsFillSymbolLayer *fill, QgsLineSymbolLayer *outline )
{
QgsSimpleFillSymbolLayer *simpleFill = dynamic_cast< QgsSimpleFillSymbolLayer *>( fill );
QgsSimpleLineSymbolLayer *simpleLine = dynamic_cast< QgsSimpleLineSymbolLayer *>( outline );

if ( !simpleFill || !simpleLine )
return false;

if ( simpleLine->useCustomDashPattern() )
return false;

if ( simpleLine->dashPatternOffset() )
return false;

if ( simpleLine->alignDashPattern() )
return false;

if ( simpleLine->tweakDashPatternOnCorners() )
return false;

if ( simpleLine->trimDistanceStart() || simpleLine->trimDistanceEnd() )
return false;

if ( simpleLine->drawInsidePolygon() )
return false;

if ( simpleLine->ringFilter() != QgsSimpleLineSymbolLayer::AllRings )
return false;

if ( simpleLine->offset() )
return false;

if ( simpleLine->hasDataDefinedProperties() )
return false;

// looks good!
simpleFill->setStrokeColor( simpleLine->color() );
simpleFill->setStrokeWidth( simpleLine->width() );
simpleFill->setStrokeWidthUnit( simpleLine->widthUnit() );
simpleFill->setStrokeWidthMapUnitScale( simpleLine->widthMapUnitScale() );
simpleFill->setStrokeStyle( simpleLine->penStyle() );
simpleFill->setPenJoinStyle( simpleLine->penJoinStyle() );
return true;
}

void QgsSymbolLayerUtils::sortVariantList( QList<QVariant> &list, Qt::SortOrder order )
{
if ( order == Qt::AscendingOrder )
Expand Down
14 changes: 14 additions & 0 deletions src/core/symbology/qgssymbollayerutils.h
Expand Up @@ -623,6 +623,20 @@ class CORE_EXPORT QgsSymbolLayerUtils
*/
static void premultiplyColor( QColor &rgb, int alpha );

/**
* Attempts to condense a \a fill and \a outline layer, by moving the outline layer to the
* fill symbol's stroke.
*
* This will only be done if the \a outline can be transformed into a stroke on the fill layer
* losslessly. If so, \a fill will be updated in place with the new stroke. Any existing stroke
* settings in \a fill will be replaced.
*
* Returns TRUE if the fill and outline were successfully condensed.
*
* \since QGIS 3.20
*/
static bool condenseFillAndOutline( QgsFillSymbolLayer *fill, QgsLineSymbolLayer *outline );

//! Sorts the passed list in requested order
static void sortVariantList( QList<QVariant> &list, Qt::SortOrder order );
//! Returns a point on the line from startPoint to directionPoint that is a certain distance away from the starting point
Expand Down
80 changes: 79 additions & 1 deletion tests/src/python/test_qgssymbollayerutils.py
Expand Up @@ -30,7 +30,14 @@
QgsArrowSymbolLayer,
QgsUnitTypes,
QgsRenderChecker,
QgsGradientColorRamp
QgsGradientColorRamp,
QgsShapeburstFillSymbolLayer,
QgsMarkerLineSymbolLayer,
QgsSimpleLineSymbolLayer,
QgsSimpleFillSymbolLayer,
QgsSymbolLayer,
QgsProperty,
QgsMapUnitScale
)
from qgis.testing import unittest, start_app

Expand Down Expand Up @@ -470,6 +477,77 @@ def testPreviewColorRampVerticalFlipped(self):
img = QImage(pix)
self.assertTrue(self.imageCheck('color_ramp_vertical_flipped', 'color_ramp_vertical_flipped', img))

def testCondenseFillAndOutline(self):
"""
Test QgsSymbolLayerUtils.condenseFillAndOutline
"""
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(None, None))

# not simple fill or line
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(QgsShapeburstFillSymbolLayer(), QgsSimpleLineSymbolLayer()))
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(QgsSimpleFillSymbolLayer(), QgsMarkerLineSymbolLayer()))

# simple fill/line
fill = QgsSimpleFillSymbolLayer()
line = QgsSimpleLineSymbolLayer()

# set incompatible settings on outline
line.setUseCustomDashPattern(True)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setDashPatternOffset(1)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setAlignDashPattern(True)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setTweakDashPatternOnCorners(True)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setTrimDistanceStart(1)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setTrimDistanceEnd(1)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setDrawInsidePolygon(True)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setRingFilter(QgsSimpleLineSymbolLayer.ExteriorRingOnly)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setOffset(1)
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

line = QgsSimpleLineSymbolLayer()
line.setDataDefinedProperty(QgsSymbolLayer.PropertyTrimEnd, QgsProperty.fromValue(4))
self.assertFalse(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

# compatible!
line = QgsSimpleLineSymbolLayer()
line.setColor(QColor(255, 0, 0))
line.setWidth(1.2)
line.setWidthUnit(QgsUnitTypes.RenderPoints)
line.setWidthMapUnitScale(QgsMapUnitScale(1, 2))
line.setPenJoinStyle(Qt.MiterJoin)
line.setPenStyle(Qt.DashDotDotLine)
self.assertTrue(QgsSymbolLayerUtils.condenseFillAndOutline(fill, line))

self.assertEqual(fill.strokeColor(), QColor(255, 0, 0))
self.assertEqual(fill.strokeWidth(), 1.2)
self.assertEqual(fill.strokeWidthUnit(), QgsUnitTypes.RenderPoints)
self.assertEqual(fill.strokeWidthMapUnitScale(), QgsMapUnitScale(1, 2))
self.assertEqual(fill.penJoinStyle(), Qt.MiterJoin)
self.assertEqual(fill.strokeStyle(), Qt.DashDotDotLine)

def imageCheck(self, name, reference_image, image):
self.report += "<h2>Render {}</h2>\n".format(name)
temp_dir = QDir.tempPath() + '/'
Expand Down

0 comments on commit be2c538

Please sign in to comment.