QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 {
36  appendEffect( effect.clone() );
37 }
38 
40 {
41  clearStack();
42 }
43 
45 {
46  if ( &rhs == this )
47  return *this;
48 
49  //deep copy
50  clearStack();
51  for ( int i = 0; i < rhs.count(); ++i )
52  {
53  appendEffect( rhs.effect( i )->clone() );
54  }
55  mEnabled = rhs.enabled();
56  return *this;
57 }
58 
60 {
62  effect->readProperties( map );
63  return effect;
64 }
65 
67 {
68  QPainter *destPainter = context.painter();
69 
70  //first, we build up a list of rendered effects
71  //we do this moving backwards through the stack, so that each effect's results
72  //becomes the source of the previous effect
73  QPicture *sourcePic = new QPicture( *source() );
74  QPicture *currentPic = sourcePic;
75  QList< QPicture * > results;
76  for ( int i = mEffectList.count() - 1; i >= 0; --i )
77  {
78  QgsPaintEffect *effect = mEffectList.at( i );
79  if ( !effect->enabled() )
80  {
81  continue;
82  }
83 
84  QPicture *pic = nullptr;
85  if ( effect->type() == QLatin1String( "drawSource" ) )
86  {
87  //draw source is always the original source, regardless of previous effect results
88  pic = sourcePic;
89  }
90  else
91  {
92  pic = currentPic;
93  }
94 
95  QPicture *resultPic = new QPicture();
96  QPainter p( resultPic );
97  context.setPainter( &p );
98  //effect stack has it's own handling of the QPicture DPI issue, so
99  //we disable QgsPaintEffect's internal workaround
100  effect->requiresQPainterDpiFix = false;
101  effect->render( *pic, context );
102  effect->requiresQPainterDpiFix = true;
103  p.end();
104 
105  results << resultPic;
106  if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Render )
107  {
108  currentPic = resultPic;
109  }
110  }
111  delete sourcePic;
112  sourcePic = nullptr;
113 
114  context.setPainter( destPainter );
115  //then, we render all the results in the opposite order
116  for ( int i = 0; i < mEffectList.count(); ++i )
117  {
118  if ( !mEffectList[i]->enabled() )
119  {
120  continue;
121  }
122 
123  QPicture *pic = results.takeLast();
124  if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Modifier )
125  {
126  context.painter()->save();
127  fixQPictureDpi( context.painter() );
128  context.painter()->drawPicture( 0, 0, *pic );
129  context.painter()->restore();
130 
131  }
132  delete pic;
133  }
134 }
135 
137 {
138  return new QgsEffectStack( *this );
139 }
140 
141 bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const
142 {
143  //effect stack needs to save all child effects
144  if ( element.isNull() )
145  {
146  return false;
147  }
148 
149  QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
150  effectElement.setAttribute( QStringLiteral( "type" ), type() );
151  effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled );
152 
153  bool ok = true;
154  for ( QgsPaintEffect *effect : mEffectList )
155  {
156  if ( effect )
157  ok = ok && effect->saveProperties( doc, effectElement );
158  }
159 
160  element.appendChild( effectElement );
161  return ok;
162 }
163 
164 bool QgsEffectStack::readProperties( const QDomElement &element )
165 {
166  if ( element.isNull() )
167  {
168  return false;
169  }
170 
171  mEnabled = ( element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
172 
173  clearStack();
174 
175  //restore all child effects
176  QDomNodeList childNodes = element.childNodes();
177  for ( int i = 0; i < childNodes.size(); ++i )
178  {
179  QDomElement childElement = childNodes.at( i ).toElement();
181  if ( effect )
182  mEffectList << effect;
183  }
184  return true;
185 }
186 
188 {
189  QgsStringMap props;
190  return props;
191 }
192 
194 {
195  Q_UNUSED( props )
196 }
197 
198 void QgsEffectStack::clearStack()
199 {
200  qDeleteAll( mEffectList );
201  mEffectList.clear();
202 }
203 
205 {
206  mEffectList.append( effect );
207 }
208 
210 {
211  if ( index < 0 || index > mEffectList.count() )
212  return false;
213  if ( !effect )
214  return false;
215 
216  mEffectList.insert( index, effect );
217  return true;
218 }
219 
221 {
222  if ( index < 0 || index >= mEffectList.count() )
223  return false;
224  if ( !effect )
225  return false;
226 
227  delete mEffectList.at( index );
228  mEffectList[index] = effect;
229  return true;
230 }
231 
233 {
234  if ( index < 0 || index >= mEffectList.count() )
235  return nullptr;
236 
237  return mEffectList.takeAt( index );
238 }
239 
240 QList<QgsPaintEffect *> *QgsEffectStack::effectList()
241 {
242  return &mEffectList;
243 }
244 
246 {
247  if ( index >= 0 && index < mEffectList.count() )
248  {
249  return mEffectList.at( index );
250  }
251  else
252  {
253  return nullptr;
254  }
255 }
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:587
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.