QGIS API Documentation  3.23.0-Master (b5237dafc3)
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 {
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 
72 QgsPaintEffect *QgsEffectStack::create( const QVariantMap &map )
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
114  effect->render( *pic, context );
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  const QgsScopedQPainterState painterState( context.painter() );
140  fixQPictureDpi( context.painter() );
141  context.painter()->drawPicture( 0, 0, *pic );
142  }
143  delete pic;
144  }
145 }
146 
148 {
149  return new QgsEffectStack( *this );
150 }
151 
152 bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const
153 {
154  //effect stack needs to save all child effects
155  if ( element.isNull() )
156  {
157  return false;
158  }
159 
160  QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
161  effectElement.setAttribute( QStringLiteral( "type" ), type() );
162  effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled );
163 
164  bool ok = true;
165  for ( QgsPaintEffect *effect : mEffectList )
166  {
167  if ( effect )
168  ok = ok && effect->saveProperties( doc, effectElement );
169  }
170 
171  element.appendChild( effectElement );
172  return ok;
173 }
174 
175 bool QgsEffectStack::readProperties( const QDomElement &element )
176 {
177  if ( element.isNull() )
178  {
179  return false;
180  }
181 
182  mEnabled = ( element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
183 
184  clearStack();
185 
186  //restore all child effects
187  const QDomNodeList childNodes = element.childNodes();
188  for ( int i = 0; i < childNodes.size(); ++i )
189  {
190  const QDomElement childElement = childNodes.at( i ).toElement();
192  if ( effect )
193  mEffectList << effect;
194  }
195  return true;
196 }
197 
198 QVariantMap QgsEffectStack::properties() const
199 {
200  QVariantMap props;
201  return props;
202 }
203 
204 void QgsEffectStack::readProperties( const QVariantMap &props )
205 {
206  Q_UNUSED( props )
207 }
208 
209 void QgsEffectStack::clearStack()
210 {
211  qDeleteAll( mEffectList );
212  mEffectList.clear();
213 }
214 
216 {
217  mEffectList.append( effect );
218 }
219 
220 bool QgsEffectStack::insertEffect( const int index, QgsPaintEffect *effect )
221 {
222  if ( index < 0 || index > mEffectList.count() )
223  return false;
224  if ( !effect )
225  return false;
226 
227  mEffectList.insert( index, effect );
228  return true;
229 }
230 
231 bool QgsEffectStack::changeEffect( const int index, QgsPaintEffect *effect )
232 {
233  if ( index < 0 || index >= mEffectList.count() )
234  return false;
235  if ( !effect )
236  return false;
237 
238  delete mEffectList.at( index );
239  mEffectList[index] = effect;
240  return true;
241 }
242 
244 {
245  if ( index < 0 || index >= mEffectList.count() )
246  return nullptr;
247 
248  return mEffectList.takeAt( index );
249 }
250 
251 QList<QgsPaintEffect *> *QgsEffectStack::effectList()
252 {
253  return &mEffectList;
254 }
255 
257 {
258  if ( index >= 0 && index < mEffectList.count() )
259  {
260  return mEffectList.at( index );
261  }
262  else
263  {
264  return nullptr;
265  }
266 }
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
A paint effect which consists of a stack of other chained paint effects.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
QgsEffectStack()=default
Constructor for empty QgsEffectStack.
bool saveProperties(QDomDocument &doc, QDomElement &element) const override
Saves the current state of the effect to a DOM element.
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
QgsEffectStack & operator=(const QgsEffectStack &rhs)
QString type() const override
Returns the effect type.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsEffectStack effect.
bool readProperties(const QDomElement &element) override
Restores the effect to the state described by a DOM element.
~QgsEffectStack() override
QList< QgsPaintEffect * > * effectList()
Returns a pointer to the list of effects currently contained by the stack.
QVariantMap properties() const override
Unused for QgsEffectStack, will always return an empty string map.
int count() const
Returns count of effects contained by the stack.
QgsEffectStack * clone() const override
Duplicates an effect by creating a deep copy of the effect.
bool insertEffect(int index, QgsPaintEffect *effect)
Inserts an effect at a specified index within the stack.
QgsPaintEffect * takeEffect(int index)
Removes an effect from the stack and returns a pointer to it.
bool changeEffect(int index, QgsPaintEffect *effect)
Replaces the effect at a specified position within the stack.
QgsPaintEffect * effect(int index) const
Returns a pointer to the effect at a specified index within the stack.
QgsPaintEffect * createEffect(const QString &name, const QVariantMap &properties=QVariantMap()) const
Creates a new paint effect given the effect name and properties map.
Base class for visual effects which can be applied to QPicture drawings.
bool requiresQPainterDpiFix
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
virtual void readProperties(const QVariantMap &props)=0
Reads a string map of an effect's properties and restores the effect to the state described by the pr...
const QPicture * source() const
Returns the source QPicture.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
virtual void render(QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
bool enabled() const
Returns whether the effect is enabled.
void fixQPictureDpi(QPainter *painter) const
Applies a workaround to a QPainter to avoid an issue with incorrect scaling when drawing QPictures.
@ Render
The result of the effect is rendered on the destination, but does not affect subsequent effects in th...
@ Modifier
The result of the effect is not rendered, but is passed on to following effects in the stack.
virtual QString type() const =0
Returns the effect type.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Scoped object for saving and restoring a QPainter object's state.