Branch data Line data Source code
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 : : #include "qgspainteffectregistry.h" 20 : : #include "qgsrendercontext.h" 21 : : #include "qgsapplication.h" 22 : : #include <QPicture> 23 : : 24 : 0 : QgsEffectStack::QgsEffectStack( const QgsEffectStack &other ) 25 : 0 : : QgsPaintEffect( other ) 26 : 0 : { 27 : : //deep copy 28 : 0 : for ( int i = 0; i < other.count(); ++i ) 29 : : { 30 : 0 : appendEffect( other.effect( i )->clone() ); 31 : 0 : } 32 : 0 : } 33 : : 34 : 0 : QgsEffectStack::QgsEffectStack( QgsEffectStack &&other ) 35 : 0 : : QgsPaintEffect( other ) 36 : 0 : { 37 : 0 : std::swap( mEffectList, other.mEffectList ); 38 : 0 : } 39 : : 40 : 0 : QgsEffectStack::QgsEffectStack( const QgsPaintEffect &effect ) 41 : 0 : { 42 : 0 : appendEffect( effect.clone() ); 43 : 0 : } 44 : : 45 : 184 : QgsEffectStack::~QgsEffectStack() 46 : 184 : { 47 : 92 : clearStack(); 48 : 184 : } 49 : : 50 : 0 : QgsEffectStack &QgsEffectStack::operator=( const QgsEffectStack &rhs ) 51 : : { 52 : 0 : if ( &rhs == this ) 53 : 0 : return *this; 54 : : 55 : : //deep copy 56 : 0 : clearStack(); 57 : 0 : for ( int i = 0; i < rhs.count(); ++i ) 58 : : { 59 : 0 : appendEffect( rhs.effect( i )->clone() ); 60 : 0 : } 61 : 0 : mEnabled = rhs.enabled(); 62 : 0 : return *this; 63 : 0 : } 64 : : 65 : 0 : QgsEffectStack &QgsEffectStack::operator=( QgsEffectStack &&other ) 66 : : { 67 : 0 : std::swap( mEffectList, other.mEffectList ); 68 : 0 : mEnabled = other.enabled(); 69 : 0 : return *this; 70 : : } 71 : : 72 : 50 : QgsPaintEffect *QgsEffectStack::create( const QVariantMap &map ) 73 : : { 74 : 50 : QgsEffectStack *effect = new QgsEffectStack(); 75 : 50 : effect->readProperties( map ); 76 : 50 : return effect; 77 : : } 78 : : 79 : 0 : void QgsEffectStack::draw( QgsRenderContext &context ) 80 : : { 81 : 0 : QPainter *destPainter = context.painter(); 82 : : 83 : : //first, we build up a list of rendered effects 84 : : //we do this moving backwards through the stack, so that each effect's results 85 : : //becomes the source of the previous effect 86 : 0 : QPicture *sourcePic = new QPicture( *source() ); 87 : 0 : QPicture *currentPic = sourcePic; 88 : 0 : QList< QPicture * > results; 89 : 0 : for ( int i = mEffectList.count() - 1; i >= 0; --i ) 90 : : { 91 : 0 : QgsPaintEffect *effect = mEffectList.at( i ); 92 : 0 : if ( !effect->enabled() ) 93 : : { 94 : 0 : continue; 95 : : } 96 : : 97 : 0 : QPicture *pic = nullptr; 98 : 0 : if ( effect->type() == QLatin1String( "drawSource" ) ) 99 : : { 100 : : //draw source is always the original source, regardless of previous effect results 101 : 0 : pic = sourcePic; 102 : 0 : } 103 : : else 104 : : { 105 : 0 : pic = currentPic; 106 : : } 107 : : 108 : 0 : QPicture *resultPic = new QPicture(); 109 : 0 : QPainter p( resultPic ); 110 : 0 : context.setPainter( &p ); 111 : : //effect stack has it's own handling of the QPicture DPI issue, so 112 : : //we disable QgsPaintEffect's internal workaround 113 : 0 : effect->requiresQPainterDpiFix = false; 114 : 0 : effect->render( *pic, context ); 115 : 0 : effect->requiresQPainterDpiFix = true; 116 : 0 : p.end(); 117 : : 118 : 0 : results << resultPic; 119 : 0 : if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Render ) 120 : : { 121 : 0 : currentPic = resultPic; 122 : 0 : } 123 : 0 : } 124 : 0 : delete sourcePic; 125 : 0 : sourcePic = nullptr; 126 : : 127 : 0 : context.setPainter( destPainter ); 128 : : //then, we render all the results in the opposite order 129 : 0 : for ( int i = 0; i < mEffectList.count(); ++i ) 130 : : { 131 : 0 : if ( !mEffectList[i]->enabled() ) 132 : : { 133 : 0 : continue; 134 : : } 135 : : 136 : 0 : QPicture *pic = results.takeLast(); 137 : 0 : if ( mEffectList.at( i )->drawMode() != QgsPaintEffect::Modifier ) 138 : : { 139 : 0 : QgsScopedQPainterState painterState( context.painter() ); 140 : 0 : fixQPictureDpi( context.painter() ); 141 : 0 : context.painter()->drawPicture( 0, 0, *pic ); 142 : 0 : } 143 : 0 : delete pic; 144 : 0 : } 145 : 0 : } 146 : : 147 : 0 : QgsEffectStack *QgsEffectStack::clone() const 148 : : { 149 : 0 : return new QgsEffectStack( *this ); 150 : 0 : } 151 : : 152 : 0 : bool QgsEffectStack::saveProperties( QDomDocument &doc, QDomElement &element ) const 153 : : { 154 : : //effect stack needs to save all child effects 155 : 0 : if ( element.isNull() ) 156 : : { 157 : 0 : return false; 158 : : } 159 : : 160 : 0 : QDomElement effectElement = doc.createElement( QStringLiteral( "effect" ) ); 161 : 0 : effectElement.setAttribute( QStringLiteral( "type" ), type() ); 162 : 0 : effectElement.setAttribute( QStringLiteral( "enabled" ), mEnabled ); 163 : : 164 : 0 : bool ok = true; 165 : 0 : for ( QgsPaintEffect *effect : mEffectList ) 166 : : { 167 : 0 : if ( effect ) 168 : 0 : ok = ok && effect->saveProperties( doc, effectElement ); 169 : : } 170 : : 171 : 0 : element.appendChild( effectElement ); 172 : 0 : return ok; 173 : 0 : } 174 : : 175 : 50 : bool QgsEffectStack::readProperties( const QDomElement &element ) 176 : : { 177 : 50 : if ( element.isNull() ) 178 : : { 179 : 0 : return false; 180 : : } 181 : : 182 : 150 : mEnabled = ( element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) ); 183 : : 184 : 50 : clearStack(); 185 : : 186 : : //restore all child effects 187 : 50 : QDomNodeList childNodes = element.childNodes(); 188 : 245 : for ( int i = 0; i < childNodes.size(); ++i ) 189 : : { 190 : 195 : QDomElement childElement = childNodes.at( i ).toElement(); 191 : 195 : QgsPaintEffect *effect = QgsApplication::paintEffectRegistry()->createEffect( childElement ); 192 : 195 : if ( effect ) 193 : 195 : mEffectList << effect; 194 : 195 : } 195 : 50 : return true; 196 : 50 : } 197 : : 198 : 0 : QVariantMap QgsEffectStack::properties() const 199 : : { 200 : 0 : QVariantMap props; 201 : 0 : return props; 202 : 0 : } 203 : : 204 : 50 : void QgsEffectStack::readProperties( const QVariantMap &props ) 205 : : { 206 : 50 : Q_UNUSED( props ) 207 : 50 : } 208 : : 209 : 142 : void QgsEffectStack::clearStack() 210 : : { 211 : 142 : qDeleteAll( mEffectList ); 212 : 142 : mEffectList.clear(); 213 : 142 : } 214 : : 215 : 390 : void QgsEffectStack::appendEffect( QgsPaintEffect *effect ) 216 : : { 217 : 390 : mEffectList.append( effect ); 218 : 390 : } 219 : : 220 : 0 : bool QgsEffectStack::insertEffect( const int index, QgsPaintEffect *effect ) 221 : : { 222 : 0 : if ( index < 0 || index > mEffectList.count() ) 223 : 0 : return false; 224 : 0 : if ( !effect ) 225 : 0 : return false; 226 : : 227 : 0 : mEffectList.insert( index, effect ); 228 : 0 : return true; 229 : 0 : } 230 : : 231 : 0 : bool QgsEffectStack::changeEffect( const int index, QgsPaintEffect *effect ) 232 : : { 233 : 0 : if ( index < 0 || index >= mEffectList.count() ) 234 : 0 : return false; 235 : 0 : if ( !effect ) 236 : 0 : return false; 237 : : 238 : 0 : delete mEffectList.at( index ); 239 : 0 : mEffectList[index] = effect; 240 : 0 : return true; 241 : 0 : } 242 : : 243 : 0 : QgsPaintEffect *QgsEffectStack::takeEffect( const int index ) 244 : : { 245 : 0 : if ( index < 0 || index >= mEffectList.count() ) 246 : 0 : return nullptr; 247 : : 248 : 0 : return mEffectList.takeAt( index ); 249 : 0 : } 250 : : 251 : 0 : QList<QgsPaintEffect *> *QgsEffectStack::effectList() 252 : : { 253 : 0 : return &mEffectList; 254 : : } 255 : : 256 : 130 : QgsPaintEffect *QgsEffectStack::effect( int index ) const 257 : : { 258 : 130 : if ( index >= 0 && index < mEffectList.count() ) 259 : : { 260 : 130 : return mEffectList.at( index ); 261 : : } 262 : : else 263 : : { 264 : 0 : return nullptr; 265 : : } 266 : 130 : }