QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsshadoweffect.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsshadoweffect.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 "qgsshadoweffect.h"
19#include "qgsimageoperation.h"
20#include "qgssymbollayerutils.h"
21#include "qgsunittypes.h"
22
24 : mColor( Qt::black )
25{
26
27}
28
30{
31 if ( !source() || !enabled() || !context.painter() )
32 return;
33
34 if ( context.feedback() && context.feedback()->isCanceled() )
35 return;
36
37 QImage colorisedIm = sourceAsImage( context )->copy();
38
39 if ( context.feedback() && context.feedback()->isCanceled() )
40 return;
41
42 QPainter *painter = context.painter();
43 const QgsScopedQPainterState painterState( painter );
44 painter->setCompositionMode( mBlendMode );
45
46 if ( !exteriorShadow() )
47 {
48 //inner shadow, first invert the opacity. The color does not matter since we will
49 //be replacing it anyway
50 colorisedIm.invertPixels( QImage::InvertRgba );
51 }
52
54
56 if ( blurLevel <= 16 )
57 {
58 QgsImageOperation::stackBlur( colorisedIm, blurLevel, false, context.feedback() );
59 }
60 else
61 {
62 QImage *imb = QgsImageOperation::gaussianBlur( colorisedIm, blurLevel, context.feedback() );
63 if ( !imb->isNull() )
64 colorisedIm = QImage( *imb );
65 delete imb;
66 }
67
68 const double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale );
69
70 const double angleRad = mOffsetAngle * M_PI / 180; // to radians
71 const QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
72 -offsetDist * std::sin( angleRad + M_PI_2 ) );
73
74 //transparency, scale
75 QgsImageOperation::multiplyOpacity( colorisedIm, mOpacity, context.feedback() );
76
77 if ( !exteriorShadow() )
78 {
79 //inner shadow, do a bit of painter juggling
80 QImage innerShadowIm( colorisedIm.width(), colorisedIm.height(), QImage::Format_ARGB32 );
81 innerShadowIm.fill( Qt::transparent );
82 QPainter imPainter( &innerShadowIm );
83
84 //draw shadow at offset
85 imPainter.drawImage( transPt.x(), transPt.y(), colorisedIm );
86
87 //restrict shadow so it's only drawn on top of original image
88 imPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
89 imPainter.drawImage( 0, 0, *sourceAsImage( context ) );
90 imPainter.end();
91
92 painter->drawImage( imageOffset( context ), innerShadowIm );
93 }
94 else
95 {
96 painter->drawImage( imageOffset( context ) + transPt, colorisedIm );
97 }
98}
99
101{
102 QVariantMap props;
103 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
104 props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
105 props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
106 props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
107 props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
108 props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) );
109 props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) );
110 props.insert( QStringLiteral( "offset_angle" ), QString::number( mOffsetAngle ) );
111 props.insert( QStringLiteral( "offset_distance" ), QString::number( mOffsetDist ) );
112 props.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
113 props.insert( QStringLiteral( "offset_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
114 props.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
115 return props;
116}
117
118void QgsShadowEffect::readProperties( const QVariantMap &props )
119{
120 bool ok;
121 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
122 if ( ok )
123 {
124 mBlendMode = mode;
125 }
126 if ( props.contains( QStringLiteral( "transparency" ) ) )
127 {
128 const double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
129 if ( ok )
130 {
131 mOpacity = 1.0 - transparency;
132 }
133 }
134 else
135 {
136 const double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
137 if ( ok )
138 {
140 }
141 }
142 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
143 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
144 const double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok );
145 if ( ok )
146 {
147 mBlurLevel = level;
148 if ( !props.contains( QStringLiteral( "blur_unit" ) ) )
149 {
150 // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters
151 mBlurLevel *= 0.2645;
152 }
153 }
154 mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() );
155 mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() );
156 const int angle = props.value( QStringLiteral( "offset_angle" ) ).toInt( &ok );
157 if ( ok )
158 {
160 }
161 const double distance = props.value( QStringLiteral( "offset_distance" ) ).toDouble( &ok );
162 if ( ok )
163 {
164 mOffsetDist = distance;
165 }
166 mOffsetUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offset_unit" ) ).toString() );
167 mOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offset_unit_scale" ) ).toString() );
168 if ( props.contains( QStringLiteral( "color" ) ) )
169 {
170 mColor = QgsSymbolLayerUtils::decodeColor( props.value( QStringLiteral( "color" ) ).toString() );
171 }
172}
173
174QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
175{
176 //blur radius and offset distance
178
179 // spread is initially the shadow offset size
181
182 //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
183 spread += blurLevel * 2 + 10;
184 return rect.adjusted( -spread, -spread, spread, spread );
185}
186
187
188//
189// QgsDropShadowEffect
190//
191
193{
195 effect->readProperties( map );
196 return effect;
197}
198
201{
202
203}
204
206{
207 return QStringLiteral( "dropShadow" );
208}
209
211{
212 return new QgsDropShadowEffect( *this );
213}
214
216{
217 return true;
218}
219
220
221//
222// QgsInnerShadowEffect
223//
224
226{
228 effect->readProperties( map );
229 return effect;
230}
231
234{
235
236}
237
239{
240 return QStringLiteral( "innerShadow" );
241}
242
244{
245 return new QgsInnerShadowEffect( *this );
246}
247
249{
250 return false;
251}
A paint effect which draws an offset and optionally blurred drop shadow.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsDropShadowEffect effect from a properties string map.
QString type() const override
Returns the effect type.
QgsDropShadowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
bool exteriorShadow() const override
Specifies whether the shadow is drawn outside the picture or within the picture.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
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.
A paint effect which draws an offset and optionally blurred drop shadow within a picture.
bool exteriorShadow() const override
Specifies whether the shadow is drawn outside the picture or within the picture.
QString type() const override
Returns the effect type.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsInnerShadowEffect effect from a properties string map.
QgsInnerShadowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
Base class for visual effects which can be applied to QPicture drawings.
DrawMode mDrawMode
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
const QPicture * source() const
Returns the source QPicture.
bool enabled() const
Returns whether the effect is enabled.
DrawMode
Drawing modes for effects.
QImage * sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
Scoped object for saving and restoring a QPainter object's state.
Base class for paint effects which offset, blurred shadows.
QVariantMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
double opacity() const
Returns the opacity for the effect.
QgsUnitTypes::RenderUnit mOffsetUnit
QgsUnitTypes::RenderUnit mBlurUnit
virtual bool exteriorShadow() const =0
Specifies whether the shadow is drawn outside the picture or within the picture.
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
QgsMapUnitScale mOffsetMapUnitScale
QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const override
Returns the bounding rect required for drawing the effect.
double blurLevel() const
Returns the blur level (radius) for the shadow.
QPainter::CompositionMode mBlendMode
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...
QgsMapUnitScale mBlurMapUnitScale
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QColor decodeColor(const QString &str)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QString encodeColor(const QColor &color)
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786