Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsgloweffect.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 "qgsgloweffect.h" 19 : : #include "qgssymbollayerutils.h" 20 : : #include "qgsimageoperation.h" 21 : : #include "qgscolorramp.h" 22 : : #include "qgsunittypes.h" 23 : : 24 : 226 : QgsGlowEffect::QgsGlowEffect() 25 : 226 : : mColor( Qt::white ) 26 : 452 : { 27 : : 28 : 226 : } 29 : : 30 : 0 : QgsGlowEffect::QgsGlowEffect( const QgsGlowEffect &other ) 31 : 0 : : QgsPaintEffect( other ) 32 : 0 : { 33 : 0 : operator=( other ); 34 : 0 : } 35 : : 36 : 166 : QgsGlowEffect::~QgsGlowEffect() 37 : 166 : { 38 : 166 : delete mRamp; 39 : 166 : } 40 : : 41 : 0 : void QgsGlowEffect::draw( QgsRenderContext &context ) 42 : : { 43 : 0 : if ( !source() || !enabled() || !context.painter() ) 44 : 0 : return; 45 : : 46 : 0 : QImage im = sourceAsImage( context )->copy(); 47 : : 48 : 0 : QgsColorRamp *ramp = nullptr; 49 : 0 : std::unique_ptr< QgsGradientColorRamp > tempRamp; 50 : 0 : if ( mColorType == ColorRamp && mRamp ) 51 : : { 52 : 0 : ramp = mRamp; 53 : 0 : } 54 : : else 55 : : { 56 : : //create a temporary ramp 57 : 0 : QColor transparentColor = mColor; 58 : 0 : transparentColor.setAlpha( 0 ); 59 : 0 : tempRamp.reset( new QgsGradientColorRamp( mColor, transparentColor ) ); 60 : 0 : ramp = tempRamp.get(); 61 : : } 62 : : 63 : 0 : QgsImageOperation::DistanceTransformProperties dtProps; 64 : 0 : dtProps.spread = context.convertToPainterUnits( mSpread, mSpreadUnit, mSpreadMapUnitScale ); 65 : 0 : dtProps.useMaxDistance = false; 66 : 0 : dtProps.shadeExterior = shadeExterior(); 67 : 0 : dtProps.ramp = ramp; 68 : 0 : QgsImageOperation::distanceTransform( im, dtProps ); 69 : : 70 : 0 : int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) ); 71 : 0 : if ( blurLevel <= 16 ) 72 : : { 73 : 0 : QgsImageOperation::stackBlur( im, blurLevel ); 74 : 0 : } 75 : : else 76 : : { 77 : 0 : QImage *imb = QgsImageOperation::gaussianBlur( im, blurLevel ); 78 : 0 : im = QImage( *imb ); 79 : 0 : delete imb; 80 : : } 81 : : 82 : 0 : QgsImageOperation::multiplyOpacity( im, mOpacity ); 83 : : 84 : 0 : if ( !shadeExterior() ) 85 : : { 86 : : //only keep interior portion 87 : 0 : QPainter p( &im ); 88 : 0 : p.setRenderHint( QPainter::Antialiasing ); 89 : 0 : p.setCompositionMode( QPainter::CompositionMode_DestinationIn ); 90 : 0 : p.drawImage( 0, 0, *sourceAsImage( context ) ); 91 : 0 : p.end(); 92 : 0 : } 93 : : 94 : 0 : QPainter *painter = context.painter(); 95 : 0 : QgsScopedQPainterState painterState( painter ); 96 : 0 : painter->setCompositionMode( mBlendMode ); 97 : 0 : painter->drawImage( imageOffset( context ), im ); 98 : 0 : } 99 : : 100 : 0 : QVariantMap QgsGlowEffect::properties() const 101 : : { 102 : 0 : QVariantMap props; 103 : 0 : props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" ); 104 : 0 : props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) ); 105 : 0 : props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) ); 106 : 0 : props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) ); 107 : 0 : props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) ); 108 : 0 : props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) ); 109 : 0 : props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) ); 110 : 0 : props.insert( QStringLiteral( "spread" ), QString::number( mSpread ) ); 111 : 0 : props.insert( QStringLiteral( "spread_unit" ), QgsUnitTypes::encodeUnit( mSpreadUnit ) ); 112 : 0 : props.insert( QStringLiteral( "spread_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mSpreadMapUnitScale ) ); 113 : 0 : props.insert( QStringLiteral( "color_type" ), QString::number( static_cast< int >( mColorType ) ) ); 114 : 0 : props.insert( QStringLiteral( "single_color" ), QgsSymbolLayerUtils::encodeColor( mColor ) ); 115 : : 116 : 0 : if ( mRamp ) 117 : : { 118 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 119 : : props.unite( mRamp->properties() ); 120 : : #else 121 : 0 : props.insert( mRamp->properties() ); 122 : : #endif 123 : 0 : } 124 : : 125 : 0 : return props; 126 : 0 : } 127 : : 128 : 140 : void QgsGlowEffect::readProperties( const QVariantMap &props ) 129 : : { 130 : : bool ok; 131 : 280 : QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) ); 132 : 140 : if ( ok ) 133 : : { 134 : 70 : mBlendMode = mode; 135 : 70 : } 136 : 280 : if ( props.contains( QStringLiteral( "transparency" ) ) ) 137 : : { 138 : 0 : double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok ); 139 : 0 : if ( ok ) 140 : : { 141 : 0 : mOpacity = 1.0 - transparency; 142 : 0 : } 143 : 0 : } 144 : : else 145 : : { 146 : 280 : double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok ); 147 : 140 : if ( ok ) 148 : : { 149 : 70 : mOpacity = opacity; 150 : 70 : } 151 : : } 152 : 420 : mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt(); 153 : 420 : mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() ); 154 : 280 : double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok ); 155 : 140 : if ( ok ) 156 : : { 157 : 70 : mBlurLevel = level; 158 : 140 : if ( !props.contains( QStringLiteral( "blur_unit" ) ) ) 159 : : { 160 : : // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters 161 : 0 : mBlurLevel *= 0.2645; 162 : 0 : } 163 : 70 : } 164 : 280 : mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() ); 165 : 280 : mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() ); 166 : 280 : double spread = props.value( QStringLiteral( "spread" ) ).toDouble( &ok ); 167 : 140 : if ( ok ) 168 : : { 169 : 70 : mSpread = spread; 170 : 70 : } 171 : 280 : mSpreadUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "spread_unit" ) ).toString() ); 172 : 280 : mSpreadMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "spread_unit_scale" ) ).toString() ); 173 : 280 : QgsGlowEffect::GlowColorType type = static_cast< QgsGlowEffect::GlowColorType >( props.value( QStringLiteral( "color_type" ) ).toInt( &ok ) ); 174 : 140 : if ( ok ) 175 : : { 176 : 70 : mColorType = type; 177 : 70 : } 178 : 280 : if ( props.contains( QStringLiteral( "single_color" ) ) ) 179 : : { 180 : 140 : mColor = QgsSymbolLayerUtils::decodeColor( props.value( QStringLiteral( "single_color" ) ).toString() ); 181 : 70 : } 182 : : 183 : : //attempt to create color ramp from props 184 : 140 : delete mRamp; 185 : 420 : if ( props.contains( QStringLiteral( "rampType" ) ) && props[QStringLiteral( "rampType" )] == QgsCptCityColorRamp::typeString() ) 186 : : { 187 : 0 : mRamp = QgsCptCityColorRamp::create( props ); 188 : 0 : } 189 : : else 190 : : { 191 : 140 : mRamp = QgsGradientColorRamp::create( props ); 192 : : } 193 : 140 : } 194 : : 195 : 0 : void QgsGlowEffect::setRamp( QgsColorRamp *ramp ) 196 : : { 197 : 0 : delete mRamp; 198 : 0 : mRamp = ramp; 199 : 0 : } 200 : : 201 : 0 : QgsGlowEffect &QgsGlowEffect::operator=( const QgsGlowEffect &rhs ) 202 : : { 203 : 0 : if ( &rhs == this ) 204 : 0 : return *this; 205 : : 206 : 0 : delete mRamp; 207 : : 208 : 0 : mSpread = rhs.spread(); 209 : 0 : mSpreadUnit = rhs.spreadUnit(); 210 : 0 : mSpreadMapUnitScale = rhs.spreadMapUnitScale(); 211 : 0 : mRamp = rhs.ramp() ? rhs.ramp()->clone() : nullptr; 212 : 0 : mBlurLevel = rhs.blurLevel(); 213 : 0 : mBlurUnit = rhs.mBlurUnit; 214 : 0 : mBlurMapUnitScale = rhs.mBlurMapUnitScale; 215 : 0 : mOpacity = rhs.opacity(); 216 : 0 : mColor = rhs.color(); 217 : 0 : mBlendMode = rhs.blendMode(); 218 : 0 : mColorType = rhs.colorType(); 219 : : 220 : 0 : return *this; 221 : 0 : } 222 : : 223 : 0 : QRectF QgsGlowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const 224 : : { 225 : : //blur radius and spread size 226 : 0 : int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) ); 227 : 0 : double spread = context.convertToPainterUnits( mSpread, mSpreadUnit, mSpreadMapUnitScale ); 228 : : //plus possible extension due to blur, with a couple of extra pixels thrown in for safety 229 : 0 : spread += blurLevel * 2 + 10; 230 : 0 : return rect.adjusted( -spread, -spread, spread, spread ); 231 : : } 232 : : 233 : : 234 : : // 235 : : // QgsOuterGlowEffect 236 : : // 237 : : 238 : 113 : QgsOuterGlowEffect::QgsOuterGlowEffect() 239 : 113 : : QgsGlowEffect() 240 : 226 : { 241 : : 242 : 113 : } 243 : : 244 : 35 : QgsPaintEffect *QgsOuterGlowEffect::create( const QVariantMap &map ) 245 : : { 246 : 35 : QgsOuterGlowEffect *effect = new QgsOuterGlowEffect(); 247 : 35 : effect->readProperties( map ); 248 : 35 : return effect; 249 : 0 : } 250 : : 251 : 0 : QgsOuterGlowEffect *QgsOuterGlowEffect::clone() const 252 : : { 253 : 0 : QgsOuterGlowEffect *newEffect = new QgsOuterGlowEffect( *this ); 254 : 0 : return newEffect; 255 : 0 : } 256 : : 257 : : 258 : : // 259 : : // QgsInnerGlowEffect 260 : : // 261 : : 262 : 113 : QgsInnerGlowEffect::QgsInnerGlowEffect() 263 : 113 : : QgsGlowEffect() 264 : 226 : { 265 : : 266 : 113 : } 267 : : 268 : 35 : QgsPaintEffect *QgsInnerGlowEffect::create( const QVariantMap &map ) 269 : : { 270 : 35 : QgsInnerGlowEffect *effect = new QgsInnerGlowEffect(); 271 : 35 : effect->readProperties( map ); 272 : 35 : return effect; 273 : 0 : } 274 : : 275 : 0 : QgsInnerGlowEffect *QgsInnerGlowEffect::clone() const 276 : 226 : { 277 : 226 : QgsInnerGlowEffect *newEffect = new QgsInnerGlowEffect( *this ); 278 : 0 : return newEffect; 279 : 226 : }