QGIS API Documentation  2.12.0-Lyon
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 <QPicture>
22 
23 Q_GUI_EXPORT extern int qt_defaultDpiX();
24 Q_GUI_EXPORT extern int qt_defaultDpiY();
25 
27  : mEnabled( true )
28  , mDrawMode( ModifyAndRender )
29  , requiresQPainterDpiFix( true )
30  , mPicture( 0 )
31  , mSourceImage( 0 )
32  , mOwnsImage( false )
33  , mPrevPainter( 0 )
34  , mEffectPainter( 0 )
35  , mTempPicture( 0 )
36 {
37 
38 }
39 
41  : mEnabled( other.enabled() )
42  , mDrawMode( other.drawMode() )
43  , requiresQPainterDpiFix( true )
44  , mPicture( 0 )
45  , mSourceImage( 0 )
46  , mOwnsImage( false )
47  , mPrevPainter( 0 )
48  , mEffectPainter( 0 )
49  , mTempPicture( 0 )
50 {
51 
52 }
53 
55 {
56  if ( mOwnsImage )
57  {
58  delete mSourceImage;
59  }
60  delete mEffectPainter;
61  delete mTempPicture;
62 }
63 
64 void QgsPaintEffect::setEnabled( const bool enabled )
65 {
66  mEnabled = enabled;
67 }
68 
70 {
72 }
73 
75 {
76  if ( element.isNull() )
77  {
78  return false;
79  }
80 
81  QDomElement effectElement = doc.createElement( "effect" );
82  effectElement.setAttribute( QString( "type" ), type() );
83 
84  QgsStringMap props = properties();
85  for ( QgsStringMap::iterator it = props.begin(); it != props.end(); ++it )
86  {
87  QDomElement propEl = doc.createElement( "prop" );
88  propEl.setAttribute( "k", it.key() );
89  propEl.setAttribute( "v", it.value() );
90  effectElement.appendChild( propEl );
91  }
92 
93  element.appendChild( effectElement );
94  return true;
95 }
96 
98 {
99  if ( element.isNull() )
100  {
101  return false;
102  }
103 
104  //default implementation converts to a string map
105  QgsStringMap props;
106 
107  QDomElement e = element.firstChildElement();
108  while ( !e.isNull() )
109  {
110  if ( e.tagName() != "prop" )
111  {
112  QgsDebugMsg( "unknown tag " + e.tagName() );
113  }
114  else
115  {
116  QString propKey = e.attribute( "k" );
117  QString propValue = e.attribute( "v" );
118  props[propKey] = propValue;
119  }
120  e = e.nextSiblingElement();
121  }
122 
123  readProperties( props );
124  return true;
125 }
126 
128 {
129  //set source picture
130  mPicture = &picture;
131  delete mSourceImage;
132  mSourceImage = 0;
133 
134  draw( context );
135 }
136 
138 {
139  //temporarily replace painter and direct paint operations for context to a QPicture
140  mPrevPainter = context.painter();
141 
142  delete mTempPicture;
143  mTempPicture = new QPicture();
144 
145  delete mEffectPainter;
146  mEffectPainter = new QPainter();
147  mEffectPainter->begin( mTempPicture );
148 
149  context.setPainter( mEffectPainter );
150 }
151 
153 {
154  if ( !mEffectPainter )
155  return;
156 
157  mEffectPainter->end();
158  delete mEffectPainter;
159  mEffectPainter = 0;
160 
161  //restore previous painter for context
162  context.setPainter( mPrevPainter );
163  mPrevPainter = 0;
164 
165  //draw using effect
166  render( *mTempPicture, context );
167 
168  //clean up
169  delete mTempPicture;
170  mTempPicture = 0;
171 }
172 
174 {
176  {
177  painter.save();
178  fixQPictureDpi( &painter );
179  painter.drawPicture( 0, 0, *mPicture );
180  painter.restore();
181  }
182  else
183  {
184  painter.drawPicture( 0, 0, *mPicture );
185  }
186 }
187 
189 {
190  //have we already created a source image? if so, return it
191  if ( mSourceImage )
192  {
193  return mSourceImage;
194  }
195 
196  if ( !mPicture )
197  return 0;
198 
199  //else create it
200  //TODO - test with premultiplied image for speed
201  QRectF bounds = imageBoundingRect( context );
202  mSourceImage = new QImage( bounds.width(), bounds.height(), QImage::Format_ARGB32 );
203  mSourceImage->fill( Qt::transparent );
204  QPainter imagePainter( mSourceImage );
205  imagePainter.setRenderHint( QPainter::Antialiasing );
206  imagePainter.translate( -bounds.left(), -bounds.top() );
207  imagePainter.drawPicture( 0, 0, *mPicture );
208  imagePainter.end();
209  mOwnsImage = true;
210  return mSourceImage;
211 }
212 
214 {
215  return imageBoundingRect( context ).topLeft();
216 }
217 
218 QRectF QgsPaintEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
219 {
220  Q_UNUSED( context );
221  return rect;
222 }
223 
225 {
226  // QPicture makes an assumption that we drawing to it with system DPI.
227  // Then when being drawn, it scales the painter. The following call
228  // negates the effect. There is no way of setting QPicture's DPI.
229  // See QTBUG-20361
230  painter->scale(( double )qt_defaultDpiX() / painter->device()->logicalDpiX(),
231  ( double )qt_defaultDpiY() / painter->device()->logicalDpiY() );
232 }
233 
234 QRectF QgsPaintEffect::imageBoundingRect( const QgsRenderContext &context ) const
235 {
236  return boundingRect( mPicture->boundingRect(), context );
237 }
238 
239 
240 //
241 // QgsDrawSourceEffect
242 //
243 
245  : QgsPaintEffect()
246  , mTransparency( 0.0 )
247  , mBlendMode( QPainter::CompositionMode_SourceOver )
248 {
249 
250 }
251 
253 {
254 
255 }
256 
258 {
260  effect->readProperties( map );
261  return effect;
262 }
263 
265 {
266  if ( !enabled() || !context.painter() )
267  return;
268 
269  QPainter* painter = context.painter();
270 
271  if ( mBlendMode == QPainter::CompositionMode_SourceOver && qgsDoubleNear( mTransparency, 0.0 ) )
272  {
273  //just draw unmodified source
274  drawSource( *painter );
275  }
276  else
277  {
278  //rasterise source and apply modifications
279  QImage image = sourceAsImage( context )->copy();
280  QgsImageOperation::multiplyOpacity( image, 1.0 - mTransparency );
281  painter->save();
282  painter->setCompositionMode( mBlendMode );
283  painter->drawImage( imageOffset( context ), image );
284  painter->restore();
285  }
286 }
287 
289 {
290  return new QgsDrawSourceEffect( *this );
291 }
292 
294 {
295  QgsStringMap props;
296  props.insert( "enabled", mEnabled ? "1" : "0" );
297  props.insert( "draw_mode", QString::number( int( mDrawMode ) ) );
298  props.insert( "blend_mode", QString::number( int( mBlendMode ) ) );
299  props.insert( "transparency", QString::number( mTransparency ) );
300  return props;
301 }
302 
304 {
305  bool ok;
306  QPainter::CompositionMode mode = ( QPainter::CompositionMode )props.value( "blend_mode" ).toInt( &ok );
307  if ( ok )
308  {
309  mBlendMode = mode;
310  }
311  double transparency = props.value( "transparency" ).toDouble( &ok );
312  if ( ok )
313  {
314  mTransparency = transparency;
315  }
316  mEnabled = props.value( "enabled", "1" ).toInt();
317  mDrawMode = ( QgsPaintEffect::DrawMode )props.value( "draw_mode", "2" ).toInt();
318 }
void setEnabled(const bool enabled)
Sets whether the effect is enabled.
virtual QgsStringMap properties() const =0
Returns the properties describing the paint effect encoded in a string format.
bool end()
QRect boundingRect() const
void setCompositionMode(CompositionMode mode)
DrawMode mDrawMode
static void multiplyOpacity(QImage &image, const double factor)
Multiplies opacity of image pixel values by a factor.
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
virtual QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const
Returns the bounding rect required for drawing the effect.
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
DrawMode drawMode() const
Returns the draw mode for the effect.
void scale(qreal sx, qreal sy)
bool enabled() const
Returns whether the effect is enabled.
Base class for visual effects which can be applied to QPicture drawings.
void setDrawMode(const DrawMode drawMode)
Sets the draw mode for the effect.
void save()
QDomElement nextSiblingElement(const QString &tagName) const
Q_GUI_EXPORT int qt_defaultDpiX()
qreal top() const
virtual ~QgsDrawSourceEffect()
QImage copy(const QRect &rectangle) const
double transparency() const
Returns the transparency for the effect.
qreal left() const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
QImage * sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
static QgsPaintEffect * create(const QgsStringMap &map)
Creates a new QgsDrawSource effect from a properties string map.
QString number(int n, int base)
void fill(uint pixelValue)
virtual QString type() const =0
Returns the effect type.
virtual QgsStringMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
void setAttribute(const QString &name, const QString &value)
QPointF topLeft() const
virtual ~QgsPaintEffect()
QPaintDevice * device() const
virtual QgsPaintEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
void setPainter(QPainter *p)
Q_GUI_EXPORT int qt_defaultDpiY()
void fixQPictureDpi(QPainter *painter) const
Applies a workaround to a QPainter to avoid an issue with incorrect scaling when drawing QPictures...
iterator end()
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
virtual 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...
iterator begin()
bool requiresQPainterDpiFix
int logicalDpiX() const
int logicalDpiY() const
DrawMode
Drawing modes for effects.
bool isNull() const
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...
void restore()
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
Contains information about the context of a rendering operation.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
QPainter * painter()
qreal width() const
void drawSource(QPainter &painter)
Draws the source QPicture onto the specified painter.
QDomElement firstChildElement(const QString &tagName) const
void translate(const QPointF &offset)
qreal height() const
iterator insert(const Key &key, const T &value)
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
QString tagName() const
void drawPicture(const QPointF &point, const QPicture &picture)
virtual void draw(QgsRenderContext &context)=0
Handles drawing of the effect's result on to the specified render context.
QDomElement createElement(const QString &tagName)
virtual void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
A paint effect which draws the source picture with minor or no alterations.
bool begin(QPaintDevice *device)
virtual void end(QgsRenderContext &context)
Ends interception of paint operations to a render context, and draws the result to the render context...
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
const T value(const Key &key) const