QGIS API Documentation 3.41.0-Master (88383c3d16f)
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#include "qgsimageoperation.h"
20#include "qgsrendercontext.h"
21#include "qgssymbollayerutils.h"
22#include "qgspainting.h"
23
24#include <QPicture>
25
27 : mEnabled( other.enabled() )
28 , mDrawMode( other.drawMode() )
29{
30
31}
32
34{
35 // ensure painter is destroyed before picture it may be drawing on, just in case
36 mEffectPainter.reset();
37}
38
39void QgsPaintEffect::setEnabled( const bool enabled )
40{
42}
43
48
49bool QgsPaintEffect::saveProperties( QDomDocument &doc, QDomElement &element ) const
50{
51 if ( element.isNull() )
52 {
53 return false;
54 }
55 QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) );
56 effectElement.setAttribute( QStringLiteral( "type" ), type() );
57 QgsSymbolLayerUtils::saveProperties( properties(), doc, effectElement );
58 element.appendChild( effectElement );
59 return true;
60}
61
62bool QgsPaintEffect::readProperties( const QDomElement &element )
63{
64 if ( element.isNull() )
65 {
66 return false;
67 }
68
69 const QVariantMap props = QgsSymbolLayerUtils::parseProperties( element );
70 readProperties( props );
71 return true;
72}
73
74void QgsPaintEffect::render( const QPicture &picture, QgsRenderContext &context )
75{
76 //set source picture
77 mPicture = picture;
78 mSourceImage = QImage();
79
80 draw( context );
81}
82
84{
85 //temporarily replace painter and direct paint operations for context to a QPicture
86 mPrevPainter = context.painter();
87
88 mTempPicture = std::make_unique< QPicture >();
89
90 mEffectPainter = std::make_unique< QPainter >();
91 mEffectPainter->begin( mTempPicture.get() );
92
93 context.setPainterFlagsUsingContext( mEffectPainter.get() );
94 context.setPainter( mEffectPainter.get() );
95}
96
98{
99 if ( !mEffectPainter )
100 return;
101
102 mEffectPainter->end();
103 mEffectPainter.reset();
104
105 //restore previous painter for context
106 context.setPainter( mPrevPainter );
107 mPrevPainter = nullptr;
108
109 // clear any existing pen/brush - sometimes these are not correctly restored when restoring a painter
110 // with a QPicture destination - see #15696
111 context.painter()->setPen( Qt::NoPen );
112 context.painter()->setBrush( Qt::NoBrush );
113
114 //draw using effect
115 render( *mTempPicture, context );
116
117 //clean up
118 mTempPicture.reset();
119}
120
121void QgsPaintEffect::drawSource( QPainter &painter )
122{
124 {
125 QgsPainting::drawPicture( &painter, QPointF( 0, 0 ), mPicture );
126 }
127 else
128 {
129 painter.drawPicture( 0, 0, mPicture );
130 }
131}
132
134{
135 //have we already created a source image? if so, return it
136 if ( !mSourceImage.isNull() )
137 {
138 return mSourceImage;
139 }
140
141 if ( mPicture.isNull() )
142 return QImage();
143
144 //else create it
145 //TODO - test with premultiplied image for speed
146 const QRectF bounds = imageBoundingRect( context );
147 mSourceImage = QImage( static_cast< int >( std::ceil( bounds.width() ) ),
148 static_cast< int >( std::ceil( bounds.height() ) ), QImage::Format_ARGB32 );
149 mSourceImage.fill( Qt::transparent );
150 QPainter imagePainter( &mSourceImage );
151 imagePainter.setRenderHint( QPainter::Antialiasing );
152 imagePainter.translate( -bounds.left(), -bounds.top() );
153 imagePainter.drawPicture( 0, 0, mPicture );
154 imagePainter.end();
155 return mSourceImage;
156}
157
158QPointF QgsPaintEffect::imageOffset( const QgsRenderContext &context ) const
159{
160 return imageBoundingRect( context ).topLeft();
161}
162
163QRectF QgsPaintEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const
164{
165 Q_UNUSED( context )
166 return rect;
167}
168
169void QgsPaintEffect::fixQPictureDpi( QPainter *painter ) const
170{
172}
173
174QRectF QgsPaintEffect::imageBoundingRect( const QgsRenderContext &context ) const
175{
176 return boundingRect( mPicture.boundingRect(), context );
177}
178
179
180//
181// QgsDrawSourceEffect
182//
183
185{
187 effect->readProperties( map );
188 return effect;
189}
190
192{
193 if ( !enabled() || !context.painter() )
194 return;
195
196 QPainter *painter = context.painter();
197
198 if ( mBlendMode == QPainter::CompositionMode_SourceOver && qgsDoubleNear( mOpacity, 1.0 ) )
199 {
200 //just draw unmodified source
201 drawSource( *painter );
202 }
203 else
204 {
205 //rasterize source and apply modifications
206 QImage image = sourceAsImage( context ).copy();
207 QgsImageOperation::multiplyOpacity( image, mOpacity, context.feedback() );
208 const QgsScopedQPainterState painterState( painter );
209 painter->setCompositionMode( mBlendMode );
210 painter->drawImage( imageOffset( context ), image );
211 }
212}
213
215{
216 return new QgsDrawSourceEffect( *this );
217}
218
220{
221 QVariantMap props;
222 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
223 props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) );
224 props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) );
225 props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) );
226 return props;
227}
228
229void QgsDrawSourceEffect::readProperties( const QVariantMap &props )
230{
231 bool ok;
232 const QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) );
233 if ( ok )
234 {
235 mBlendMode = mode;
236 }
237 if ( props.contains( QStringLiteral( "transparency" ) ) )
238 {
239 const double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok );
240 if ( ok )
241 {
242 mOpacity = 1.0 - transparency;
243 }
244 }
245 else
246 {
247 const double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok );
248 if ( ok )
249 {
250 mOpacity = opacity;
251 }
252 }
253 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
254 mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() );
255}
256
257
258//
259// QgsEffectPainter
260//
261
263 : mRenderContext( renderContext )
264
265{
266 mPainter = renderContext.painter();
267 mPainter->save();
268}
269
271 : mRenderContext( renderContext )
272 , mEffect( effect )
273{
274 mPainter = mRenderContext.painter();
275 mPainter->save();
276 mEffect->begin( mRenderContext );
277}
278
280{
281 Q_ASSERT( !mEffect );
282
283 mEffect = effect;
284 mEffect->begin( mRenderContext );
285}
286
288{
289 Q_ASSERT( mEffect );
290
291 mEffect->end( mRenderContext );
292 mPainter->restore();
293}
A paint effect which draws the source picture with minor or no alterations.
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...
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 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:6125