Skip to content
Permalink
Browse files

When we are rendering symbol preview icons, we can't handle

sizes set in Meters in Map Units, because there's no map context
available to calculate these with

So, to avoid dangerously small or large sizes, when we are creating
preview images we have to just treat these sizes as mm and then clamp
them to reasonable size ranges depending on the symbol property

This fixes the quasi-hang from #28690, where a marker line symbol
with interval in meters in map units caused a symbol preview
icon to be drawn with an extremely tiny interval -- resulting
in an effectively endless loop while trying to render multiple
billion markers around the edge of the symbol preview icon.

It's not ideal, but there's no better approach we can take here!

Fixes #28690
  • Loading branch information
nyalldawson committed Jun 16, 2020
1 parent e49f357 commit 7c6286f8dfb7cc24e5b06c9ab950d9dd55fac517
@@ -353,6 +353,13 @@ void QgsSimpleLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbo
else
{
double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
if ( mOffsetUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
scaledOffset = std::min( std::max( context.renderContext().convertToPainterUnits( offset, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
}

QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != QgsWkbTypes::UnknownGeometry ? context.originalGeometryType() : QgsWkbTypes::LineGeometry );
for ( int part = 0; part < mline.count(); ++part )
{
@@ -1109,12 +1116,25 @@ void QgsTemplatedLineSymbolLayerBase::renderPolylineInterval( const QPolygonF &p
offsetAlongLine = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyOffsetAlongLine, context.renderContext().expressionContext(), mOffsetAlongLine );
}

const double painterUnitInterval = rc.convertToPainterUnits( interval, intervalUnit(), intervalMapUnitScale() );
double painterUnitInterval = rc.convertToPainterUnits( interval, intervalUnit(), intervalMapUnitScale() );
if ( intervalUnit() == QgsUnitTypes::RenderMetersInMapUnits && rc.flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- an interval in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
painterUnitInterval = std::min( std::max( rc.convertToPainterUnits( interval, QgsUnitTypes::RenderMillimeters ), 10.0 ), 100.0 );
}

if ( painterUnitInterval < 0 )
return;

const double painterUnitOffsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
double painterUnitOffsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
if ( offsetAlongLineUnit() == QgsUnitTypes::RenderMetersInMapUnits && rc.flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- an offset in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
painterUnitOffsetAlongLine = std::min( std::max( rc.convertToPainterUnits( offsetAlongLine, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
}

lengthLeft = painterUnitInterval - painterUnitOffsetAlongLine;

if ( averageOver > 0 && !qgsDoubleNear( averageOver, 0.0 ) )
@@ -149,6 +149,13 @@ void QgsSimpleMarkerSymbolLayerBase::startRender( QgsSymbolRenderContext &contex
if ( !hasDataDefinedSize )
{
double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
}

double half = scaledSize / 2.0;
transform.scale( half, half );
}
@@ -228,6 +235,12 @@ void QgsSimpleMarkerSymbolLayerBase::renderPoint( QPointF point, QgsSymbolRender
if ( hasDataDefinedSize || createdNewPath )
{
double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
}
double half = s / 2.0;
transform.scale( half, half );
}
@@ -865,6 +878,13 @@ void QgsSimpleMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
bool QgsSimpleMarkerSymbolLayer::prepareCache( QgsSymbolRenderContext &context )
{
double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
if ( mSizeUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
}

// take into account angle (which is not data-defined otherwise cache wouldn't be used)
if ( !qgsDoubleNear( mAngle, 0.0 ) )
{
@@ -529,7 +529,21 @@ void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double
}

double anchorPointCorrectionX = context.renderContext().convertToPainterUnits( width, widthUnit, widthMapUnitScale ) / 2.0;
if ( widthUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
anchorPointCorrectionX = std::min( std::max( context.renderContext().convertToPainterUnits( width, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 ) / 2.0;
}

double anchorPointCorrectionY = context.renderContext().convertToPainterUnits( height, heightUnit, heightMapUnitScale ) / 2.0;
if ( heightUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & QgsRenderContext::RenderSymbolPreview )
{
// rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
// and clamp it to a reasonable range. It's the best we can do in this situation!
anchorPointCorrectionY = std::min( std::max( context.renderContext().convertToPainterUnits( height, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 ) / 2.0;
}

if ( horizontalAnchorPoint == Left )
{
offsetX += anchorPointCorrectionX;

0 comments on commit 7c6286f

Please sign in to comment.
You can’t perform that action at this time.