QGIS API Documentation 3.99.0-Master (b927df884fe)
qgsblureffect.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsblureffect.cpp
3 -----------------
4 begin : December 2014
5 copyright : (C) 2014 Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsblureffect.h"
19#include "qgsimageoperation.h"
20#include "qgsrendercontext.h"
21#include "qgssymbollayerutils.h"
22#include "qgsunittypes.h"
23
24QgsPaintEffect *QgsBlurEffect::create( const QVariantMap &map )
25{
26 QgsBlurEffect *newEffect = new QgsBlurEffect();
27 newEffect->readProperties( map );
28 return newEffect;
29}
30
35
37{
38 if ( !enabled() || !context.painter() || source().isNull() )
39 return;
40
42 {
43 //just draw unmodified source, we can't render this effect when forcing vectors
44 drawSource( *context.painter() );
45 return;
46 }
47
48 switch ( mBlurMethod )
49 {
50 case StackBlur:
51 drawStackBlur( context );
52 break;
53 case GaussianBlur:
54 drawGaussianBlur( context );
55 break;
56 }
57}
58
59void QgsBlurEffect::drawStackBlur( QgsRenderContext &context )
60{
61 const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale, Qgis::RenderSubcomponentProperty::BlurSize ) );
62
63 QImage im = sourceAsImage( context ).copy();
64 QgsImageOperation::stackBlur( im, blurLevel, false, context.feedback() );
65 drawBlurredImage( context, im );
66}
67
68void QgsBlurEffect::drawGaussianBlur( QgsRenderContext &context )
69{
70 const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale, Qgis::RenderSubcomponentProperty::BlurSize ) );
71
72 QImage source = sourceAsImage( context ).copy();
73 QImage *im = QgsImageOperation::gaussianBlur( source, blurLevel, context.feedback() );
74 if ( !im->isNull() )
75 drawBlurredImage( context, *im );
76 delete im;
77}
78
79void QgsBlurEffect::drawBlurredImage( QgsRenderContext &context, QImage &image )
80{
81 //opacity
82 QgsImageOperation::multiplyOpacity( image, mOpacity, context.feedback() );
83
84 QPainter *painter = context.painter();
85 const QgsScopedQPainterState painterState( painter );
86 painter->setCompositionMode( mBlendMode );
87 painter->drawImage( imageOffset( context ), image );
88}
89
90QVariantMap QgsBlurEffect::properties() const
91{
92 QVariantMap props;
93 props.insert( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
94 props.insert( QStringLiteral( "draw_mode" ), QString::number( static_cast< int >( mDrawMode ) ) );
95 props.insert( QStringLiteral( "blend_mode" ), QString::number( static_cast< int >( mBlendMode ) ) );
96 props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
97 props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
98 props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) );
99 props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) );
100 props.insert( QStringLiteral( "blur_method" ), QString::number( static_cast< int >( mBlurMethod ) ) );
101 return props;
102}
103
104void QgsBlurEffect::readProperties( const QVariantMap &props )
105{
106 bool ok;
107 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
108 if ( ok )
109 {
110 mBlendMode = mode;
111 }
112 if ( props.contains( QStringLiteral( "transparency" ) ) )
113 {
114 const double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
115 if ( ok )
116 {
117 mOpacity = 1.0 - transparency;
118 }
119 }
120 else
121 {
122 const double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
123 if ( ok )
124 {
125 mOpacity = opacity;
126 }
127 }
128
129 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
130 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
131 const double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok );
132 if ( ok )
133 {
134 mBlurLevel = level;
135 if ( !props.contains( QStringLiteral( "blur_unit" ) ) )
136 {
137 // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters
138 mBlurLevel *= 0.2645;
139 }
140 }
141 mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() );
142 mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() );
143 const QgsBlurEffect::BlurMethod method = static_cast< QgsBlurEffect::BlurMethod >( props.value( QStringLiteral( "blur_method" ) ).toInt( &ok ) );
144 if ( ok )
145 {
146 mBlurMethod = method;
147 }
148}
149
151{
152 QgsBlurEffect *newEffect = new QgsBlurEffect( *this );
153 return newEffect;
154}
155
156QRectF QgsBlurEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
157{
158 const int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale, Qgis::RenderSubcomponentProperty::BlurSize ) );
159
160 //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
161 const double spread = blurLevel * 2.0 + 10;
162 return rect.adjusted( -spread, -spread, spread, spread );
163}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
@ RequiresRasterization
The effect requires raster-based rendering.
QFlags< PaintEffectFlag > PaintEffectFlags
Flags which control how paint effects behave.
Definition qgis.h:2765
A paint effect which blurs a source picture, using a number of different blur methods.
QgsBlurEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
QVariantMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const override
Returns the bounding rect required for drawing the effect.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsBlurEffect effect from a properties string map.
QgsBlurEffect()=default
void readProperties(const QVariantMap &props) override
Reads a string map of an effect's properties and restores the effect to the state described by the pr...
BlurMethod
Available blur methods (algorithms)
@ GaussianBlur
Gaussian blur, a slower but high quality blur. Blur level values are the distance in pixels for the b...
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
Qgis::PaintEffectFlags flags() const override
Returns flags which specify how the paint effect behaves.
double opacity() const
Returns the opacity for the effect.
double blurLevel() const
Returns the blur level (radius)
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
static QImage * gaussianBlur(QImage &image, int radius, QgsFeedback *feedback=nullptr)
Performs a gaussian blur on an image.
static void stackBlur(QImage &image, int radius, bool alphaOnly=false, QgsFeedback *feedback=nullptr)
Performs a stack blur on an image.
Base class for visual effects which can be applied to QPicture drawings.
void drawSource(QPainter &painter)
Draws the source QPicture onto the specified painter.
QImage sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
const QPicture & source() const
Returns the source QPicture.
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
bool enabled() const
Returns whether the effect is enabled.
DrawMode
Drawing modes for effects.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
Scoped object for saving and restoring a QPainter object's state.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.