1 /*************************************************************************** 2 qgspainteffect.h 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 #ifndef QGSPAINTEFFECT_H 18 #define QGSPAINTEFFECT_H 19 20 #include "qgis_core.h" 21 #include "qgis_sip.h" 22 #include "qgssymbollayer.h" 23 #include <QPainter> 24 #include <QDomDocument> 25 #include <QDomElement> 26 27 class QgsRenderContext; 28 29 /** 30 * \ingroup core 31 * \class QgsPaintEffect 32 * \brief Base class for visual effects which can be applied to QPicture drawings 33 * 34 * QgsPaintEffect objects can be used to modify QPicture drawings prior to rendering 35 * them with a QPainter operation. There are two methods for drawing using an effect, 36 * either drawing a picture directly, or by intercepting drawing operations to a 37 * render context. 38 * 39 * To directly draw a picture, use the render() method with a source 40 * QPicture and destination render context. 41 * 42 * Intercepting drawing operations to a render context is achieved by first calling 43 * the begin() method, passing a render context. Any drawing operations 44 * performed on the render context will not directly affect the context's paint 45 * device. When the drawing operations have been completed, call the end() 46 * method. This will perform the paint effect on the intercepted drawing operations 47 * and render the result to the render context's paint device. 48 * 49 * \see QgsPaintEffectRegistry 50 * \since QGIS 2.9 51 */ 52 53 class CORE_EXPORT QgsPaintEffect SIP_NODEFAULTCTORS 54 { 55 56 #ifdef SIP_RUN 57 SIP_CONVERT_TO_SUBCLASS_CODE 58 if ( sipCpp->type() == "drawSource" && dynamic_cast<QgsDrawSourceEffect *>( sipCpp ) != NULL ) 59 { 60 sipType = sipType_QgsDrawSourceEffect; 61 } 62 else if ( sipCpp->type() == "effectStack" && dynamic_cast<QgsEffectStack *>( sipCpp ) != NULL ) 63 { 64 sipType = sipType_QgsEffectStack; 65 } 66 else if ( sipCpp->type() == "blur" && dynamic_cast<QgsBlurEffect *>( sipCpp ) != NULL ) 67 { 68 sipType = sipType_QgsBlurEffect; 69 } 70 else if ( sipCpp->type() == "dropShadow" && dynamic_cast<QgsDropShadowEffect *>( sipCpp ) != NULL ) 71 { 72 sipType = sipType_QgsDropShadowEffect; 73 } 74 else if ( sipCpp->type() == "outerGlow" && dynamic_cast<QgsOuterGlowEffect *>( sipCpp ) != NULL ) 75 { 76 sipType = sipType_QgsOuterGlowEffect; 77 } 78 else if ( sipCpp->type() == "innerGlow" && dynamic_cast<QgsInnerGlowEffect *>( sipCpp ) != NULL ) 79 { 80 sipType = sipType_QgsInnerGlowEffect; 81 } 82 else if ( sipCpp->type() == "transform" && dynamic_cast<QgsTransformEffect *>( sipCpp ) != NULL ) 83 { 84 sipType = sipType_QgsTransformEffect; 85 } 86 else if ( sipCpp->type() == "color" && dynamic_cast<QgsColorEffect *>( sipCpp ) != NULL ) 87 { 88 sipType = sipType_QgsColorEffect; 89 } 90 else 91 { 92 sipType = 0; 93 } 94 SIP_END 95 #endif 96 97 public: 98 99 /** 100 * Drawing modes for effects. These modes are used only when effects are 101 * drawn as part of an effects stack 102 * \see QgsEffectStack 103 */ 104 enum DrawMode 105 { 106 Modifier, //!< The result of the effect is not rendered, but is passed on to following effects in the stack 107 Render, //!< The result of the effect is rendered on the destination, but does not affect subsequent effects in the stack 108 ModifyAndRender //!< The result of the effect is both rendered and passed on to subsequent effects in the stack 109 }; 110 111 /** 112 * Constructor for QgsPaintEffect. 113 */ 114 QgsPaintEffect() = default; 115 116 QgsPaintEffect( const QgsPaintEffect &other ); 117 virtual ~QgsPaintEffect(); 118 119 /** 120 * Returns the effect type. 121 * \returns unique string representation of the effect type 122 */ 123 virtual QString type() const = 0; 124 125 /** 126 * Duplicates an effect by creating a deep copy of the effect 127 * \returns clone of paint effect 128 */ 129 virtual QgsPaintEffect *clone() const = 0 SIP_FACTORY; 130 131 /** 132 * Returns the properties describing the paint effect encoded in a 133 * string format. 134 * \returns string map of properties, in the form property key, value 135 * \see readProperties 136 * \see saveProperties 137 */ 138 virtual QgsStringMap properties() const = 0; 139 140 /** 141 * Reads a string map of an effect's properties and restores the effect 142 * to the state described by the properties map. 143 * \param props effect properties encoded in a string map 144 * \see properties 145 */ 146 virtual void readProperties( const QgsStringMap &props ) = 0; 147 148 /** 149 * Saves the current state of the effect to a DOM element. The default 150 * behavior is to save the properties string map returned by 151 * properties(). 152 * \param doc destination DOM document 153 * \param element destination DOM element 154 * \returns TRUE if save was successful 155 * \see readProperties 156 */ 157 virtual bool saveProperties( QDomDocument &doc, QDomElement &element ) const; 158 159 /** 160 * Restores the effect to the state described by a DOM element. 161 * \param element DOM element describing an effect's state 162 * \returns TRUE if read was successful 163 * \see saveProperties 164 */ 165 virtual bool readProperties( const QDomElement &element ); 166 167 /** 168 * Renders a picture using the effect. 169 * \param picture source QPicture to render 170 * \param context destination render context 171 * \see begin 172 */ 173 virtual void render( QPicture &picture, QgsRenderContext &context ); 174 175 /** 176 * Begins intercepting paint operations to a render context. When the corresponding 177 * end() member is called all intercepted paint operations will be 178 * drawn to the render context after being modified by the effect. 179 * \param context destination render context 180 * \see end 181 * \see render 182 */ 183 virtual void begin( QgsRenderContext &context ); 184 185 /** 186 * Ends interception of paint operations to a render context, and draws the result 187 * to the render context after being modified by the effect. 188 * \param context destination render context 189 * \see begin 190 */ 191 virtual void end( QgsRenderContext &context ); 192 193 /** 194 * Returns whether the effect is enabled 195 * \returns TRUE if effect is enabled 196 * \see setEnabled 197 */ enabled()198 bool enabled() const { return mEnabled; } 199 200 /** 201 * Sets whether the effect is enabled 202 * \param enabled set to FALSE to disable the effect 203 * \see enabled 204 */ 205 void setEnabled( bool enabled ); 206 207 /** 208 * Returns the draw mode for the effect. This property only has an 209 * effect if the paint effect is used in a QgsEffectStack. 210 * \returns draw mode for effect 211 * \see setDrawMode 212 */ drawMode()213 DrawMode drawMode() const { return mDrawMode; } 214 215 /** 216 * Sets the draw mode for the effect. This property only has an 217 * effect if the paint effect is used in a QgsEffectStack. 218 * \param drawMode draw mode for effect 219 * \see drawMode 220 */ 221 void setDrawMode( DrawMode drawMode ); 222 223 protected: 224 225 bool mEnabled = true; 226 DrawMode mDrawMode = ModifyAndRender; 227 bool requiresQPainterDpiFix = true; 228 229 /** 230 * Handles drawing of the effect's result on to the specified render context. 231 * Derived classes must reimplement this method to apply any transformations to 232 * the source QPicture and draw the result using the context's painter. 233 * \param context destination render context 234 * \see drawSource 235 */ 236 virtual void draw( QgsRenderContext &context ) = 0; 237 238 /** 239 * Draws the source QPicture onto the specified painter. Handles scaling of the picture 240 * to account for the destination painter's DPI. 241 * \param painter destination painter 242 * \see source 243 * \see sourceAsImage 244 */ 245 void drawSource( QPainter &painter ); 246 247 /** 248 * Returns the source QPicture. The draw() member can utilize this when 249 * drawing the effect. 250 * \returns source QPicture 251 * \see drawSource 252 * \see sourceAsImage 253 */ source()254 const QPicture *source() const { return mPicture; } 255 256 /** 257 * Returns the source QPicture rendered to a new QImage. The draw() member can 258 * utilize this when drawing the effect. The image will be padded or cropped from the original 259 * source QPicture by the results of the boundingRect() method. 260 * The result is cached to speed up subsequent calls to sourceAsImage. 261 * \returns source QPicture rendered to an image 262 * \see drawSource 263 * \see source 264 * \see imageOffset 265 * \see boundingRect 266 */ 267 QImage *sourceAsImage( QgsRenderContext &context ); 268 269 /** 270 * Returns the offset which should be used when drawing the source image on to a destination 271 * render context. 272 * \param context destination render context 273 * \returns point offset for image top left corner 274 * \see sourceAsImage 275 */ 276 QPointF imageOffset( const QgsRenderContext &context ) const; 277 278 /** 279 * Returns the bounding rect required for drawing the effect. This method can be used 280 * to expand the bounding rect of a source picture to account for offset or blurring 281 * effects. 282 * \param rect original source bounding rect 283 * \param context destination render context 284 * \returns modified bounding rect 285 * \see sourceAsImage 286 */ 287 virtual QRectF boundingRect( const QRectF &rect, const QgsRenderContext &context ) const; 288 289 /** 290 * Applies a workaround to a QPainter to avoid an issue with incorrect scaling 291 * when drawing QPictures. This may need to be called by derived classes prior 292 * to rendering results onto a painter. 293 * \param painter destination painter 294 */ 295 void fixQPictureDpi( QPainter *painter ) const; 296 297 private: 298 299 const QPicture *mPicture = nullptr; 300 QImage *mSourceImage = nullptr; 301 bool mOwnsImage = false; 302 303 QPainter *mPrevPainter = nullptr; 304 QPainter *mEffectPainter = nullptr; 305 QPicture *mTempPicture = nullptr; 306 307 QRectF imageBoundingRect( const QgsRenderContext &context ) const; 308 309 friend class QgsEffectStack; 310 311 QgsPaintEffect &operator= ( const QgsPaintEffect & ) = delete; 312 313 }; 314 315 /** 316 * \ingroup core 317 * \class QgsDrawSourceEffect 318 * \brief A paint effect which draws the source picture with minor or no alterations 319 * 320 * The draw source effect can be used to draw an unaltered copy of the original source 321 * picture. Minor changes like lowering the opacity and applying a blend mode are 322 * supported, however these changes will force the resultant output to be rasterized. 323 * If no alterations are performed then the original picture will be rendered as a vector. 324 * 325 * \since QGIS 2.9 326 */ 327 328 class CORE_EXPORT QgsDrawSourceEffect : public QgsPaintEffect SIP_NODEFAULTCTORS 329 { 330 public: 331 332 //! Constructor for QgsDrawSourceEffect 333 QgsDrawSourceEffect() = default; 334 335 /** 336 * Creates a new QgsDrawSource effect from a properties string map. 337 * \param map encoded properties string map 338 * \returns new QgsDrawSourceEffect 339 */ 340 static QgsPaintEffect *create( const QgsStringMap &map ) SIP_FACTORY; 341 type()342 QString type() const override { return QStringLiteral( "drawSource" ); } 343 QgsDrawSourceEffect *clone() const override SIP_FACTORY; 344 QgsStringMap properties() const override; 345 void readProperties( const QgsStringMap &props ) override; 346 347 /** 348 * Sets the \a opacity for the effect. 349 * \param opacity double between 0 and 1 inclusive, where 0 is fully transparent 350 * and 1 is fully opaque 351 * \see opacity() 352 */ setOpacity(const double opacity)353 void setOpacity( const double opacity ) { mOpacity = opacity; } 354 355 /** 356 * Returns the opacity for the effect 357 * \returns opacity value between 0 and 1 inclusive, where 0 is fully transparent 358 * and 1 is fully opaque 359 * \see setOpacity() 360 */ opacity()361 double opacity() const { return mOpacity; } 362 363 /** 364 * Sets the blend mode for the effect 365 * \param mode blend mode used for drawing the source on to a destination 366 * paint device 367 * \see blendMode 368 */ setBlendMode(const QPainter::CompositionMode mode)369 void setBlendMode( const QPainter::CompositionMode mode ) { mBlendMode = mode; } 370 371 /** 372 * Returns the blend mode for the effect 373 * \returns blend mode used for drawing the source on to a destination 374 * paint device 375 * \see setBlendMode 376 */ blendMode()377 QPainter::CompositionMode blendMode() const { return mBlendMode; } 378 379 protected: 380 381 void draw( QgsRenderContext &context ) override; 382 383 private: 384 385 double mOpacity = 1.0; 386 QPainter::CompositionMode mBlendMode = QPainter::CompositionMode_SourceOver; 387 }; 388 389 /** 390 * \ingroup core 391 * \class QgsEffectPainter 392 * \brief A class to manager painter saving and restoring required for effect drawing 393 * 394 * \since QGIS 3.0 395 */ 396 class CORE_EXPORT QgsEffectPainter 397 { 398 public: 399 400 /** 401 * QgsEffectPainter constructor 402 * 403 * \param renderContext the QgsRenderContext object 404 * \since QGIS 3.0 405 */ 406 QgsEffectPainter( QgsRenderContext &renderContext ); 407 408 /** 409 * QgsEffectPainter constructor alternative if no painter translation is needed 410 * 411 * \param renderContext the QgsRenderContext object 412 * \param effect the QgsPaintEffect object 413 * \since QGIS 3.0 414 */ 415 QgsEffectPainter( QgsRenderContext &renderContext, QgsPaintEffect *effect ); 416 ~QgsEffectPainter(); 417 418 /** 419 * Sets the effect to be painted 420 * 421 * \param effect the QgsPaintEffect object 422 */ 423 void setEffect( QgsPaintEffect *effect ); 424 425 ///@cond PRIVATE 426 427 /** 428 * Access to the painter object 429 * 430 * \since QGIS 3.0 431 */ 432 QPainter *operator->() { return mPainter; } 433 ///@endcond 434 435 private: 436 #ifdef SIP_RUN 437 const QgsEffectPainter &operator=( const QgsEffectPainter & ); 438 #endif 439 440 QgsRenderContext &mRenderContext; 441 QPainter *mPainter = nullptr; 442 QgsPaintEffect *mEffect = nullptr; 443 }; 444 445 #endif // QGSPAINTEFFECT_H 446 447