QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgspainteffect.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspainteffect.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 "qgspainteffect.h"
19 #include "qgsimageoperation.h"
20 #include "qgslogger.h"
21 #include "qgsrendercontext.h"
22 #include <QPicture>
23 
24 Q_GUI_EXPORT extern int qt_defaultDpiX();
25 Q_GUI_EXPORT extern int qt_defaultDpiY();
26 
28  : mEnabled( other.enabled() )
29  , mDrawMode( other.drawMode() )
30 {
31 
32 }
33 
35 {
36  if ( mOwnsImage )
37  {
38  delete mSourceImage;
39  }
40  delete mEffectPainter;
41  delete mTempPicture;
42 }
43 
44 void QgsPaintEffect::setEnabled( const bool enabled )
45 {
46  mEnabled = enabled;
47 }
48 
50 {
52 }
53 
54 bool QgsPaintEffect::saveProperties( QDomDocument &doc, QDomElement &element ) const
55 {
56  if ( element.isNull() )
57  {
58  return false;
59  }
60 
61  QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
62  effectElement.setAttribute( QStringLiteral( "type" ), type() );
63 
64  QgsStringMap props = properties();
65  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
66  {
67  QDomElement propEl = doc.createElement( QStringLiteral( "prop" ) );
68  propEl.setAttribute( QStringLiteral( "k" ), it.key() );
69  propEl.setAttribute( QStringLiteral( "v" ), it.value() );
70  effectElement.appendChild( propEl );
71  }
72 
73  element.appendChild( effectElement );
74  return true;
75 }
76 
77 bool QgsPaintEffect::readProperties( const QDomElement &element )
78 {
79  if ( element.isNull() )
80  {
81  return false;
82  }
83 
84  //default implementation converts to a string map
85  QgsStringMap props;
86 
87  QDomElement e = element.firstChildElement();
88  while ( !e.isNull() )
89  {
90  if ( e.tagName() != QLatin1String( "prop" ) )
91  {
92  QgsDebugMsg( "unknown tag " + e.tagName() );
93  }
94  else
95  {
96  QString propKey = e.attribute( QStringLiteral( "k" ) );
97  QString propValue = e.attribute( QStringLiteral( "v" ) );
98  props[propKey] = propValue;
99  }
100  e = e.nextSiblingElement();
101  }
102 
103  readProperties( props );
104  return true;
105 }
106 
107 void QgsPaintEffect::render( QPicture &picture, QgsRenderContext &context )
108 {
109  //set source picture
110  mPicture = &picture;
111  delete mSourceImage;
112  mSourceImage = nullptr;
113 
114  draw( context );
115 }
116 
118 {
119  //temporarily replace painter and direct paint operations for context to a QPicture
120  mPrevPainter = context.painter();
121 
122  delete mTempPicture;
123  mTempPicture = new QPicture();
124 
125  delete mEffectPainter;
126  mEffectPainter = new QPainter();
127  mEffectPainter->begin( mTempPicture );
128 
129  context.setPainter( mEffectPainter );
130 }
131 
133 {
134  if ( !mEffectPainter )
135  return;
136 
137  mEffectPainter->end();
138  delete mEffectPainter;
139  mEffectPainter = nullptr;
140 
141  //restore previous painter for context
142  context.setPainter( mPrevPainter );
143  mPrevPainter = nullptr;
144 
145  // clear any existing pen/brush - sometimes these are not correctly restored when restoring a painter
146  // with a QPicture destination - see #15696
147  context.painter()->setPen( Qt::NoPen );
148  context.painter()->setBrush( Qt::NoBrush );
149 
150  //draw using effect
151  render( *mTempPicture, context );
152 
153  //clean up
154  delete mTempPicture;
155  mTempPicture = nullptr;
156 }
157 
158 void QgsPaintEffect::drawSource( QPainter &painter )
159 {
161  {
162  QgsScopedQPainterState painterState( &painter );
163  fixQPictureDpi( &painter );
164  painter.drawPicture( 0, 0, *mPicture );
165  }
166  else
167  {
168  painter.drawPicture( 0, 0, *mPicture );
169  }
170 }
171 
173 {
174  //have we already created a source image? if so, return it
175  if ( mSourceImage )
176  {
177  return mSourceImage;
178  }
179 
180  if ( !mPicture )
181  return nullptr;
182 
183  //else create it
184  //TODO - test with premultiplied image for speed
185  QRectF bounds = imageBoundingRect( context );
186  mSourceImage = new QImage( bounds.width(), bounds.height(), QImage::Format_ARGB32 );
187  mSourceImage->fill( Qt::transparent );
188  QPainter imagePainter( mSourceImage );
189  imagePainter.setRenderHint( QPainter::Antialiasing );
190  imagePainter.translate( -bounds.left(), -bounds.top() );
191  imagePainter.drawPicture( 0, 0, *mPicture );
192  imagePainter.end();
193  mOwnsImage = true;
194  return mSourceImage;
195 }
196 
197 QPointF QgsPaintEffect::imageOffset( const QgsRenderContext &context ) const
198 {
199  return imageBoundingRect( context ).topLeft();
200 }
201 
202 QRectF QgsPaintEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
203 {
204  Q_UNUSED( context )
205  return rect;
206 }
207 
208 void QgsPaintEffect::fixQPictureDpi( QPainter *painter ) const
209 {
210  // QPicture makes an assumption that we drawing to it with system DPI.
211  // Then when being drawn, it scales the painter. The following call
212  // negates the effect. There is no way of setting QPicture's DPI.
213  // See QTBUG-20361
214  painter->scale( static_cast< double >( qt_defaultDpiX() ) / painter->device()->logicalDpiX(),
215  static_cast< double >( qt_defaultDpiY() ) / painter->device()->logicalDpiY() );
216 }
217 
218 QRectF QgsPaintEffect::imageBoundingRect( const QgsRenderContext &context ) const
219 {
220  return boundingRect( mPicture->boundingRect(), context );
221 }
222 
223 
224 //
225 // QgsDrawSourceEffect
226 //
227 
229 {
231  effect->readProperties( map );
232  return effect;
233 }
234 
236 {
237  if ( !enabled() || !context.painter() )
238  return;
239 
240  QPainter *painter = context.painter();
241 
242  if ( mBlendMode == QPainter::CompositionMode_SourceOver && qgsDoubleNear( mOpacity, 1.0 ) )
243  {
244  //just draw unmodified source
245  drawSource( *painter );
246  }
247  else
248  {
249  //rasterize source and apply modifications
250  QImage image = sourceAsImage( context )->copy();
251  QgsImageOperation::multiplyOpacity( image, mOpacity );
252  QgsScopedQPainterState painterState( painter );
253  painter->setCompositionMode( mBlendMode );
254  painter->drawImage( imageOffset( context ), image );
255  }
256 }
257 
259 {
260  return new QgsDrawSourceEffect( *this );
261 }
262 
264 {
265  QgsStringMap props;
266  props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
267  props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
268  props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
269  props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
270  return props;
271 }
272 
274 {
275  bool ok;
276  QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
277  if ( ok )
278  {
279  mBlendMode = mode;
280  }
281  if ( props.contains( QStringLiteral( "transparency" ) ) )
282  {
283  double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
284  if ( ok )
285  {
286  mOpacity = 1.0 - transparency;
287  }
288  }
289  else
290  {
291  double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
292  if ( ok )
293  {
294  mOpacity = opacity;
295  }
296  }
297  mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
298  mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
299 }
300 
301 
302 //
303 // QgsEffectPainter
304 //
305 
307  : mRenderContext( renderContext )
308 
309 {
310  mPainter = renderContext.painter();
311  mPainter->save();
312 }
313 
315  : mRenderContext( renderContext )
316  , mEffect( effect )
317 {
318  mPainter = mRenderContext.painter();
319  mPainter->save();
320  mEffect->begin( mRenderContext );
321 }
322 
324 {
325  Q_ASSERT( !mEffect );
326 
327  mEffect = effect;
328  mEffect->begin( mRenderContext );
329 }
330 
332 {
333  Q_ASSERT( mEffect );
334 
335  mEffect->end( mRenderContext );
336  mPainter->restore();
337 }
QgsPaintEffect::drawSource
void drawSource(QPainter &painter)
Draws the source QPicture onto the specified painter.
Definition: qgspainteffect.cpp:158
QgsPaintEffect::DrawMode
DrawMode
Drawing modes for effects.
Definition: qgspainteffect.h:105
QgsEffectPainter::setEffect
void setEffect(QgsPaintEffect *effect)
Sets the effect to be painted.
Definition: qgspainteffect.cpp:323
QgsPaintEffect::requiresQPainterDpiFix
bool requiresQPainterDpiFix
Definition: qgspainteffect.h:227
QgsPaintEffect::render
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
Definition: qgspainteffect.cpp:107
QgsPaintEffect::fixQPictureDpi
void fixQPictureDpi(QPainter *painter) const
Applies a workaround to a QPainter to avoid an issue with incorrect scaling when drawing QPictures.
Definition: qgspainteffect.cpp:208
QgsDrawSourceEffect::properties
QgsStringMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
Definition: qgspainteffect.cpp:263
QgsEffectPainter::QgsEffectPainter
QgsEffectPainter(QgsRenderContext &renderContext)
QgsEffectPainter constructor.
Definition: qgspainteffect.cpp:306
QgsPaintEffect::begin
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
Definition: qgspainteffect.cpp:117
QgsPaintEffect::properties
virtual QgsStringMap properties() const =0
Returns the properties describing the paint effect encoded in a string format.
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:491
QgsImageOperation::multiplyOpacity
static void multiplyOpacity(QImage &image, double factor)
Multiplies opacity of image pixel values by a factor.
Definition: qgsimageoperation.cpp:322
QgsDrawSourceEffect
A paint effect which draws the source picture with minor or no alterations.
Definition: qgspainteffect.h:329
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
qgsimageoperation.h
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgspainteffect.h
QgsPaintEffect::saveProperties
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
Definition: qgspainteffect.cpp:54
QgsPaintEffect::mEnabled
bool mEnabled
Definition: qgspainteffect.h:225
QgsDrawSourceEffect::QgsDrawSourceEffect
QgsDrawSourceEffect()=default
Constructor for QgsDrawSourceEffect.
QgsDrawSourceEffect::clone
QgsDrawSourceEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
Definition: qgspainteffect.cpp:258
QgsPaintEffect::setDrawMode
void setDrawMode(DrawMode drawMode)
Sets the draw mode for the effect.
Definition: qgspainteffect.cpp:49
QgsPaintEffect::QgsPaintEffect
QgsPaintEffect()=default
Constructor for QgsPaintEffect.
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
qt_defaultDpiY
Q_GUI_EXPORT int qt_defaultDpiY()
QgsPaintEffect::readProperties
virtual void readProperties(const QgsStringMap &props)=0
Reads a string map of an effect's properties and restores the effect to the state described by the pr...
qgsrendercontext.h
QgsPaintEffect::boundingRect
virtual QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const
Returns the bounding rect required for drawing the effect.
Definition: qgspainteffect.cpp:202
QgsDrawSourceEffect::draw
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
Definition: qgspainteffect.cpp:235
QgsPaintEffect::end
virtual void end(QgsRenderContext &context)
Ends interception of paint operations to a render context, and draws the result to the render context...
Definition: qgspainteffect.cpp:132
QgsPaintEffect::drawMode
DrawMode drawMode() const
Returns the draw mode for the effect.
Definition: qgspainteffect.h:213
QgsScopedQPainterState
Scoped object for saving and restoring a QPainter object's state.
Definition: qgsrendercontext.h:1120
QgsPaintEffect::imageOffset
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
Definition: qgspainteffect.cpp:197
QgsPaintEffect::setEnabled
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Definition: qgspainteffect.cpp:44
QgsPaintEffect::mDrawMode
DrawMode mDrawMode
Definition: qgspainteffect.h:226
QgsDrawSourceEffect::create
static QgsPaintEffect * create(const QgsStringMap &map)
Creates a new QgsDrawSource effect from a properties string map.
Definition: qgspainteffect.cpp:228
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:758
QgsEffectPainter::~QgsEffectPainter
~QgsEffectPainter()
Definition: qgspainteffect.cpp:331
QgsPaintEffect::sourceAsImage
QImage * sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
Definition: qgspainteffect.cpp:172
QgsPaintEffect
Base class for visual effects which can be applied to QPicture drawings.
Definition: qgspainteffect.h:54
QgsPaintEffect::~QgsPaintEffect
virtual ~QgsPaintEffect()
Definition: qgspainteffect.cpp:34
qgslogger.h
qt_defaultDpiX
Q_GUI_EXPORT int qt_defaultDpiX()
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:179
QgsPaintEffect::enabled
bool enabled() const
Returns whether the effect is enabled.
Definition: qgspainteffect.h:198
QgsDrawSourceEffect::readProperties
void readProperties(const QgsStringMap &props) override
Reads a string map of an effect's properties and restores the effect to the state described by the pr...
Definition: qgspainteffect.cpp:273
QgsPaintEffect::type
virtual QString type() const =0
Returns the effect type.
QgsDrawSourceEffect::opacity
double opacity() const
Returns the opacity for the effect.
Definition: qgspainteffect.h:361
QgsPaintEffect::draw
virtual void draw(QgsRenderContext &context)=0
Handles drawing of the effect's result on to the specified render context.