Skip to content

Commit

Permalink
[FEATURE][rasters] Invert colors' raster rendering filter option
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jun 21, 2021
1 parent cf8b96a commit 01fe6c5
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 15 deletions.
16 changes: 16 additions & 0 deletions python/core/auto_generated/raster/qgshuesaturationfilter.sip.in
Expand Up @@ -49,6 +49,22 @@ Color and saturation filter pipe for rasters.
void setSaturation( int saturation );
int saturation() const;

void setInvertColors( bool invertColors );
%Docstring
Sets whether the filter will invert colors.

:param invertColors: Set to ``True`` to invert colors.

.. versionadded:: 3.22
%End

bool invertColors() const;
%Docstring
Returns ``True`` if the filter inverts colors.

.. versionadded:: 3.22
%End

void setGrayscaleMode( QgsHueSaturationFilter::GrayscaleMode grayscaleMode );
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const;

Expand Down
33 changes: 23 additions & 10 deletions src/core/raster/qgshuesaturationfilter.cpp
Expand Up @@ -32,6 +32,7 @@ QgsHueSaturationFilter *QgsHueSaturationFilter::clone() const
{
QgsDebugMsgLevel( QStringLiteral( "Entered hue/saturation filter" ), 4 );
QgsHueSaturationFilter *filter = new QgsHueSaturationFilter( nullptr );
filter->setInvertColors( mInvertColors );
filter->setSaturation( mSaturation );
filter->setGrayscaleMode( mGrayscaleMode );
filter->setColorizeOn( mColorizeOn );
Expand Down Expand Up @@ -127,7 +128,7 @@ QgsRasterBlock *QgsHueSaturationFilter::block( int bandNo, QgsRectangle const &
return outputBlock.release();
}

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

// Get rgb for color
myColor.getRgb( &r, &g, &b );
if ( alpha != 255 )

if ( mInvertColors || alpha != 255 )
{
// Semi-transparent pixel. We need to adjust the colors since we are using Qgis::DataType::ARGB32_Premultiplied
// and color values have been premultiplied by alpha
alphaFactor = alpha / 255.;
r /= alphaFactor;
g /= alphaFactor;
b /= alphaFactor;
if ( mInvertColors )
{
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
if ( alpha != 255 )
{
// Semi-transparent pixel. We need to adjust the colors since we are using Qgis::DataType::ARGB32_Premultiplied
// and color values have been premultiplied by alpha
alphaFactor = alpha / 255.;
r /= alphaFactor;
g /= alphaFactor;
b /= alphaFactor;
}
myColor = QColor::fromRgb( r, g, b );
}

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

filterElem.setAttribute( QStringLiteral( "saturation" ), QString::number( mSaturation ) );
filterElem.setAttribute( QStringLiteral( "grayscaleMode" ), QString::number( mGrayscaleMode ) );
filterElem.setAttribute( QStringLiteral( "invertColors" ), QString::number( mInvertColors ) );
filterElem.setAttribute( QStringLiteral( "colorizeOn" ), QString::number( mColorizeOn ) );
filterElem.setAttribute( QStringLiteral( "colorizeRed" ), QString::number( mColorizeColor.red() ) );
filterElem.setAttribute( QStringLiteral( "colorizeGreen" ), QString::number( mColorizeColor.green() ) );
Expand All @@ -356,9 +368,10 @@ void QgsHueSaturationFilter::readXml( const QDomElement &filterElem )
}

setSaturation( filterElem.attribute( QStringLiteral( "saturation" ), QStringLiteral( "0" ) ).toInt() );
mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( QStringLiteral( "grayscaleMode" ), QStringLiteral( "0" ) ).toInt();
mGrayscaleMode = static_cast< QgsHueSaturationFilter::GrayscaleMode >( filterElem.attribute( QStringLiteral( "grayscaleMode" ), QStringLiteral( "0" ) ).toInt() );
mInvertColors = static_cast< bool >( filterElem.attribute( QStringLiteral( "invertColors" ), QStringLiteral( "0" ) ).toInt() );

mColorizeOn = ( bool )filterElem.attribute( QStringLiteral( "colorizeOn" ), QStringLiteral( "0" ) ).toInt();
mColorizeOn = static_cast< bool >( filterElem.attribute( QStringLiteral( "colorizeOn" ), QStringLiteral( "0" ) ).toInt() );
int mColorizeRed = filterElem.attribute( QStringLiteral( "colorizeRed" ), QStringLiteral( "255" ) ).toInt();
int mColorizeGreen = filterElem.attribute( QStringLiteral( "colorizeGreen" ), QStringLiteral( "128" ) ).toInt();
int mColorizeBlue = filterElem.attribute( QStringLiteral( "colorizeBlue" ), QStringLiteral( "128" ) ).toInt();
Expand Down
15 changes: 15 additions & 0 deletions src/core/raster/qgshuesaturationfilter.h
Expand Up @@ -56,6 +56,19 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
void setSaturation( int saturation );
int saturation() const { return mSaturation; }

/**
* Sets whether the filter will invert colors.
* \param invertColors Set to TRUE to invert colors.
* \since QGIS 3.22
*/
void setInvertColors( bool invertColors ) { mInvertColors = invertColors; }

/**
* Returns TRUE if the filter inverts colors.
* \since QGIS 3.22
*/
bool invertColors() const { return mInvertColors; }

void setGrayscaleMode( QgsHueSaturationFilter::GrayscaleMode grayscaleMode ) { mGrayscaleMode = grayscaleMode; }
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const { return mGrayscaleMode; }

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

bool mInvertColors = false;

//! Colorize settings
bool mColorizeOn = false;
QColor mColorizeColor;
Expand Down
5 changes: 5 additions & 0 deletions src/core/raster/qgsrasterlayer.cpp
Expand Up @@ -1629,6 +1629,11 @@ bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &error
rasterSymbolizerElem.appendChild( vendorOptionElem );
};

if ( hueSaturationFilter()->invertColors() )
{
vendorOptionWriter( QStringLiteral( "invertColors" ), QString::number( 1 ) );
}

// add greyScale rendering mode if set
if ( hueSaturationFilter()->grayscaleMode() != QgsHueSaturationFilter::GrayscaleOff )
{
Expand Down
3 changes: 3 additions & 0 deletions src/gui/raster/qgsrasterlayerproperties.cpp
Expand Up @@ -337,6 +337,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
}

//blend mode
Expand Down Expand Up @@ -773,6 +774,7 @@ void QgsRasterLayerProperties::sync()
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );
mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
}

/*
Expand Down Expand Up @@ -1044,6 +1046,7 @@ void QgsRasterLayerProperties::apply()
hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
}

//set the blend mode for the layer
Expand Down
4 changes: 4 additions & 0 deletions src/gui/raster/qgsrendererrasterpropertieswidget.cpp
Expand Up @@ -103,6 +103,7 @@ QgsRendererRasterPropertiesWidget::QgsRendererRasterPropertiesWidget( QgsMapLaye
connect( spinBoxSaturation, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
connect( spinColorizeStrength, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
connect( btnColorizeColor, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
connect( mInvertColorsCheck, &QAbstractButton::toggled, this, &QgsPanelWidget::widgetChanged );

connect( mBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
connect( mZoomedInResamplingComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
Expand Down Expand Up @@ -160,6 +161,7 @@ void QgsRendererRasterPropertiesWidget::apply()
hueSaturationFilter->setColorizeOn( mColorizeCheck->checkState() );
hueSaturationFilter->setColorizeColor( btnColorizeColor->color() );
hueSaturationFilter->setColorizeStrength( sliderColorizeStrength->value() );
hueSaturationFilter->setInvertColors( mInvertColorsCheck->isChecked() );
}

mResamplingUtils.refreshLayerFromWidgets();
Expand Down Expand Up @@ -219,6 +221,8 @@ void QgsRendererRasterPropertiesWidget::syncToLayer( QgsRasterLayer *layer )
btnColorizeColor->setColor( hueSaturationFilter->colorizeColor() );
toggleColorizeControls( hueSaturationFilter->colorizeOn() );
sliderColorizeStrength->setValue( hueSaturationFilter->colorizeStrength() );

mInvertColorsCheck->setChecked( hueSaturationFilter->invertColors() );
}

//blend mode
Expand Down
7 changes: 7 additions & 0 deletions src/ui/qgsrasterlayerpropertiesbase.ui
Expand Up @@ -637,6 +637,13 @@ border-radius: 2px;</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="mInvertColorsCheck">
<property name="text">
<string>Invert colors</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="labelGrayscale">
<property name="text">
Expand Down
17 changes: 12 additions & 5 deletions src/ui/qgsrendererrasterpropswidgetbase.ui
Expand Up @@ -105,7 +105,7 @@
<string>Layer Rendering</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="6" column="1" colspan="2">
<item row="7" column="1" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="mColorizeCheck">
Expand Down Expand Up @@ -175,7 +175,7 @@
</item>
</layout>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Hue</string>
Expand Down Expand Up @@ -211,7 +211,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="labelGrayscale">
<property name="text">
<string>Grayscale</string>
Expand All @@ -238,7 +238,7 @@
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<item row="6" column="1" colspan="2">
<widget class="QComboBox" name="comboGrayscale">
<item>
<property name="text">
Expand Down Expand Up @@ -345,7 +345,14 @@
</property>
</widget>
</item>
<item row="7" column="1">
<item row="5" column="1" colspan="2">
<widget class="QCheckBox" name="mInvertColorsCheck">
<property name="text">
<string>Invert colors</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QToolButton" name="mResetColorRenderingBtn">
<property name="toolTip">
<string>Reset all color rendering options to default</string>
Expand Down
31 changes: 31 additions & 0 deletions tests/src/python/test_qgsrasterlayer.py
Expand Up @@ -540,6 +540,27 @@ def testBrightnessContrastGamma(self):

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

def testInvertColors(self):
""" test raster invert colors filter"""
path = os.path.join(unitTestDataPath(),
'landsat_4326.tif')
info = QFileInfo(path)
base_name = info.baseName()
layer = QgsRasterLayer(path, base_name)
self.assertTrue(layer.isValid(), 'Raster not loaded: {}'.format(path))

layer.hueSaturationFilter().setInvertColors(True)

ms = QgsMapSettings()
ms.setLayers([layer])
ms.setExtent(layer.extent())

checker = QgsRenderChecker()
checker.setControlName("expected_raster_invertcolors")
checker.setMapSettings(ms)

self.assertTrue(checker.runTest("expected_raster_invertcolors"), "Invert colors rendering test failed")

def testPalettedColorTableToClassData(self):
entries = [QgsColorRampShader.ColorRampItem(5, QColor(255, 0, 0), 'item1'),
QgsColorRampShader.ColorRampItem(3, QColor(0, 255, 0), 'item2'),
Expand Down Expand Up @@ -1015,6 +1036,16 @@ def testWriteSld(self):
self.assertFalse(temp.isNull())
self.assertEqual(temp.text(), '0.01')

# check non default hueSaturationFilter values
hue = myRasterLayer.hueSaturationFilter()
hue.setInvertColors(True)
dom, root, errorMessage = self.layerToSld(myRasterLayer)
elements = dom.elementsByTagName('sld:RasterSymbolizer')
self.assertEqual(len(elements), 1)
element = elements.at(0).toElement()
self.assertFalse(element.isNull())
self.assertVendorOption(element, 'invertColors', '1')

# check non default hueSaturationFilter values
hue = myRasterLayer.hueSaturationFilter()
hue.setGrayscaleMode(QgsHueSaturationFilter.GrayscaleLightness)
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 01fe6c5

Please sign in to comment.