QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  QImage colorisedIm = sourceAsImage( context )->copy();
35 
36  QPainter *painter = context.painter();
37  painter->save();
38  painter->setCompositionMode( mBlendMode );
39 
40  if ( !exteriorShadow() )
41  {
42  //inner shadow, first invert the opacity. The color does not matter since we will
43  //be replacing it anyway
44  colorisedIm.invertPixels( QImage::InvertRgba );
45  }
46 
48 
49  int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
50  if ( blurLevel <= 16 )
51  {
52  QgsImageOperation::stackBlur( colorisedIm, blurLevel );
53  }
54  else
55  {
56  QImage *imb = QgsImageOperation::gaussianBlur( colorisedIm, blurLevel );
57  colorisedIm = QImage( *imb );
58  delete imb;
59  }
60 
61  double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale );
62 
63  double angleRad = mOffsetAngle * M_PI / 180; // to radians
64  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
65  -offsetDist * std::sin( angleRad + M_PI_2 ) );
66 
67  //transparency, scale
69 
70  if ( !exteriorShadow() )
71  {
72  //inner shadow, do a bit of painter juggling
73  QImage innerShadowIm( colorisedIm.width(), colorisedIm.height(), QImage::Format_ARGB32 );
74  innerShadowIm.fill( Qt::transparent );
75  QPainter imPainter( &innerShadowIm );
76 
77  //draw shadow at offset
78  imPainter.drawImage( transPt.x(), transPt.y(), colorisedIm );
79 
80  //restrict shadow so it's only drawn on top of original image
81  imPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
82  imPainter.drawImage( 0, 0, *sourceAsImage( context ) );
83  imPainter.end();
84 
85  painter->drawImage( imageOffset( context ), innerShadowIm );
86  }
87  else
88  {
89  painter->drawImage( imageOffset( context ) + transPt, colorisedIm );
90  }
91  painter->restore();
92 }
93 
95 {
96  QgsStringMap props;
97  props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
98  props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
99  props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
100  props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
101  props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
102  props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) );
103  props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) );
104  props.insert( QStringLiteral( "offset_angle" ), QString::number( mOffsetAngle ) );
105  props.insert( QStringLiteral( "offset_distance" ), QString::number( mOffsetDist ) );
106  props.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
107  props.insert( QStringLiteral( "offset_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
108  props.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
109  return props;
110 }
111 
113 {
114  bool ok;
115  QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
116  if ( ok )
117  {
118  mBlendMode = mode;
119  }
120  if ( props.contains( QStringLiteral( "transparency" ) ) )
121  {
122  double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
123  if ( ok )
124  {
125  mOpacity = 1.0 - transparency;
126  }
127  }
128  else
129  {
130  double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
131  if ( ok )
132  {
133  mOpacity = opacity;
134  }
135  }
136  mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
137  mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
138  double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok );
139  if ( ok )
140  {
141  mBlurLevel = level;
142  if ( !props.contains( QStringLiteral( "blur_unit" ) ) )
143  {
144  // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters
145  mBlurLevel *= 0.2645;
146  }
147  }
148  mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ) );
149  mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ) );
150  int angle = props.value( QStringLiteral( "offset_angle" ) ).toInt( &ok );
151  if ( ok )
152  {
154  }
155  double distance = props.value( QStringLiteral( "offset_distance" ) ).toDouble( &ok );
156  if ( ok )
157  {
158  mOffsetDist = distance;
159  }
160  mOffsetUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offset_unit" ) ) );
161  mOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offset_unit_scale" ) ) );
162  if ( props.contains( QStringLiteral( "color" ) ) )
163  {
164  mColor = QgsSymbolLayerUtils::decodeColor( props.value( QStringLiteral( "color" ) ) );
165  }
166 }
167 
168 QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
169 {
170  //blur radius and offset distance
171  int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) );
173  //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
174  spread += blurLevel * 2 + 10;
175  return rect.adjusted( -spread, -spread, spread, spread );
176 }
177 
178 
179 //
180 // QgsDropShadowEffect
181 //
182 
184 {
186  effect->readProperties( map );
187  return effect;
188 }
189 
191  : QgsShadowEffect()
192 {
193 
194 }
195 
197 {
198  return QStringLiteral( "dropShadow" );
199 }
200 
202 {
203  return new QgsDropShadowEffect( *this );
204 }
205 
207 {
208  return true;
209 }
210 
211 
212 //
213 // QgsInnerShadowEffect
214 //
215 
217 {
219  effect->readProperties( map );
220  return effect;
221 }
222 
224  : QgsShadowEffect()
225 {
226 
227 }
228 
230 {
231  return QStringLiteral( "innerShadow" );
232 }
233 
235 {
236  return new QgsInnerShadowEffect( *this );
237 }
238 
240 {
241  return false;
242 }
static void multiplyOpacity(QImage &image, double factor)
Multiplies opacity of image pixel values by a factor.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
DrawMode mDrawMode
double blurLevel() const
Returns the blur level (radius) for the shadow.
QString type() const override
Returns the effect type.
Base class for visual effects which can be applied to QPicture drawings.
QgsStringMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QgsDropShadowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
QgsMapUnitScale mBlurMapUnitScale
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
virtual bool exteriorShadow() const =0
Specifies whether the shadow is drawn outside the picture or within the picture.
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
QImage * sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
static QString encodeColor(const QColor &color)
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
QgsMapUnitScale mOffsetMapUnitScale
double opacity() const
Returns the opacity for the effect.
bool exteriorShadow() const override
Specifies whether the shadow is drawn outside the picture or within the picture.
QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const override
Returns the bounding rect required for drawing the effect.
QPainter::CompositionMode mBlendMode
bool enabled() const
Returns whether the effect is enabled.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
A paint effect which draws an offset and optionally blurred drop shadow.
void draw(QgsRenderContext &context) override
Handles drawing of the effect&#39;s result on to the specified render context.
bool exteriorShadow() const override
Specifies whether the shadow is drawn outside the picture or within the picture.
DrawMode
Drawing modes for effects.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsInnerShadowEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
static QImage * gaussianBlur(QImage &image, int radius)
Performs a gaussian blur on an image.
QString type() const override
Returns the effect type.
A paint effect which draws an offset and optionally blurred drop shadow within a picture.
QgsUnitTypes::RenderUnit mBlurUnit
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
static void stackBlur(QImage &image, int radius, bool alphaOnly=false)
Performs a stack blur on an image.
void readProperties(const QgsStringMap &props) override
Reads a string map of an effect&#39;s properties and restores the effect to the state described by the pr...
static QgsPaintEffect * create(const QgsStringMap &map)
Creates a new QgsInnerShadowEffect effect from a properties string map.
Base class for paint effects which offset, blurred shadows.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QgsUnitTypes::RenderUnit mOffsetUnit
const QPicture * source() const
Returns the source QPicture.
static QColor decodeColor(const QString &str)
static QgsPaintEffect * create(const QgsStringMap &map)
Creates a new QgsDropShadowEffect effect from a properties string map.