QGIS API Documentation 3.99.0-Master (8e76e220402)
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#include <QString>
27
28using namespace Qt::StringLiterals;
29
31 : QgsPaintEffect( other )
32{
33 //deep copy
34 for ( int i = 0; i < other.count(); ++i )
35 {
36 appendEffect( other.effect( i )->clone() );
37 }
38}
39
41 : QgsPaintEffect( other )
42{
43 std::swap( mEffectList, other.mEffectList );
44}
45
50
52{
53 clearStack();
54}
55
57{
59 for ( const QgsPaintEffect *effect : mEffectList )
60 {
62 {
64 }
65 }
66 return res;
67}
68
70{
71 if ( &rhs == this )
72 return *this;
73
74 //deep copy
75 clearStack();
76 for ( int i = 0; i < rhs.count(); ++i )
77 {
78 appendEffect( rhs.effect( i )->clone() );
79 }
80 mEnabled = rhs.enabled();
81 return *this;
82}
83
85{
86 if ( &other == this )
87 return *this;
88
89 std::swap( mEffectList, other.mEffectList );
90 mEnabled = other.enabled();
91 return *this;
92}
93
94QgsPaintEffect *QgsEffectStack::create( const QVariantMap &map )
95{
97 effect->readProperties( map );
98 return effect;
99}
100
102{
103 QPainter *destPainter = context.painter();
104
106 {
107 // can we render this stack if we're forcing vectors?
108 bool requiresRasterization = false;
109 for ( const QgsPaintEffect *effect : std::as_const( mEffectList ) )
110 {
111 if ( effect->enabled() && effect->flags().testFlag( Qgis::PaintEffectFlag::RequiresRasterization ) )
112 {
113 requiresRasterization = true;
114 break;
115 }
116 }
117
118 if ( requiresRasterization )
119 {
120 //just draw unmodified source, we can't render this effect stack when forcing vectors
121 drawSource( *context.painter() );
122 }
123 return;
124 }
125
126 //first, we build up a list of rendered effects
127 //we do this moving backwards through the stack, so that each effect's results
128 //becomes the source of the previous effect
129 const QPicture sourcePic = source();
130 const QPicture *currentPic = &sourcePic;
131 std::vector< QPicture > results;
132 results.reserve( mEffectList.count() );
133 for ( int i = mEffectList.count() - 1; i >= 0; --i )
134 {
135 QgsPaintEffect *effect = mEffectList.at( i );
136 if ( !effect->enabled() )
137 {
138 continue;
139 }
140
141 const QPicture *pic = nullptr;
142 if ( effect->type() == "drawSource"_L1 )
143 {
144 //draw source is always the original source, regardless of previous effect results
145 pic = &sourcePic;
146 }
147 else
148 {
149 pic = currentPic;
150 }
151
152 QPicture resultPic;
153 QPainter p( &resultPic );
154 context.setPainter( &p );
155 //effect stack has it's own handling of the QPicture DPI issue, so
156 //we disable QgsPaintEffect's internal workaround
157 effect->requiresQPainterDpiFix = false;
158 effect->render( *pic, context );
159 effect->requiresQPainterDpiFix = true;
160 p.end();
161
162 results.emplace_back( std::move( resultPic ) );
163 if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Render )
164 {
165 currentPic = &results.back();
166 }
167 }
168
169 context.setPainter( destPainter );
170 //then, we render all the results in the opposite order
171 for ( int i = 0; i < mEffectList.count(); ++i )
172 {
173 if ( !mEffectList[i]->enabled() )
174 {
175 continue;
176 }
177
178 if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Modifier )
179 {
180 QgsPainting::drawPicture( context.painter(), QPointF( 0, 0 ), results.back() );
181 }
182 results.pop_back();
183 }
184}
185
187{
188 return new QgsEffectStack( *this );
189}
190
191bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const
192{
193 //effect stack needs to save all child effects
194 if ( element.isNull() )
195 {
196 return false;
197 }
198
199 QDomElement effectElement = doc.createElement( u"effect"_s );
200 effectElement.setAttribute( u"type"_s, type() );
201 effectElement.setAttribute( u"enabled"_s, mEnabled );
202
203 bool ok = true;
204 for ( QgsPaintEffect *effect : mEffectList )
205 {
206 if ( effect )
207 ok = ok && effect->saveProperties( doc, effectElement );
208 }
209
210 element.appendChild( effectElement );
211 return ok;
212}
213
214bool QgsEffectStack::readProperties( const QDomElement &element )
215{
216 if ( element.isNull() )
217 {
218 return false;
219 }
220
221 mEnabled = ( element.attribute( u"enabled"_s, u"0"_s ) != "0"_L1 );
222
223 clearStack();
224
225 //restore all child effects
226 const QDomNodeList childNodes = element.childNodes();
227 for ( int i = 0; i < childNodes.size(); ++i )
228 {
229 const QDomElement childElement = childNodes.at( i ).toElement();
231 if ( effect )
232 mEffectList << effect;
233 }
234 return true;
235}
236
237QVariantMap QgsEffectStack::properties() const
238{
239 QVariantMap props;
240 return props;
241}
242
243void QgsEffectStack::readProperties( const QVariantMap &props )
244{
245 Q_UNUSED( props )
246}
247
248void QgsEffectStack::clearStack()
249{
250 qDeleteAll( mEffectList );
251 mEffectList.clear();
252}
253
255{
256 mEffectList.append( effect );
257}
258
260{
261 if ( index < 0 || index > mEffectList.count() )
262 return false;
263 if ( !effect )
264 return false;
265
266 mEffectList.insert( index, effect );
267 return true;
268}
269
271{
272 if ( index < 0 || index >= mEffectList.count() )
273 return false;
274 if ( !effect )
275 return false;
276
277 delete mEffectList.at( index );
278 mEffectList[index] = effect;
279 return true;
280}
281
283{
284 if ( index < 0 || index >= mEffectList.count() )
285 return nullptr;
286
287 return mEffectList.takeAt( index );
288}
289
290QList<QgsPaintEffect *> *QgsEffectStack::effectList()
291{
292 return &mEffectList;
293}
294
296{
297 if ( index >= 0 && index < mEffectList.count() )
298 {
299 return mEffectList.at( index );
300 }
301 else
302 {
303 return nullptr;
304 }
305}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2764
@ RequiresRasterization
The effect requires raster-based rendering.
Definition qgis.h:2864
QFlags< PaintEffectFlag > PaintEffectFlags
Flags which control how paint effects behave.
Definition qgis.h:2873
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.