QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgseffectstack.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgseffectstack.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 "qgseffectstack.h"
19 #include "qgspainteffectregistry.h"
20 #include "qgsrendercontext.h"
21 #include "qgsapplication.h"
22 #include <QPicture>
23 
25  : QgsPaintEffect( other )
26 {
27  //deep copy
28  for ( int i = 0; i < other.count(); ++i )
29  {
30  appendEffect( other.effect( i )->clone() );
31  }
32 }
33 
35  : QgsPaintEffect( other )
36 {
37  std::swap( mEffectList, other.mEffectList );
38 }
39 
41 {
42  appendEffect( effect.clone() );
43 }
44 
46 {
47  clearStack();
48 }
49 
51 {
52  if ( &rhs == this )
53  return *this;
54 
55  //deep copy
56  clearStack();
57  for ( int i = 0; i < rhs.count(); ++i )
58  {
59  appendEffect( rhs.effect( i )->clone() );
60  }
61  mEnabled = rhs.enabled();
62  return *this;
63 }
64 
66 {
67  std::swap( mEffectList, other.mEffectList );
68  mEnabled = other.enabled();
69  return *this;
70 }
71 
73 {
75  effect->readProperties( map );
76  return effect;
77 }
78 
80 {
81  QPainter *destPainter = context.painter();
82 
83  //first, we build up a list of rendered effects
84  //we do this moving backwards through the stack, so that each effect's results
85  //becomes the source of the previous effect
86  QPicture *sourcePic = new QPicture( *source() );
87  QPicture *currentPic = sourcePic;
88  QList< QPicture * > results;
89  for ( int i = mEffectList.count() - 1; i >= 0; --i )
90  {
91  QgsPaintEffect *effect = mEffectList.at( i );
92  if ( !effect->enabled() )
93  {
94  continue;
95  }
96 
97  QPicture *pic = nullptr;
98  if ( effect->type() == QLatin1String( "drawSource" ) )
99  {
100  //draw source is always the original source, regardless of previous effect results
101  pic = sourcePic;
102  }
103  else
104  {
105  pic = currentPic;
106  }
107 
108  QPicture *resultPic = new QPicture();
109  QPainter p( resultPic );
110  context.setPainter( &p );
111  //effect stack has it's own handling of the QPicture DPI issue, so
112  //we disable QgsPaintEffect's internal workaround
113  effect->requiresQPainterDpiFix = false;
114  effect->render( *pic, context );
115  effect->requiresQPainterDpiFix = true;
116  p.end();
117 
118  results << resultPic;
119  if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Render )
120  {
121  currentPic = resultPic;
122  }
123  }
124  delete sourcePic;
125  sourcePic = nullptr;
126 
127  context.setPainter( destPainter );
128  //then, we render all the results in the opposite order
129  for ( int i = 0; i < mEffectList.count(); ++i )
130  {
131  if ( !mEffectList[i]->enabled() )
132  {
133  continue;
134  }
135 
136  QPicture *pic = results.takeLast();
137  if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Modifier )
138  {
139  context.painter()->save();
140  fixQPictureDpi( context.painter() );
141  context.painter()->drawPicture( 0, 0, *pic );
142  context.painter()->restore();
143 
144  }
145  delete pic;
146  }
147 }
148 
150 {
151  return new QgsEffectStack( *this );
152 }
153 
154 bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const
155 {
156  //effect stack needs to save all child effects
157  if ( element.isNull() )
158  {
159  return false;
160  }
161 
162  QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
163  effectElement.setAttribute( QStringLiteral( "type" ), type() );
164  effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled );
165 
166  bool ok = true;
167  for ( QgsPaintEffect *effect : mEffectList )
168  {
169  if ( effect )
170  ok = ok && effect->saveProperties( doc, effectElement );
171  }
172 
173  element.appendChild( effectElement );
174  return ok;
175 }
176 
177 bool QgsEffectStack::readProperties( const QDomElement &element )
178 {
179  if ( element.isNull() )
180  {
181  return false;
182  }
183 
184  mEnabled = ( element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
185 
186  clearStack();
187 
188  //restore all child effects
189  QDomNodeList childNodes = element.childNodes();
190  for ( int i = 0; i < childNodes.size(); ++i )
191  {
192  QDomElement childElement = childNodes.at( i ).toElement();
194  if ( effect )
195  mEffectList << effect;
196  }
197  return true;
198 }
199 
201 {
202  QgsStringMap props;
203  return props;
204 }
205 
207 {
208  Q_UNUSED( props )
209 }
210 
211 void QgsEffectStack::clearStack()
212 {
213  qDeleteAll( mEffectList );
214  mEffectList.clear();
215 }
216 
218 {
219  mEffectList.append( effect );
220 }
221 
223 {
224  if ( index < 0 || index > mEffectList.count() )
225  return false;
226  if ( !effect )
227  return false;
228 
229  mEffectList.insert( index, effect );
230  return true;
231 }
232 
234 {
235  if ( index < 0 || index >= mEffectList.count() )
236  return false;
237  if ( !effect )
238  return false;
239 
240  delete mEffectList.at( index );
241  mEffectList[index] = effect;
242  return true;
243 }
244 
246 {
247  if ( index < 0 || index >= mEffectList.count() )
248  return nullptr;
249 
250  return mEffectList.takeAt( index );
251 }
252 
253 QList<QgsPaintEffect *> *QgsEffectStack::effectList()
254 {
255  return &mEffectList;
256 }
257 
259 {
260  if ( index >= 0 && index < mEffectList.count() )
261  {
262  return mEffectList.at( index );
263  }
264  else
265  {
266  return nullptr;
267  }
268 }
bool insertEffect(int index, QgsPaintEffect *effect)
Inserts an effect at a specified index within the stack.
The result of the effect is rendered on the destination, but does not affect subsequent effects in th...
void fixQPictureDpi(QPainter *painter) const
Applies a workaround to a QPainter to avoid an issue with incorrect scaling when drawing QPictures...
Base class for visual effects which can be applied to QPicture drawings.
~QgsEffectStack() override
void draw(QgsRenderContext &context) override
Handles drawing of the effect&#39;s result on to the specified render context.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:612
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application&#39;s paint effect registry, used for managing paint effects. ...
bool saveProperties(QDomDocument &doc, QDomElement &element) const override
Saves the current state of the effect to a DOM element.
bool readProperties(const QDomElement &element) override
Restores the effect to the state described by a DOM element.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
static QgsPaintEffect * create(const QgsStringMap &map)
Creates a new QgsEffectStack effect.
int count() const
Returns count of effects contained by the stack.
virtual QString type() const =0
Returns the effect type.
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index within the stack.
QString type() const override
Returns the effect type.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
A paint effect which consists of a stack of other chained paint effects.
QgsEffectStack * clone() const override
Duplicates an effect by creating a deep copy of the effect.
bool enabled() const
Returns whether the effect is enabled.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
The result of the effect is not rendered, but is passed on to following effects in the stack...
bool requiresQPainterDpiFix
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QList< QgsPaintEffect *> * effectList()
Returns a pointer to the list of effects currently contained by the stack.
QgsEffectStack & operator=(const QgsEffectStack &rhs)
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
QgsPaintEffect * takeEffect(int index)
Removes an effect from the stack and returns a pointer to it.
const QPicture * source() const
Returns the source QPicture.
QgsPaintEffect * createEffect(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
Creates a new paint effect given the effect name and properties map.
QgsStringMap properties() const override
Unused for QgsEffectStack, will always return an empty string map.
QgsEffectStack()=default
Constructor for empty QgsEffectStack.
bool changeEffect(int index, QgsPaintEffect *effect)
Replaces the effect at a specified position within the stack.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.