Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsshadoweffect.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 "qgsshadoweffect.h" 19 : : #include "qgsimageoperation.h" 20 : : #include "qgssymbollayerutils.h" 21 : : #include "qgsunittypes.h" 22 : : 23 : 231 : QgsShadowEffect::QgsShadowEffect() 24 : 231 : : mColor( Qt::black ) 25 : 462 : { 26 : : 27 : 231 : } 28 : : 29 : 0 : void QgsShadowEffect::draw( QgsRenderContext &context ) 30 : : { 31 : 0 : if ( !source() || !enabled() || !context.painter() ) 32 : 0 : return; 33 : : 34 : 0 : QImage colorisedIm = sourceAsImage( context )->copy(); 35 : : 36 : 0 : QPainter *painter = context.painter(); 37 : 0 : QgsScopedQPainterState painterState( painter ); 38 : 0 : painter->setCompositionMode( mBlendMode ); 39 : : 40 : 0 : if ( !exteriorShadow() ) 41 : : { 42 : : //inner shadow, first invert the opacity. The color does not matter since we will 43 : : //be replacing it anyway 44 : 0 : colorisedIm.invertPixels( QImage::InvertRgba ); 45 : 0 : } 46 : : 47 : 0 : QgsImageOperation::overlayColor( colorisedIm, mColor ); 48 : : 49 : 0 : int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) ); 50 : 0 : if ( blurLevel <= 16 ) 51 : : { 52 : 0 : QgsImageOperation::stackBlur( colorisedIm, blurLevel ); 53 : 0 : } 54 : : else 55 : : { 56 : 0 : QImage *imb = QgsImageOperation::gaussianBlur( colorisedIm, blurLevel ); 57 : 0 : colorisedIm = QImage( *imb ); 58 : 0 : delete imb; 59 : : } 60 : : 61 : 0 : double offsetDist = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale ); 62 : : 63 : 0 : double angleRad = mOffsetAngle * M_PI / 180; // to radians 64 : 0 : QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), 65 : 0 : -offsetDist * std::sin( angleRad + M_PI_2 ) ); 66 : : 67 : : //transparency, scale 68 : 0 : QgsImageOperation::multiplyOpacity( colorisedIm, mOpacity ); 69 : : 70 : 0 : if ( !exteriorShadow() ) 71 : : { 72 : : //inner shadow, do a bit of painter juggling 73 : 0 : QImage innerShadowIm( colorisedIm.width(), colorisedIm.height(), QImage::Format_ARGB32 ); 74 : 0 : innerShadowIm.fill( Qt::transparent ); 75 : 0 : QPainter imPainter( &innerShadowIm ); 76 : : 77 : : //draw shadow at offset 78 : 0 : imPainter.drawImage( transPt.x(), transPt.y(), colorisedIm ); 79 : : 80 : : //restrict shadow so it's only drawn on top of original image 81 : 0 : imPainter.setCompositionMode( QPainter::CompositionMode_DestinationIn ); 82 : 0 : imPainter.drawImage( 0, 0, *sourceAsImage( context ) ); 83 : 0 : imPainter.end(); 84 : : 85 : 0 : painter->drawImage( imageOffset( context ), innerShadowIm ); 86 : 0 : } 87 : : else 88 : : { 89 : 0 : painter->drawImage( imageOffset( context ) + transPt, colorisedIm ); 90 : : } 91 : 0 : } 92 : : 93 : 0 : QVariantMap QgsShadowEffect::properties() const 94 : : { 95 : 0 : QVariantMap props; 96 : 0 : props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" ); 97 : 0 : props.insert( QStringLiteral( "draw_mode" ), QString::number( int( mDrawMode ) ) ); 98 : 0 : props.insert( QStringLiteral( "blend_mode" ), QString::number( int( mBlendMode ) ) ); 99 : 0 : props.insert( QStringLiteral( "opacity" ), QString::number( mOpacity ) ); 100 : 0 : props.insert( QStringLiteral( "blur_level" ), QString::number( mBlurLevel ) ); 101 : 0 : props.insert( QStringLiteral( "blur_unit" ), QgsUnitTypes::encodeUnit( mBlurUnit ) ); 102 : 0 : props.insert( QStringLiteral( "blur_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mBlurMapUnitScale ) ); 103 : 0 : props.insert( QStringLiteral( "offset_angle" ), QString::number( mOffsetAngle ) ); 104 : 0 : props.insert( QStringLiteral( "offset_distance" ), QString::number( mOffsetDist ) ); 105 : 0 : props.insert( QStringLiteral( "offset_unit" ), QgsUnitTypes::encodeUnit( mOffsetUnit ) ); 106 : 0 : props.insert( QStringLiteral( "offset_unit_scale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale ) ); 107 : 0 : props.insert( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( mColor ) ); 108 : 0 : return props; 109 : 0 : } 110 : : 111 : 150 : void QgsShadowEffect::readProperties( const QVariantMap &props ) 112 : : { 113 : : bool ok; 114 : 300 : QPainter::CompositionMode mode = static_cast< QPainter::CompositionMode >( props.value( QStringLiteral( "blend_mode" ) ).toInt( &ok ) ); 115 : 150 : if ( ok ) 116 : : { 117 : 75 : mBlendMode = mode; 118 : 75 : } 119 : 300 : if ( props.contains( QStringLiteral( "transparency" ) ) ) 120 : : { 121 : 0 : double transparency = props.value( QStringLiteral( "transparency" ) ).toDouble( &ok ); 122 : 0 : if ( ok ) 123 : : { 124 : 0 : mOpacity = 1.0 - transparency; 125 : 0 : } 126 : 0 : } 127 : : else 128 : : { 129 : 300 : double opacity = props.value( QStringLiteral( "opacity" ) ).toDouble( &ok ); 130 : 150 : if ( ok ) 131 : : { 132 : 75 : mOpacity = opacity; 133 : 75 : } 134 : : } 135 : 450 : mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt(); 136 : 450 : mDrawMode = static_cast< QgsPaintEffect::DrawMode >( props.value( QStringLiteral( "draw_mode" ), QStringLiteral( "2" ) ).toInt() ); 137 : 300 : double level = props.value( QStringLiteral( "blur_level" ) ).toDouble( &ok ); 138 : 150 : if ( ok ) 139 : : { 140 : 75 : mBlurLevel = level; 141 : 150 : if ( !props.contains( QStringLiteral( "blur_unit" ) ) ) 142 : : { 143 : : // deal with pre blur unit era by assuming 96 dpi and converting pixel values as millimeters 144 : 0 : mBlurLevel *= 0.2645; 145 : 0 : } 146 : 75 : } 147 : 300 : mBlurUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "blur_unit" ) ).toString() ); 148 : 300 : mBlurMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "blur_unit_scale" ) ).toString() ); 149 : 300 : int angle = props.value( QStringLiteral( "offset_angle" ) ).toInt( &ok ); 150 : 150 : if ( ok ) 151 : : { 152 : 75 : mOffsetAngle = angle; 153 : 75 : } 154 : 300 : double distance = props.value( QStringLiteral( "offset_distance" ) ).toDouble( &ok ); 155 : 150 : if ( ok ) 156 : : { 157 : 75 : mOffsetDist = distance; 158 : 75 : } 159 : 300 : mOffsetUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offset_unit" ) ).toString() ); 160 : 300 : mOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offset_unit_scale" ) ).toString() ); 161 : 300 : if ( props.contains( QStringLiteral( "color" ) ) ) 162 : : { 163 : 150 : mColor = QgsSymbolLayerUtils::decodeColor( props.value( QStringLiteral( "color" ) ).toString() ); 164 : 75 : } 165 : 150 : } 166 : : 167 : 0 : QRectF QgsShadowEffect::boundingRect( const QRectF &rect, const QgsRenderContext &context ) const 168 : : { 169 : : //blur radius and offset distance 170 : 0 : int blurLevel = std::round( context.convertToPainterUnits( mBlurLevel, mBlurUnit, mBlurMapUnitScale ) ); 171 : 0 : double spread = context.convertToPainterUnits( mOffsetDist, mOffsetUnit, mOffsetMapUnitScale ); 172 : : //plus possible extension due to blur, with a couple of extra pixels thrown in for safety 173 : 0 : spread += blurLevel * 2 + 10; 174 : 0 : return rect.adjusted( -spread, -spread, spread, spread ); 175 : : } 176 : : 177 : : 178 : : // 179 : : // QgsDropShadowEffect 180 : : // 181 : : 182 : 40 : QgsPaintEffect *QgsDropShadowEffect::create( const QVariantMap &map ) 183 : : { 184 : 40 : QgsDropShadowEffect *effect = new QgsDropShadowEffect(); 185 : 40 : effect->readProperties( map ); 186 : 40 : return effect; 187 : 0 : } 188 : : 189 : 118 : QgsDropShadowEffect::QgsDropShadowEffect() 190 : 118 : : QgsShadowEffect() 191 : 236 : { 192 : : 193 : 118 : } 194 : : 195 : 0 : QString QgsDropShadowEffect::type() const 196 : : { 197 : 0 : return QStringLiteral( "dropShadow" ); 198 : : } 199 : : 200 : 0 : QgsDropShadowEffect *QgsDropShadowEffect::clone() const 201 : : { 202 : 0 : return new QgsDropShadowEffect( *this ); 203 : 0 : } 204 : : 205 : 0 : bool QgsDropShadowEffect::exteriorShadow() const 206 : : { 207 : 0 : return true; 208 : : } 209 : : 210 : : 211 : : // 212 : : // QgsInnerShadowEffect 213 : : // 214 : : 215 : 35 : QgsPaintEffect *QgsInnerShadowEffect::create( const QVariantMap &map ) 216 : : { 217 : 35 : QgsInnerShadowEffect *effect = new QgsInnerShadowEffect(); 218 : 35 : effect->readProperties( map ); 219 : 35 : return effect; 220 : 0 : } 221 : : 222 : 113 : QgsInnerShadowEffect::QgsInnerShadowEffect() 223 : 113 : : QgsShadowEffect() 224 : 226 : { 225 : : 226 : 113 : } 227 : : 228 : 0 : QString QgsInnerShadowEffect::type() const 229 : : { 230 : 0 : return QStringLiteral( "innerShadow" ); 231 : : } 232 : : 233 : 231 : QgsInnerShadowEffect *QgsInnerShadowEffect::clone() const 234 : 231 : { 235 : 0 : return new QgsInnerShadowEffect( *this ); 236 : 231 : } 237 : 231 : 238 : 231 : bool QgsInnerShadowEffect::exteriorShadow() const 239 : : { 240 : 231 : return false; 241 : : }