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