QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgscolorutils.h"
22#include "qgsunittypes.h"
23
25 : mColor( Qt::black )
26{
27
28}
29
31{
32 if ( !source() || !enabled() || !context.painter() )
33 return;
34
35 if ( context.feedback() && context.feedback()->isCanceled() )
36 return;
37
38 QImage colorisedIm = sourceAsImage( context )->copy();
39
40 if ( context.feedback() && context.feedback()->isCanceled() )
41 return;
42
43 QPainter *painter = context.painter();
44 const QgsScopedQPainterState painterState( painter );
45 painter->setCompositionMode( mBlendMode );
46
47 if ( !exteriorShadow() )
48 {
49 //inner shadow, first invert the opacity. The color does not matter since we will
50 //be replacing it anyway
51 colorisedIm.invertPixels( QImage::InvertRgba );
52 }
53
55
57 if ( blurLevel <= 16 )
58 {
59 QgsImageOperation::stackBlur( colorisedIm, blurLevel, false, context.feedback() );
60 }
61 else
62 {
63 QImage *imb = QgsImageOperation::gaussianBlur( colorisedIm, blurLevel, context.feedback() );
64 if ( !imb->isNull() )
65 colorisedIm = QImage( *imb );
66 delete imb;
67 }
68
69 const double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale );
70
71 const double angleRad = mOffsetAngle * M_PI / 180; // to radians
72 const QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
73 -offsetDist * std::sin( angleRad + M_PI_2 ) );
74
75 //transparency, scale
76 QgsImageOperation::multiplyOpacity( colorisedIm, mOpacity, context.feedback() );
77
78 if ( !exteriorShadow() )
79 {
80 //inner shadow, do a bit of painter juggling
81 QImage innerShadowIm( colorisedIm.width(), colorisedIm.height(), QImage::Format_ARGB32 );
82 innerShadowIm.fill( Qt::transparent );
83 QPainter imPainter( &innerShadowIm );
84
85 //draw shadow at offset
86 imPainter.drawImage( transPt.x(), transPt.y(), colorisedIm );
87
88 //restrict shadow so it's only drawn on top of original image
89 imPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
90 imPainter.drawImage( 0, 0, *sourceAsImage( context ) );
91 imPainter.end();
92
93 painter->drawImage( imageOffset( context ), innerShadowIm );
94 }
95 else
96 {
97 painter->drawImage( imageOffset( context ) + transPt, colorisedIm );
98 }
99}
100
102{
103 QVariantMap props;
104 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
105 props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
106 props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
107 props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
108 props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
109 props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) );
110 props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) );
111 props.insert( QStringLiteral( "offset_angle" ), QString::number( mOffsetAngle ) );
112 props.insert( QStringLiteral( "offset_distance" ), QString::number( mOffsetDist ) );
113 props.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
114 props.insert( QStringLiteral( "offset_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
115 props.insert( QStringLiteral( "color" ), QgsColorUtils::colorToString( mColor ) );
116 return props;
117}
118
119void QgsShadowEffect::readProperties( const QVariantMap &props )
120{
121 bool ok;
122 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
123 if ( ok )
124 {
125 mBlendMode = mode;
126 }
127 if ( props.contains( QStringLiteral( "transparency" ) ) )
128 {
129 const double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
130 if ( ok )
131 {
132 mOpacity = 1.0 - transparency;
133 }
134 }
135 else
136 {
137 const double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
138 if ( ok )
139 {
141 }
142 }
143 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
144 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
145 const double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok );
146 if ( ok )
147 {
148 mBlurLevel = level;
149 if ( !props.contains( QStringLiteral( "blur_unit" ) ) )
150 {
151 // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters
152 mBlurLevel *= 0.2645;
153 }
154 }
155 mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() );
156 mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() );
157 const int angle = props.value( QStringLiteral( "offset_angle" ) ).toInt( &ok );
158 if ( ok )
159 {
161 }
162 const double distance = props.value( QStringLiteral( "offset_distance" ) ).toDouble( &ok );
163 if ( ok )
164 {
165 mOffsetDist = distance;
166 }
167 mOffsetUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offset_unit" ) ).toString() );
168 mOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offset_unit_scale" ) ).toString() );
169 if ( props.contains( QStringLiteral( "color" ) ) )
170 {
171 mColor = QgsColorUtils::colorFromString( props.value( QStringLiteral( "color" ) ).toString() );
172 }
173}
174
175QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
176{
177 //blur radius and offset distance
179
180 // spread is initially the shadow offset size
182
183 //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
184 spread += blurLevel * 2 + 10;
185 return rect.adjusted( -spread, -spread, spread, spread );
186}
187
188
189//
190// QgsDropShadowEffect
191//
192
194{
196 effect->readProperties( map );
197 return effect;
198}
199
202{
203
204}
205
207{
208 return QStringLiteral( "dropShadow" );
209}
210
212{
213 return new QgsDropShadowEffect( *this );
214}
215
217{
218 return true;
219}
220
221
222//
223// QgsInnerShadowEffect
224//
225
227{
229 effect->readProperties( map );
230 return effect;
231}
232
235{
236
237}
238
240{
241 return QStringLiteral( "innerShadow" );
242}
243
245{
246 return new QgsInnerShadowEffect( *this );
247}
248
250{
251 return false;
252}
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.
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
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
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.
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...
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.
Qgis::RenderUnit mOffsetUnit
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
Qgis::RenderUnit mBlurUnit
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 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.
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:716