QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgsgloweffect.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgloweffect.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 "qgsgloweffect.h"
19
20#include <memory>
21
22#include "qgscolorrampimpl.h"
23#include "qgscolorutils.h"
24#include "qgsimageoperation.h"
25#include "qgssymbollayerutils.h"
26#include "qgsunittypes.h"
27
29 : mColor( Qt::white )
30{
31
32}
33
35 : QgsPaintEffect( other )
36{
37 operator=( other );
38}
39
44
49
51{
52 if ( !enabled() || !context.painter() || source().isNull() )
53 return;
54
56 {
57 //just draw unmodified source, we can't render this effect when forcing vectors
58 drawSource( *context.painter() );
59 return;
60 }
61
62 QImage im = sourceAsImage( context ).copy();
63
64 QgsColorRamp *ramp = nullptr;
65 std::unique_ptr< QgsGradientColorRamp > tempRamp;
66 if ( mColorType == ColorRamp && mRamp )
67 {
68 ramp = mRamp;
69 }
70 else
71 {
72 //create a temporary ramp
73 QColor transparentColor = mColor;
74 transparentColor.setAlpha( 0 );
75 tempRamp = std::make_unique<QgsGradientColorRamp>( mColor, transparentColor );
76 ramp = tempRamp.get();
77 }
78
81 dtProps.useMaxDistance = false;
82 dtProps.shadeExterior = shadeExterior();
83 dtProps.ramp = ramp;
84 QgsImageOperation::distanceTransform( im, dtProps, context.feedback() );
85
86 if ( context.feedback() && context.feedback()->isCanceled() )
87 return;
88
90 if ( blurLevel <= 16 )
91 {
92 QgsImageOperation::stackBlur( im, blurLevel, false, context.feedback() );
93 }
94 else
95 {
96 QImage *imb = QgsImageOperation::gaussianBlur( im, blurLevel, context.feedback() );
97 if ( !imb->isNull() )
98 im = QImage( *imb );
99 delete imb;
100 }
101
102 if ( context.feedback() && context.feedback()->isCanceled() )
103 return;
104
106
107 if ( context.feedback() && context.feedback()->isCanceled() )
108 return;
109
110 if ( !shadeExterior() )
111 {
112 //only keep interior portion
113 QPainter p( &im );
114 p.setRenderHint( QPainter::Antialiasing );
115 p.setCompositionMode( QPainter::CompositionMode_DestinationIn );
116 p.drawImage( 0, 0, sourceAsImage( context ) );
117 p.end();
118 }
119
120 QPainter *painter = context.painter();
121 const QgsScopedQPainterState painterState( painter );
122 painter->setCompositionMode( mBlendMode );
123 painter->drawImage( imageOffset( context ), im );
124}
125
126QVariantMap QgsGlowEffect::properties() const
127{
128 QVariantMap props;
129 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
130 props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
131 props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
132 props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
133 props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
134 props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) );
135 props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) );
136 props.insert( QStringLiteral( "spread" ), QString::number( mSpread ) );
137 props.insert( QStringLiteral( "spread_unit" ), QgsUnitTypes::encodeUnit( mSpreadUnit ) );
138 props.insert( QStringLiteral( "spread_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSpreadMapUnitScale ) );
139 props.insert( QStringLiteral( "color_type" ), QString::number( static_cast< int >( mColorType ) ) );
140 props.insert( QStringLiteral( "single_color" ), QgsColorUtils::colorToString( mColor ) );
141
142 if ( mRamp )
143 {
144 props.insert( mRamp->properties() );
145 }
146
147 return props;
148}
149
150void QgsGlowEffect::readProperties( const QVariantMap &props )
151{
152 bool ok;
153 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
154 if ( ok )
155 {
156 mBlendMode = mode;
157 }
158 if ( props.contains( QStringLiteral( "transparency" ) ) )
159 {
160 const double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
161 if ( ok )
162 {
163 mOpacity = 1.0 - transparency;
164 }
165 }
166 else
167 {
168 const double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
169 if ( ok )
170 {
172 }
173 }
174 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
175 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
176 const double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok );
177 if ( ok )
178 {
179 mBlurLevel = level;
180 if ( !props.contains( QStringLiteral( "blur_unit" ) ) )
181 {
182 // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters
183 mBlurLevel *= 0.2645;
184 }
185 }
186 mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() );
187 mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() );
188 const double spread = props.value( QStringLiteral( "spread" ) ).toDouble( &ok );
189 if ( ok )
190 {
191 mSpread = spread;
192 }
193 mSpreadUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "spread_unit" ) ).toString() );
194 mSpreadMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "spread_unit_scale" ) ).toString() );
195 const QgsGlowEffect::GlowColorType type = static_cast< QgsGlowEffect::GlowColorType >( props.value( QStringLiteral( "color_type" ) ).toInt( &ok ) );
196 if ( ok )
197 {
199 }
200 if ( props.contains( QStringLiteral( "single_color" ) ) )
201 {
202 mColor = QgsColorUtils::colorFromString( props.value( QStringLiteral( "single_color" ) ).toString() );
203 }
204
205 //attempt to create color ramp from props
206 delete mRamp;
207 if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QgsCptCityColorRamp::typeString() )
208 {
210 }
211 else
212 {
214 }
215}
216
218{
219 delete mRamp;
220 mRamp = ramp;
221}
222
224{
225 if ( &rhs == this )
226 return *this;
227
228 delete mRamp;
229
230 mSpread = rhs.spread();
231 mSpreadUnit = rhs.spreadUnit();
233 mRamp = rhs.ramp() ? rhs.ramp()->clone() : nullptr;
234 mBlurLevel = rhs.blurLevel();
235 mBlurUnit = rhs.mBlurUnit;
237 mOpacity = rhs.opacity();
238 mColor = rhs.color();
239 mBlendMode = rhs.blendMode();
240 mColorType = rhs.colorType();
241
242 return *this;
243}
244
245QRectF QgsGlowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
246{
247 //blur radius and spread size
250
251 //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
252 spread += blurLevel * 2 + 10;
253 return rect.adjusted( -spread, -spread, spread, spread );
254}
255
256
257//
258// QgsOuterGlowEffect
259//
260
266
268{
270 effect->readProperties( map );
271 return effect;
272}
273
275{
276 QgsOuterGlowEffect *newEffect = new QgsOuterGlowEffect( *this );
277 return newEffect;
278}
279
280
281//
282// QgsInnerGlowEffect
283//
284
290
292{
294 effect->readProperties( map );
295 return effect;
296}
297
299{
300 QgsInnerGlowEffect *newEffect = new QgsInnerGlowEffect( *this );
301 return newEffect;
302}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2706
@ RequiresRasterization
The effect requires raster-based rendering.
Definition qgis.h:2806
@ GlowSpread
Glow spread size.
Definition qgis.h:3057
QFlags< PaintEffectFlag > PaintEffectFlags
Flags which control how paint effects behave.
Definition qgis.h:2815
Abstract base class for color ramps.
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
static QgsColorRamp * create(const QVariantMap &properties=QVariantMap())
Creates the symbol layer.
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
QgsGlowEffect & operator=(const QgsGlowEffect &rhs)
QgsMapUnitScale mBlurMapUnitScale
~QgsGlowEffect() override
Qgis::RenderUnit mSpreadUnit
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
double spread() const
Returns the spread distance used for drawing the glow effect.
virtual bool shadeExterior() const =0
Specifies whether the glow is drawn outside the picture or within the picture.
const QgsMapUnitScale & spreadMapUnitScale() const
Returns the map unit scale used for the spread distance.
GlowColorType
Color sources for the glow.
@ ColorRamp
Use colors from a color ramp.
QPainter::CompositionMode blendMode() const
Returns the blend mode for the effect.
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...
QgsColorRamp * mRamp
double blurLevel() const
Returns the blur level (radius) for the glow.
double opacity() const
Returns the opacity for the effect.
Qgis::RenderUnit mBlurUnit
QColor color() const
Returns the color for the glow.
GlowColorType mColorType
QgsColorRamp * ramp() const
Returns the color ramp used for the glow.
Qgis::RenderUnit spreadUnit() const
Returns the units used for the glow spread distance.
QPainter::CompositionMode mBlendMode
QgsMapUnitScale mSpreadMapUnitScale
void setRamp(QgsColorRamp *ramp)
Sets the color ramp for the glow.
QVariantMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const override
Returns the bounding rect required for drawing the effect.
GlowColorType colorType() const
Returns the color mode used for the glow.
Qgis::PaintEffectFlags flags() const override
Returns flags which specify how the paint effect behaves.
static QgsColorRamp * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsColorRamp from a map of properties.
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
static void distanceTransform(QImage &image, const QgsImageOperation::DistanceTransformProperties &properties, QgsFeedback *feedback=nullptr)
Performs a distance transform on the source image and shades the result using a color ramp.
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.
QgsInnerGlowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsInnerGlowEffect effect from a properties string map.
QgsOuterGlowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsOuterGlowEffect effect from a properties string map.
QgsPaintEffect()=default
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.
virtual QString type() const =0
Returns the effect type.
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.
Struct for storing properties of a distance transform operation.
bool useMaxDistance
Set to true to automatically calculate the maximum distance in the transform to use as the spread val...
bool shadeExterior
Set to true to perform the distance transform on transparent pixels in the source image,...
double spread
Maximum distance (in pixels) for the distance transform shading to spread.
QgsColorRamp * ramp
Color ramp to use for shading the distance transform.