QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
49 
50  double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale );
51 
52  double angleRad = mOffsetAngle * M_PI / 180; // to radians
53  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
54  -offsetDist * std::sin( angleRad + M_PI_2 ) );
55 
56  //transparency, scale
58 
59  if ( !exteriorShadow() )
60  {
61  //inner shadow, do a bit of painter juggling
62  QImage innerShadowIm( colorisedIm.width(), colorisedIm.height(), QImage::Format_ARGB32 );
63  innerShadowIm.fill( Qt::transparent );
64  QPainter imPainter( &innerShadowIm );
65 
66  //draw shadow at offset
67  imPainter.drawImage( transPt.x(), transPt.y(), colorisedIm );
68 
69  //restrict shadow so it's only drawn on top of original image
70  imPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
71  imPainter.drawImage( 0, 0, *sourceAsImage( context ) );
72  imPainter.end();
73 
74  painter->drawImage( imageOffset( context ), innerShadowIm );
75  }
76  else
77  {
78  painter->drawImage( imageOffset( context ) + transPt, colorisedIm );
79  }
80  painter->restore();
81 }
82 
84 {
85  QgsStringMap props;
86  props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
87  props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
88  props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
89  props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
90  props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) );
91  props.insert( QStringLiteral( "offset_angle" ), QString::number( mOffsetAngle ) );
92  props.insert( QStringLiteral( "offset_distance" ), QString::number( mOffsetDist ) );
93  props.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) );
94  props.insert( QStringLiteral( "offset_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) );
95  props.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
96  return props;
97 }
98 
100 {
101  bool ok;
102  QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
103  if ( ok )
104  {
105  mBlendMode = mode;
106  }
107  if ( props.contains( QStringLiteral( "transparency" ) ) )
108  {
109  double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
110  if ( ok )
111  {
112  mOpacity = 1.0 - transparency;
113  }
114  }
115  else
116  {
117  double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
118  if ( ok )
119  {
120  mOpacity = opacity;
121  }
122  }
123  mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
124  mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
125  int level = props.value( QStringLiteral( "blur_level" ) ).toInt( &ok );
126  if ( ok )
127  {
128  mBlurLevel = level;
129  }
130  int angle = props.value( QStringLiteral( "offset_angle" ) ).toInt( &ok );
131  if ( ok )
132  {
134  }
135  double distance = props.value( QStringLiteral( "offset_distance" ) ).toDouble( &ok );
136  if ( ok )
137  {
138  mOffsetDist = distance;
139  }
140  mOffsetUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offset_unit" ) ) );
141  mOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offset_unit_scale" ) ) );
142  if ( props.contains( QStringLiteral( "color" ) ) )
143  {
144  mColor = QgsSymbolLayerUtils::decodeColor( props.value( QStringLiteral( "color" ) ) );
145  }
146 }
147 
148 QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
149 {
150  //offset distance
152  //plus possible extension due to blur, with a couple of extra pixels thrown in for safety
153  spread += mBlurLevel * 2 + 10;
154  return rect.adjusted( -spread, -spread, spread, spread );
155 }
156 
157 
158 //
159 // QgsDropShadowEffect
160 //
161 
163 {
165  effect->readProperties( map );
166  return effect;
167 }
168 
170  : QgsShadowEffect()
171 {
172 
173 }
174 
176 {
177  return QStringLiteral( "dropShadow" );
178 }
179 
181 {
182  return new QgsDropShadowEffect( *this );
183 }
184 
186 {
187  return true;
188 }
189 
190 
191 //
192 // QgsInnerShadowEffect
193 //
194 
196 {
198  effect->readProperties( map );
199  return effect;
200 }
201 
203  : QgsShadowEffect()
204 {
205 
206 }
207 
209 {
210  return QStringLiteral( "innerShadow" );
211 }
212 
214 {
215  return new QgsInnerShadowEffect( *this );
216 }
217 
219 {
220  return false;
221 }
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
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.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
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.
QString type() const override
Returns the effect type.
A paint effect which draws an offset and optionally blurred drop shadow within a picture.
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.