Skip to content

Commit 01fe6c5

Browse files
committedJun 21, 2021
[FEATURE][rasters] Invert colors' raster rendering filter option
1 parent cf8b96a commit 01fe6c5

File tree

10 files changed

+116
-15
lines changed

10 files changed

+116
-15
lines changed
 

‎python/core/auto_generated/raster/qgshuesaturationfilter.sip.in

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ Color and saturation filter pipe for rasters.
4949
void setSaturation( int saturation );
5050
int saturation() const;
5151

52+
void setInvertColors( bool invertColors );
53+
%Docstring
54+
Sets whether the filter will invert colors.
55+
56+
:param invertColors: Set to ``True`` to invert colors.
57+
58+
.. versionadded:: 3.22
59+
%End
60+
61+
bool invertColors() const;
62+
%Docstring
63+
Returns ``True`` if the filter inverts colors.
64+
65+
.. versionadded:: 3.22
66+
%End
67+
5268
void setGrayscaleMode( QgsHueSaturationFilter::GrayscaleMode grayscaleMode );
5369
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const;
5470

‎src/core/raster/qgshuesaturationfilter.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ QgsHueSaturationFilter *QgsHueSaturationFilter::clone() const
3232
{
3333
QgsDebugMsgLevel( QStringLiteral( "Entered hue/saturation filter" ), 4 );
3434
QgsHueSaturationFilter *filter = new QgsHueSaturationFilter( nullptr );
35+
filter->setInvertColors( mInvertColors );
3536
filter->setSaturation( mSaturation );
3637
filter->setGrayscaleMode( mGrayscaleMode );
3738
filter->setColorizeOn( mColorizeOn );
@@ -127,7 +128,7 @@ QgsRasterBlock *QgsHueSaturationFilter::block( int bandNo, QgsRectangle const &
127128
return outputBlock.release();
128129
}
129130

130-
if ( mSaturation == 0 && mGrayscaleMode == GrayscaleOff && !mColorizeOn )
131+
if ( !mInvertColors && mSaturation == 0 && mGrayscaleMode == GrayscaleOff && !mColorizeOn )
131132
{
132133
QgsDebugMsgLevel( QStringLiteral( "No hue/saturation change." ), 4 );
133134
return inputBlock.release();
@@ -169,14 +170,24 @@ QgsRasterBlock *QgsHueSaturationFilter::block( int bandNo, QgsRectangle const &
169170

170171
// Get rgb for color
171172
myColor.getRgb( &r, &g, &b );
172-
if ( alpha != 255 )
173+
174+
if ( mInvertColors || alpha != 255 )
173175
{
174-
// Semi-transparent pixel. We need to adjust the colors since we are using Qgis::DataType::ARGB32_Premultiplied
175-
// and color values have been premultiplied by alpha
176-
alphaFactor = alpha / 255.;
177-
r /= alphaFactor;
178-
g /= alphaFactor;
179-
b /= alphaFactor;
176+
if ( mInvertColors )
177+
{
178+
r = 255 - r;
179+
g = 255 - g;
180+
b = 255 - b;
181+
}
182+
if ( alpha != 255 )
183+
{
184+
// Semi-transparent pixel. We need to adjust the colors since we are using Qgis::DataType::ARGB32_Premultiplied
185+
// and color values have been premultiplied by alpha
186+
alphaFactor = alpha / 255.;
187+
r /= alphaFactor;
188+
g /= alphaFactor;
189+
b /= alphaFactor;
190+
}
180191
myColor = QColor::fromRgb( r, g, b );
181192
}
182193

@@ -339,6 +350,7 @@ void QgsHueSaturationFilter::writeXml( QDomDocument &doc, QDomElement &parentEle
339350

340351
filterElem.setAttribute( QStringLiteral( "saturation" ), QString::number( mSaturation ) );
341352
filterElem.setAttribute( QStringLiteral( "grayscaleMode" ), QString::number( mGrayscaleMode ) );
353+
filterElem.setAttribute( QStringLiteral( "invertColors" ), QString::number( mInvertColors ) );
342354
filterElem.setAttribute( QStringLiteral( "colorizeOn" ), QString::number( mColorizeOn ) );
343355
filterElem.setAttribute( QStringLiteral( "colorizeRed" ), QString::number( mColorizeColor.red() ) );
344356
filterElem.setAttribute( QStringLiteral( "colorizeGreen" ), QString::number( mColorizeColor.green() ) );
@@ -356,9 +368,10 @@ void QgsHueSaturationFilter::readXml( const QDomElement &filterElem )
356368
}
357369

358370
setSaturation( filterElem.attribute( QStringLiteral( "saturation" ), QStringLiteral( "0" ) ).toInt() );
359-
mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( QStringLiteral( "grayscaleMode" ), QStringLiteral( "0" ) ).toInt();
371+
mGrayscaleMode = static_cast< QgsHueSaturationFilter::GrayscaleMode >( filterElem.attribute( QStringLiteral( "grayscaleMode" ), QStringLiteral( "0" ) ).toInt() );
372+
mInvertColors = static_cast< bool >( filterElem.attribute( QStringLiteral( "invertColors" ), QStringLiteral( "0" ) ).toInt() );
360373

361-
mColorizeOn = ( bool )filterElem.attribute( QStringLiteral( "colorizeOn" ), QStringLiteral( "0" ) ).toInt();
374+
mColorizeOn = static_cast< bool >( filterElem.attribute( QStringLiteral( "colorizeOn" ), QStringLiteral( "0" ) ).toInt() );
362375
int mColorizeRed = filterElem.attribute( QStringLiteral( "colorizeRed" ), QStringLiteral( "255" ) ).toInt();
363376
int mColorizeGreen = filterElem.attribute( QStringLiteral( "colorizeGreen" ), QStringLiteral( "128" ) ).toInt();
364377
int mColorizeBlue = filterElem.attribute( QStringLiteral( "colorizeBlue" ), QStringLiteral( "128" ) ).toInt();

‎src/core/raster/qgshuesaturationfilter.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
5656
void setSaturation( int saturation );
5757
int saturation() const { return mSaturation; }
5858

59+
/**
60+
* Sets whether the filter will invert colors.
61+
* \param invertColors Set to TRUE to invert colors.
62+
* \since QGIS 3.22
63+
*/
64+
void setInvertColors( bool invertColors ) { mInvertColors = invertColors; }
65+
66+
/**
67+
* Returns TRUE if the filter inverts colors.
68+
* \since QGIS 3.22
69+
*/
70+
bool invertColors() const { return mInvertColors; }
71+
5972
void setGrayscaleMode( QgsHueSaturationFilter::GrayscaleMode grayscaleMode ) { mGrayscaleMode = grayscaleMode; }
6073
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const { return mGrayscaleMode; }
6174

@@ -84,6 +97,8 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
8497
//! Current grayscale mode
8598
QgsHueSaturationFilter::GrayscaleMode mGrayscaleMode = QgsHueSaturationFilter::GrayscaleOff;
8699

100+
bool mInvertColors = false;
101+
87102
//! Colorize settings
88103
bool mColorizeOn = false;
89104
QColor mColorizeColor;

‎src/core/raster/qgsrasterlayer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,11 @@ bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &error
16291629
rasterSymbolizerElem.appendChild( vendorOptionElem );
16301630
};
16311631

1632+
if ( hueSaturationFilter()->invertColors() )
1633+
{
1634+
vendorOptionWriter( QStringLiteral( "invertColors" ), QString::number( 1 ) );
1635+
}
1636+
16321637
// add greyScale rendering mode if set
16331638
if ( hueSaturationFilter()->grayscaleMode() != QgsHueSaturationFilter::GrayscaleOff )
16341639
{

‎src/gui/raster/qgsrasterlayerproperties.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
337337
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
338338
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
339339
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
340+
mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
340341
}
341342

342343
//blend mode
@@ -773,6 +774,7 @@ void QgsRasterLayerProperties::sync()
773774
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
774775
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
775776
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
777+
mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
776778
}
777779

778780
/*
@@ -1044,6 +1046,7 @@ void QgsRasterLayerProperties::apply()
10441046
hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
10451047
hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
10461048
hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
1049+
hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
10471050
}
10481051

10491052
//set the blend mode for the layer

‎src/gui/raster/qgsrendererrasterpropertieswidget.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ QgsRendererRasterPropertiesWidget::QgsRendererRasterPropertiesWidget( QgsMapLaye
103103
connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
104104
connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
105105
connect( btnColorizeColor, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
106+
connect( mInvertColorsCheck, &QAbstractButton::toggled, this, &QgsPanelWidget::widgetChanged );
106107

107108
connect( mBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
108109
connect( mZoomedInResamplingComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
@@ -160,6 +161,7 @@ void QgsRendererRasterPropertiesWidget::apply()
160161
hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
161162
hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
162163
hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
164+
hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
163165
}
164166

165167
mResamplingUtils.refreshLayerFromWidgets();
@@ -219,6 +221,8 @@ void QgsRendererRasterPropertiesWidget::syncToLayer( QgsRasterLayer *layer )
219221
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
220222
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
221223
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
224+
225+
mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
222226
}
223227

224228
//blend mode

‎src/ui/qgsrasterlayerpropertiesbase.ui

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,13 @@ border-radius: 2px;</string>
637637
</property>
638638
</widget>
639639
</item>
640+
<item row="3" column="0" colspan="3">
641+
<widget class="QCheckBox" name="mInvertColorsCheck">
642+
<property name="text">
643+
<string>Invert colors</string>
644+
</property>
645+
</widget>
646+
</item>
640647
<item row="3" column="4">
641648
<widget class="QLabel" name="labelGrayscale">
642649
<property name="text">

‎src/ui/qgsrendererrasterpropswidgetbase.ui

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
<string>Layer Rendering</string>
106106
</property>
107107
<layout class="QGridLayout" name="gridLayout_3">
108-
<item row="6" column="1" colspan="2">
108+
<item row="7" column="1" colspan="2">
109109
<layout class="QGridLayout" name="gridLayout">
110110
<item row="0" column="0" colspan="2">
111111
<widget class="QCheckBox" name="mColorizeCheck">
@@ -175,7 +175,7 @@
175175
</item>
176176
</layout>
177177
</item>
178-
<item row="6" column="0">
178+
<item row="7" column="0">
179179
<widget class="QLabel" name="label_3">
180180
<property name="text">
181181
<string>Hue</string>
@@ -211,7 +211,7 @@
211211
</property>
212212
</widget>
213213
</item>
214-
<item row="5" column="0">
214+
<item row="6" column="0">
215215
<widget class="QLabel" name="labelGrayscale">
216216
<property name="text">
217217
<string>Grayscale</string>
@@ -238,7 +238,7 @@
238238
</property>
239239
</widget>
240240
</item>
241-
<item row="5" column="1" colspan="2">
241+
<item row="6" column="1" colspan="2">
242242
<widget class="QComboBox" name="comboGrayscale">
243243
<item>
244244
<property name="text">
@@ -345,7 +345,14 @@
345345
</property>
346346
</widget>
347347
</item>
348-
<item row="7" column="1">
348+
<item row="5" column="1" colspan="2">
349+
<widget class="QCheckBox" name="mInvertColorsCheck">
350+
<property name="text">
351+
<string>Invert colors</string>
352+
</property>
353+
</widget>
354+
</item>
355+
<item row="8" column="1">
349356
<widget class="QToolButton" name="mResetColorRenderingBtn">
350357
<property name="toolTip">
351358
<string>Reset all color rendering options to default</string>

‎tests/src/python/test_qgsrasterlayer.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,27 @@ def testBrightnessContrastGamma(self):
540540

541541
self.assertTrue(checker.runTest("expected_raster_gamma222"), "Gamma correction (gamma = 2.22) rendering test failed")
542542

543+
def testInvertColors(self):
544+
""" test raster invert colors filter"""
545+
path = os.path.join(unitTestDataPath(),
546+
'landsat_4326.tif')
547+
info = QFileInfo(path)
548+
base_name = info.baseName()
549+
layer = QgsRasterLayer(path, base_name)
550+
self.assertTrue(layer.isValid(), 'Raster not loaded: {}'.format(path))
551+
552+
layer.hueSaturationFilter().setInvertColors(True)
553+
554+
ms = QgsMapSettings()
555+
ms.setLayers([layer])
556+
ms.setExtent(layer.extent())
557+
558+
checker = QgsRenderChecker()
559+
checker.setControlName("expected_raster_invertcolors")
560+
checker.setMapSettings(ms)
561+
562+
self.assertTrue(checker.runTest("expected_raster_invertcolors"), "Invert colors rendering test failed")
563+
543564
def testPalettedColorTableToClassData(self):
544565
entries = [QgsColorRampShader.ColorRampItem(5, QColor(255, 0, 0), 'item1'),
545566
QgsColorRampShader.ColorRampItem(3, QColor(0, 255, 0), 'item2'),
@@ -1015,6 +1036,16 @@ def testWriteSld(self):
10151036
self.assertFalse(temp.isNull())
10161037
self.assertEqual(temp.text(), '0.01')
10171038

1039+
# check non default hueSaturationFilter values
1040+
hue = myRasterLayer.hueSaturationFilter()
1041+
hue.setInvertColors(True)
1042+
dom, root, errorMessage = self.layerToSld(myRasterLayer)
1043+
elements = dom.elementsByTagName('sld:RasterSymbolizer')
1044+
self.assertEqual(len(elements), 1)
1045+
element = elements.at(0).toElement()
1046+
self.assertFalse(element.isNull())
1047+
self.assertVendorOption(element, 'invertColors', '1')
1048+
10181049
# check non default hueSaturationFilter values
10191050
hue = myRasterLayer.hueSaturationFilter()
10201051
hue.setGrayscaleMode(QgsHueSaturationFilter.GrayscaleLightness)
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.