Skip to content

Commit

Permalink
Avoid trying to calculate excessively large spread/blurs in
Browse files Browse the repository at this point in the history
preview icons for draw effects

This can quasi-hang the QGIS interface

Fixes #41149
  • Loading branch information
nyalldawson committed Sep 27, 2021
1 parent 615f68a commit 49d4b95
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 7 deletions.
28 changes: 25 additions & 3 deletions src/core/effects/qgsblureffect.cpp
Expand Up @@ -45,15 +45,29 @@ void QgsBlurEffect::draw( QgsRenderContext &context )

void QgsBlurEffect::drawStackBlur( QgsRenderContext &context )
{
const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur or offset in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
}

QImage im = sourceAsImage( context )->copy();
QgsImageOperation::stackBlur( im, blurLevel, false, context.feedback() );
drawBlurredImage( context, im );
}

void QgsBlurEffect::drawGaussianBlur( QgsRenderContext &context )
{
const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur or offset in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
}

QImage *im = QgsImageOperation::gaussianBlur( *sourceAsImage( context ), blurLevel, context.feedback() );
if ( !im->isNull() )
drawBlurredImage( context, *im );
Expand Down Expand Up @@ -139,7 +153,15 @@ QgsBlurEffect *QgsBlurEffect::clone() const

QRectF QgsBlurEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
{
const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );

if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur or offset in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
}

//plus possible extension due to blur, with a couple of extra pixels thrown in for safety
const double spread = blurLevel * 2.0 + 10;
return rect.adjusted( -spread, -spread, spread, spread );
Expand Down
28 changes: 26 additions & 2 deletions src/core/effects/qgsgloweffect.cpp
Expand Up @@ -60,6 +60,14 @@ void QgsGlowEffect::draw( QgsRenderContext &context )
ramp = tempRamp.get();
}

double spread = context.convertToPainterUnits( mSpread, mSpreadUnit, mSpreadMapUnitScale );
if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessive spread in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
spread = std::min( spread, 50.0 );
}

QgsImageOperation::DistanceTransformProperties dtProps;
dtProps.spread = context.convertToPainterUnits( mSpread, mSpreadUnit, mSpreadMapUnitScale );
dtProps.useMaxDistance = false;
Expand All @@ -70,7 +78,14 @@ void QgsGlowEffect::draw( QgsRenderContext &context )
if ( context.feedback() && context.feedback()->isCanceled() )
return;

const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessive blur in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
}

if ( blurLevel <= 16 )
{
QgsImageOperation::stackBlur( im, blurLevel, false, context.feedback() );
Expand Down Expand Up @@ -233,8 +248,17 @@ QgsGlowEffect &QgsGlowEffect::operator=( const QgsGlowEffect &rhs )
QRectF QgsGlowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
{
//blur radius and spread size
const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
double spread = context.convertToPainterUnits( mSpread, mSpreadUnit, mSpreadMapUnitScale );

if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur or spread in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
spread = std::min( spread, 50.0 );
}

//plus possible extension due to blur, with a couple of extra pixels thrown in for safety
spread += blurLevel * 2 + 10;
return rect.adjusted( -spread, -spread, spread, spread );
Expand Down
6 changes: 6 additions & 0 deletions src/core/effects/qgsimageoperation.cpp
Expand Up @@ -717,6 +717,9 @@ void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::Im
destRef = reinterpret_cast< QRgb * >( outputLineRef );
for ( int x = 0; x < width; ++x, ++destRef, sourceRef += 4 )
{
if ( mFeedback && mFeedback->isCanceled() )
break;

*destRef = gaussianBlurVertical( y, sourceRef, sourceBpl, height );
}
}
Expand All @@ -732,6 +735,9 @@ void QgsImageOperation::GaussianBlurOperation::operator()( QgsImageOperation::Im
destRef = reinterpret_cast< QRgb * >( outputLineRef );
for ( int x = 0; x < width; ++x, ++destRef )
{
if ( mFeedback && mFeedback->isCanceled() )
break;

*destRef = gaussianBlurHorizontal( x, sourceRef, width );
}
}
Expand Down
21 changes: 19 additions & 2 deletions src/core/effects/qgsshadoweffect.cpp
Expand Up @@ -52,7 +52,14 @@ void QgsShadowEffect::draw( QgsRenderContext &context )

QgsImageOperation::overlayColor( colorisedIm, mColor );

const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
}

if ( blurLevel <= 16 )
{
QgsImageOperation::stackBlur( colorisedIm, blurLevel, false, context.feedback() );
Expand Down Expand Up @@ -174,8 +181,18 @@ void QgsShadowEffect::readProperties( const QVariantMap &props )
QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
{
//blur radius and offset distance
const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );

double spread = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale );

if ( context.flags() & QgsRenderContext::Flag::RenderSymbolPreview )
{
// avoid excessively large blur or offset in symbol preview icons -- it's too slow to calculate, and unnecessary
// for just a preview icon
blurLevel = std::min( blurLevel, 30 );
spread = std::min( spread, 100.0 );
}

//plus possible extension due to blur, with a couple of extra pixels thrown in for safety
spread += blurLevel * 2 + 10;
return rect.adjusted( -spread, -spread, spread, spread );
Expand Down
1 change: 1 addition & 0 deletions src/gui/effects/qgseffectstackpropertieswidget.cpp
Expand Up @@ -211,6 +211,7 @@ void QgsEffectStackPropertiesWidget::updatePreview()
painter.begin( &previewImage );
painter.setRenderHint( QPainter::Antialiasing );
QgsRenderContext context = QgsRenderContext::fromQPainter( &painter );
context.setFlag( QgsRenderContext::RenderSymbolPreview, true );
if ( mPreviewPicture.isNull() )
{
QPicture previewPic;
Expand Down

0 comments on commit 49d4b95

Please sign in to comment.