QGIS API Documentation 4.1.0-Master (60fea48833c)
Loading...
Searching...
No Matches
qgspainteffect.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspainteffect.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 "qgspainteffect.h"
19
20#include "qgsimageoperation.h"
21#include "qgspainting.h"
22#include "qgsrendercontext.h"
23#include "qgssymbollayerutils.h"
24
25#include <QPicture>
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
31 : mEnabled( other.enabled() )
32 , mDrawMode( other.drawMode() )
33{}
34
36{
37 // ensure painter is destroyed before picture it may be drawing on, just in case
38 mEffectPainter.reset();
39}
40
45
47{
49}
50
55
56bool QgsPaintEffect::saveProperties( QDomDocument &doc, QDomElement &element ) const
57{
58 if ( element.isNull() )
59 {
60 return false;
61 }
62 QDomElement effectElement = doc.createElement( u"effect"_s );
63 effectElement.setAttribute( u"type"_s, type() );
64 QgsSymbolLayerUtils::saveProperties( properties(), doc, effectElement );
65 element.appendChild( effectElement );
66 return true;
67}
68
69bool QgsPaintEffect::readProperties( const QDomElement &element )
70{
71 if ( element.isNull() )
72 {
73 return false;
74 }
75
76 const QVariantMap props = QgsSymbolLayerUtils::parseProperties( element );
77 readProperties( props );
78 return true;
79}
80
81void QgsPaintEffect::render( const QPicture &picture, QgsRenderContext &context )
82{
83 //set source picture
84 mPicture = picture;
85 mSourceImage = QImage();
86
87 draw( context );
88}
89
91{
92 //temporarily replace painter and direct paint operations for context to a QPicture
93 mPrevPainter = context.painter();
94
95 mTempPicture = std::make_unique< QPicture >();
96
97 mEffectPainter = std::make_unique< QPainter >();
98 mEffectPainter->begin( mTempPicture.get() );
99
100 context.setPainterFlagsUsingContext( mEffectPainter.get() );
101 context.setPainter( mEffectPainter.get() );
102}
103
105{
106 if ( !mEffectPainter )
107 return;
108
109 mEffectPainter->end();
110 mEffectPainter.reset();
111
112 //restore previous painter for context
113 context.setPainter( mPrevPainter );
114 mPrevPainter = nullptr;
115
116 // clear any existing pen/brush - sometimes these are not correctly restored when restoring a painter
117 // with a QPicture destination - see #15696
118 context.painter()->setPen( Qt::NoPen );
119 context.painter()->setBrush( Qt::NoBrush );
120
121 //draw using effect
122 render( *mTempPicture, context );
123
124 //clean up
125 mTempPicture.reset();
126}
127
128void QgsPaintEffect::drawSource( QPainter &painter )
129{
131 {
132 QgsPainting::drawPicture( &painter, QPointF( 0, 0 ), mPicture );
133 }
134 else
135 {
136 painter.drawPicture( 0, 0, mPicture );
137 }
138}
139
141{
142 //have we already created a source image? if so, return it
143 if ( !mSourceImage.isNull() )
144 {
145 return mSourceImage;
146 }
147
148 if ( mPicture.isNull() )
149 return QImage();
150
151 //else create it
152 //TODO - test with premultiplied image for speed
153 const QRectF bounds = imageBoundingRect( context );
154 mSourceImage = QImage( static_cast< int >( std::ceil( bounds.width() ) ), static_cast< int >( std::ceil( bounds.height() ) ), QImage::Format_ARGB32 );
155 mSourceImage.fill( Qt::transparent );
156 QPainter imagePainter( &mSourceImage );
157 imagePainter.setRenderHint( QPainter::Antialiasing );
158 imagePainter.translate( -bounds.left(), -bounds.top() );
159 imagePainter.drawPicture( 0, 0, mPicture );
160 imagePainter.end();
161 return mSourceImage;
162}
163
164QPointF QgsPaintEffect::imageOffset( const QgsRenderContext &context ) const
165{
166 return imageBoundingRect( context ).topLeft();
167}
168
169QRectF QgsPaintEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
170{
171 Q_UNUSED( context )
172 return rect;
173}
174
175void QgsPaintEffect::fixQPictureDpi( QPainter *painter ) const
176{
178}
179
180QRectF QgsPaintEffect::imageBoundingRect( const QgsRenderContext &context ) const
181{
182 return boundingRect( mPicture.boundingRect(), context );
183}
184
185
186//
187// QgsDrawSourceEffect
188//
189
191{
193 effect->readProperties( map );
194 return effect;
195}
196
198{
200 if ( mBlendMode != QPainter::CompositionMode_SourceOver || !qgsDoubleNear( mOpacity, 1.0 ) )
201 {
203 }
204 return res;
205}
206
208{
209 if ( !enabled() || !context.painter() )
210 return;
211
212 QPainter *painter = context.painter();
213
214 if ( mBlendMode == QPainter::CompositionMode_SourceOver && qgsDoubleNear( mOpacity, 1.0 ) )
215 {
216 //just draw unmodified source
217 drawSource( *painter );
218 }
219 else
220 {
221 //rasterize source and apply modifications
222 QImage image = sourceAsImage( context ).copy();
223 QgsImageOperation::multiplyOpacity( image, mOpacity, context.feedback() );
224 const QgsScopedQPainterState painterState( painter );
225 painter->setCompositionMode( mBlendMode );
226 painter->drawImage( imageOffset( context ), image );
227 }
228}
229
231{
232 return new QgsDrawSourceEffect( *this );
233}
234
236{
237 QVariantMap props;
238 props.insert( u"enabled"_s, mEnabled ? "1" : "0" );
239 props.insert( u"draw_mode"_s, QString::number( int( mDrawMode ) ) );
240 props.insert( u"blend_mode"_s, QString::number( int( mBlendMode ) ) );
241 props.insert( u"opacity"_s, QString::number( mOpacity ) );
242 return props;
243}
244
245void QgsDrawSourceEffect::readProperties( const QVariantMap &props )
246{
247 bool ok;
248 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( u"blend_mode"_s ).toInt( &ok ) );
249 if ( ok )
250 {
251 mBlendMode = mode;
252 }
253 if ( props.contains( u"transparency"_s ) )
254 {
255 const double transparency = props.value( u"transparency"_s ).toDouble( &ok );
256 if ( ok )
257 {
258 mOpacity = 1.0 - transparency;
259 }
260 }
261 else
262 {
263 const double opacity = props.value( u"opacity"_s ).toDouble( &ok );
264 if ( ok )
265 {
266 mOpacity = opacity;
267 }
268 }
269 mEnabled = props.value( u"enabled"_s, u"1"_s ).toInt();
270 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( u"draw_mode"_s, u"2"_s ).toInt() );
271}
272
273
274//
275// QgsEffectPainter
276//
277
279 : mRenderContext( renderContext )
280
281{
282 mPainter = renderContext.painter();
283 mPainter->save();
284}
285
287 : mRenderContext( renderContext )
288 , mEffect( effect )
289{
290 mPainter = mRenderContext.painter();
291 mPainter->save();
292 mEffect->begin( mRenderContext );
293}
294
296{
297 Q_ASSERT( !mEffect );
298
299 mEffect = effect;
300 mEffect->begin( mRenderContext );
301}
302
304{
305 Q_ASSERT( mEffect );
306
307 mEffect->end( mRenderContext );
308 mPainter->restore();
309}
@ RequiresRasterization
The effect requires raster-based rendering.
Definition qgis.h:2903
QFlags< PaintEffectFlag > PaintEffectFlags
Flags which control how paint effects behave.
Definition qgis.h:2912
QVariantMap properties() const override
Returns the properties describing the paint effect encoded in a string format.
QgsDrawSourceEffect()=default
double opacity() const
Returns the opacity for the effect.
void draw(QgsRenderContext &context) override
Handles drawing of the effect's result on to the specified render context.
QgsDrawSourceEffect * clone() const override
Duplicates an effect by creating a deep copy of the effect.
static QgsPaintEffect * create(const QVariantMap &map)
Creates a new QgsDrawSource effect from a properties string map.
void readProperties(const QVariantMap &props) override
Reads a string map of an effect's properties and restores the effect to the state described by the pr...
Qgis::PaintEffectFlags flags() const override
Returns flags which specify how the paint effect behaves.
QgsEffectPainter(QgsRenderContext &renderContext)
QgsEffectPainter constructor.
void setEffect(QgsPaintEffect *effect)
Sets the effect to be painted.
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
Base class for visual effects which can be applied to QPicture drawings.
QgsPaintEffect()=default
void setDrawMode(DrawMode drawMode)
Sets the draw mode for the effect.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
void drawSource(QPainter &painter)
Draws the source QPicture onto the specified painter.
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...
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
QImage sourceAsImage(QgsRenderContext &context)
Returns the source QPicture rendered to a new QImage.
QPointF imageOffset(const QgsRenderContext &context) const
Returns the offset which should be used when drawing the source image on to a destination render cont...
DrawMode drawMode() const
Returns the draw mode for the effect.
virtual ~QgsPaintEffect()
virtual void end(QgsRenderContext &context)
Ends interception of paint operations to a render context, and draws the result to the render context...
bool enabled() const
Returns whether the effect is enabled.
virtual Qgis::PaintEffectFlags flags() const
Returns flags which specify how the paint effect behaves.
virtual void render(const QPicture &picture, QgsRenderContext &context)
Renders a picture using the effect.
virtual QRectF boundingRect(const QRectF &rect, const QgsRenderContext &context) const
Returns the bounding rect required for drawing the effect.
virtual QVariantMap properties() const =0
Returns the properties describing the paint effect encoded in a string format.
Q_DECL_DEPRECATED void fixQPictureDpi(QPainter *painter) const
Applies a workaround to a QPainter to avoid an issue with incorrect scaling when drawing QPictures.
DrawMode
Drawing modes for effects.
virtual void draw(QgsRenderContext &context)=0
Handles drawing of the effect's result on to the specified render context.
virtual QString type() const =0
Returns the effect type.
static void applyScaleFixForQPictureDpi(QPainter *painter)
Applies a workaround to a painter to avoid an issue with incorrect scaling when drawing QPictures.
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.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Scoped object for saving and restoring a QPainter object's state.
static void saveProperties(QVariantMap props, QDomDocument &doc, QDomElement &element)
Saves the map of properties to XML.
static QVariantMap parseProperties(const QDomElement &element)
Parses the properties from XML and returns a map.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975