QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include "qgsapplication.h"
22#include "qgspainting.h"
23#include "qgsrendercontext.h"
24
25#include <QPicture>
26
28 : QgsPaintEffect( other )
29{
30 //deep copy
31 for ( int i = 0; i < other.count(); ++i )
32 {
33 appendEffect( other.effect( i )->clone() );
34 }
35}
36
38 : QgsPaintEffect( other )
39{
40 std::swap( mEffectList, other.mEffectList );
41}
42
47
49{
50 clearStack();
51}
52
54{
56 for ( const QgsPaintEffect *effect : mEffectList )
57 {
59 {
61 }
62 }
63 return res;
64}
65
67{
68 if ( &rhs == this )
69 return *this;
70
71 //deep copy
72 clearStack();
73 for ( int i = 0; i < rhs.count(); ++i )
74 {
75 appendEffect( rhs.effect( i )->clone() );
76 }
77 mEnabled = rhs.enabled();
78 return *this;
79}
80
82{
83 if ( &other == this )
84 return *this;
85
86 std::swap( mEffectList, other.mEffectList );
87 mEnabled = other.enabled();
88 return *this;
89}
90
91QgsPaintEffect *QgsEffectStack::create( const QVariantMap &map )
92{
94 effect->readProperties( map );
95 return effect;
96}
97
99{
100 QPainter *destPainter = context.painter();
101
103 {
104 // can we render this stack if we're forcing vectors?
105 bool requiresRasterization = false;
106 for ( const QgsPaintEffect *effect : std::as_const( mEffectList ) )
107 {
108 if ( effect->enabled() && effect->flags().testFlag( Qgis::PaintEffectFlag::RequiresRasterization ) )
109 {
110 requiresRasterization = true;
111 break;
112 }
113 }
114
115 if ( requiresRasterization )
116 {
117 //just draw unmodified source, we can't render this effect stack when forcing vectors
118 drawSource( *context.painter() );
119 }
120 return;
121 }
122
123 //first, we build up a list of rendered effects
124 //we do this moving backwards through the stack, so that each effect's results
125 //becomes the source of the previous effect
126 const QPicture sourcePic = source();
127 const QPicture *currentPic = &sourcePic;
128 std::vector< QPicture > results;
129 results.reserve( mEffectList.count() );
130 for ( int i = mEffectList.count() - 1; i >= 0; --i )
131 {
132 QgsPaintEffect *effect = mEffectList.at( i );
133 if ( !effect->enabled() )
134 {
135 continue;
136 }
137
138 const QPicture *pic = nullptr;
139 if ( effect->type() == QLatin1String( "drawSource" ) )
140 {
141 //draw source is always the original source, regardless of previous effect results
142 pic = &sourcePic;
143 }
144 else
145 {
146 pic = currentPic;
147 }
148
149 QPicture resultPic;
150 QPainter p( &resultPic );
151 context.setPainter( &p );
152 //effect stack has it's own handling of the QPicture DPI issue, so
153 //we disable QgsPaintEffect's internal workaround
154 effect->requiresQPainterDpiFix = false;
155 effect->render( *pic, context );
156 effect->requiresQPainterDpiFix = true;
157 p.end();
158
159 results.emplace_back( std::move( resultPic ) );
160 if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Render )
161 {
162 currentPic = &results.back();
163 }
164 }
165
166 context.setPainter( destPainter );
167 //then, we render all the results in the opposite order
168 for ( int i = 0; i < mEffectList.count(); ++i )
169 {
170 if ( !mEffectList[i]->enabled() )
171 {
172 continue;
173 }
174
175 if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Modifier )
176 {
177 QgsPainting::drawPicture( context.painter(), QPointF( 0, 0 ), results.back() );
178 }
179 results.pop_back();
180 }
181}
182
184{
185 return new QgsEffectStack( *this );
186}
187
188bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const
189{
190 //effect stack needs to save all child effects
191 if ( element.isNull() )
192 {
193 return false;
194 }
195
196 QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
197 effectElement.setAttribute( QStringLiteral( "type" ), type() );
198 effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled );
199
200 bool ok = true;
201 for ( QgsPaintEffect *effect : mEffectList )
202 {
203 if ( effect )
204 ok = ok && effect->saveProperties( doc, effectElement );
205 }
206
207 element.appendChild( effectElement );
208 return ok;
209}
210
211bool QgsEffectStack::readProperties( const QDomElement &element )
212{
213 if ( element.isNull() )
214 {
215 return false;
216 }
217
218 mEnabled = ( element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
219
220 clearStack();
221
222 //restore all child effects
223 const QDomNodeList childNodes = element.childNodes();
224 for ( int i = 0; i < childNodes.size(); ++i )
225 {
226 const QDomElement childElement = childNodes.at( i ).toElement();
228 if ( effect )
229 mEffectList << effect;
230 }
231 return true;
232}
233
234QVariantMap QgsEffectStack::properties() const
235{
236 QVariantMap props;
237 return props;
238}
239
240void QgsEffectStack::readProperties( const QVariantMap &props )
241{
242 Q_UNUSED( props )
243}
244
245void QgsEffectStack::clearStack()
246{
247 qDeleteAll( mEffectList );
248 mEffectList.clear();
249}
250
252{
253 mEffectList.append( effect );
254}
255
257{
258 if ( index < 0 || index > mEffectList.count() )
259 return false;
260 if ( !effect )
261 return false;
262
263 mEffectList.insert( index, effect );
264 return true;
265}
266
268{
269 if ( index < 0 || index >= mEffectList.count() )
270 return false;
271 if ( !effect )
272 return false;
273
274 delete mEffectList.at( index );
275 mEffectList[index] = effect;
276 return true;
277}
278
280{
281 if ( index < 0 || index >= mEffectList.count() )
282 return nullptr;
283
284 return mEffectList.takeAt( index );
285}
286
287QList<QgsPaintEffect *> *QgsEffectStack::effectList()
288{
289 return &mEffectList;
290}
291
293{
294 if ( index >= 0 && index < mEffectList.count() )
295 {
296 return mEffectList.at( index );
297 }
298 else
299 {
300 return nullptr;
301 }
302}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2706
@ RequiresRasterization
The effect requires raster-based rendering.
Definition qgis.h:2806
QFlags< PaintEffectFlag > PaintEffectFlags
Flags which control how paint effects behave.
Definition qgis.h:2815
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
QgsEffectStack()=default
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.
Qgis::PaintEffectFlags flags() const override
Returns flags which specify how the paint effect behaves.
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.
QgsPaintEffect()=default
void drawSource(QPainter &painter)
Draws the source QPicture onto the specified painter.
const QPicture & source() const
Returns the source QPicture.
bool enabled() const
Returns whether the effect is enabled.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
@ 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.
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.