QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsbrightnesscontrastfilter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsbrightnesscontrastfilter.cpp
3  ---------------------
4  begin : February 2013
5  copyright : (C) 2013 by Alexander Bruy
6  email : alexander dot bruy 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 "qgsrasterdataprovider.h"
20 
21 #include <QDomDocument>
22 #include <QDomElement>
23 
25  : QgsRasterInterface( input )
26 {
27 }
28 
30 {
31  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
33  filter->setBrightness( mBrightness );
34  filter->setContrast( mContrast );
35  filter->setGamma( mGamma );
36  return filter;
37 }
38 
40 {
41  if ( mOn )
42  {
43  return 1;
44  }
45 
46  if ( mInput )
47  {
48  return mInput->bandCount();
49  }
50 
51  return 0;
52 }
53 
55 {
56  if ( mOn )
57  {
59  }
60 
61  if ( mInput )
62  {
63  return mInput->dataType( bandNo );
64  }
65 
67 }
68 
70 {
71  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
72 
73  // Brightness filter can only work with single band ARGB32_Premultiplied
74  if ( !input )
75  {
76  QgsDebugMsgLevel( QStringLiteral( "No input" ), 4 );
77  return false;
78  }
79 
80  if ( !mOn )
81  {
82  // In off mode we can connect to anything
83  QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
84  mInput = input;
85  return true;
86  }
87 
88  if ( input->bandCount() < 1 )
89  {
90  QgsDebugMsg( QStringLiteral( "No input band" ) );
91  return false;
92  }
93 
96  {
97  QgsDebugMsg( QStringLiteral( "Unknown input data type" ) );
98  return false;
99  }
100 
101  mInput = input;
102  QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
103  return true;
104 }
105 
106 QgsRasterBlock *QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
107 {
108  Q_UNUSED( bandNo )
109  QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 );
110 
111  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
112  if ( !mInput )
113  {
114  return outputBlock.release();
115  }
116 
117  // At this moment we know that we read rendered image
118  int bandNumber = 1;
119  std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, extent, width, height, feedback ) );
120  if ( !inputBlock || inputBlock->isEmpty() )
121  {
122  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
123  return outputBlock.release();
124  }
125 
126  if ( mBrightness == 0 && mContrast == 0 && mGamma == 1.0 )
127  {
128  QgsDebugMsgLevel( QStringLiteral( "No brightness/contrast/gamma changes." ), 4 );
129  return inputBlock.release();
130  }
131 
132  if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
133  {
134  return outputBlock.release();
135  }
136 
137  // adjust image
138  QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
139  QRgb myColor;
140 
141  int r, g, b, alpha;
142  double f = std::pow( ( mContrast + 100 ) / 100.0, 2 );
143  double gammaCorrection = 1.0 / mGamma;
144 
145  for ( qgssize i = 0; i < ( qgssize )width * height; i++ )
146  {
147  if ( inputBlock->color( i ) == myNoDataColor )
148  {
149  outputBlock->setColor( i, myNoDataColor );
150  continue;
151  }
152 
153  myColor = inputBlock->color( i );
154  alpha = qAlpha( myColor );
155 
156  r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f, gammaCorrection );
157  g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f, gammaCorrection );
158  b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f, gammaCorrection );
159 
160  outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
161  }
162 
163  return outputBlock.release();
164 }
165 
167 {
168  mBrightness = std::clamp( brightness, -255, 255 );
169 }
170 
172 {
173  mContrast = std::clamp( contrast, -100, 100 );
174 }
175 
177 {
178  mGamma = std::clamp( gamma, 0.1, 10.0 );
179 }
180 
181 int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor, double gammaCorrection ) const
182 {
183  if ( alpha == 255 )
184  {
185  // Opaque pixel, do simpler math
186  return std::clamp( ( int )( 255 * std::pow( ( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255.0, gammaCorrection ) ), 0, 255 );
187  }
188  else if ( alpha == 0 )
189  {
190  // Totally transparent pixel
191  return 0;
192  }
193  else
194  {
195  // Semi-transparent pixel. We need to adjust the math since we are using Qgis::DataType::ARGB32_Premultiplied
196  // and color values have been premultiplied by alpha
197  double alphaFactor = alpha / 255.;
198  double adjustedColor = colorComponent / alphaFactor;
199 
200  // Make sure to return a premultiplied color
201  return alphaFactor * std::clamp( 255 * std::pow( ( ( ( ( ( ( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255, gammaCorrection ), 0., 255. );
202  }
203 }
204 
205 void QgsBrightnessContrastFilter::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
206 {
207  if ( parentElem.isNull() )
208  {
209  return;
210  }
211 
212  QDomElement filterElem = doc.createElement( QStringLiteral( "brightnesscontrast" ) );
213 
214  filterElem.setAttribute( QStringLiteral( "brightness" ), QString::number( mBrightness ) );
215  filterElem.setAttribute( QStringLiteral( "contrast" ), QString::number( mContrast ) );
216  filterElem.setAttribute( QStringLiteral( "gamma" ), QString::number( mGamma ) );
217  parentElem.appendChild( filterElem );
218 }
219 
220 void QgsBrightnessContrastFilter::readXml( const QDomElement &filterElem )
221 {
222  if ( filterElem.isNull() )
223  {
224  return;
225  }
226 
227  mBrightness = filterElem.attribute( QStringLiteral( "brightness" ), QStringLiteral( "0" ) ).toInt();
228  mContrast = filterElem.attribute( QStringLiteral( "contrast" ), QStringLiteral( "0" ) ).toInt();
229  mGamma = filterElem.attribute( QStringLiteral( "gamma" ), QStringLiteral( "1" ) ).toDouble();
230 }
QgsBrightnessContrastFilter::bandCount
int bandCount() const override
Gets number of bands.
Definition: qgsbrightnesscontrastfilter.cpp:39
QgsRasterInterface::mInput
QgsRasterInterface * mInput
Definition: qgsrasterinterface.h:500
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsBrightnessContrastFilter::block
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
Definition: qgsbrightnesscontrastfilter.cpp:106
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:128
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsBrightnessContrastFilter::readXml
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Definition: qgsbrightnesscontrastfilter.cpp:220
QgsBrightnessContrastFilter::setInput
bool setInput(QgsRasterInterface *input) override
Set input.
Definition: qgsbrightnesscontrastfilter.cpp:69
QgsBrightnessContrastFilter::brightness
int brightness() const
Returns current brightness level.
Definition: qgsbrightnesscontrastfilter.h:73
QgsBrightnessContrastFilter::clone
QgsBrightnessContrastFilter * clone() const override
Clone itself, create deep copy.
Definition: qgsbrightnesscontrastfilter.cpp:29
QgsRasterInterface::dataType
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
Qgis::DataType::UnknownDataType
@ UnknownDataType
Unknown or unspecified type.
Qgis::DataType::ARGB32_Premultiplied
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
QgsBrightnessContrastFilter::contrast
int contrast() const
Returns current contrast level.
Definition: qgsbrightnesscontrastfilter.h:85
qgsbrightnesscontrastfilter.h
Qgis::DataType::ARGB32
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
QgsRasterInterface::mOn
bool mOn
Definition: qgsrasterinterface.h:509
QgsRasterInterface
Base class for processing filters like renderers, reprojector, resampler etc.
Definition: qgsrasterinterface.h:135
QgsBrightnessContrastFilter::dataType
Qgis::DataType dataType(int bandNo) const override
Returns data type for the band specified by number.
Definition: qgsbrightnesscontrastfilter.cpp:54
QgsBrightnessContrastFilter
Brightness/contrast and gamma correction filter pipe for rasters.
Definition: qgsbrightnesscontrastfilter.h:31
QgsBrightnessContrastFilter::writeXml
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
Definition: qgsbrightnesscontrastfilter.cpp:205
QgsRasterInterface::input
virtual QgsRasterInterface * input() const
Current input.
Definition: qgsrasterinterface.h:302
QgsRasterBlockFeedback
Feedback object tailored for raster block reading.
Definition: qgsrasterinterface.h:41
QgsBrightnessContrastFilter::QgsBrightnessContrastFilter
QgsBrightnessContrastFilter(QgsRasterInterface *input=nullptr)
Definition: qgsbrightnesscontrastfilter.cpp:24
QgsBrightnessContrastFilter::setContrast
void setContrast(int contrast)
Set contrast level.
Definition: qgsbrightnesscontrastfilter.cpp:171
QgsBrightnessContrastFilter::setBrightness
void setBrightness(int brightness)
Set brightness level.
Definition: qgsbrightnesscontrastfilter.cpp:166
QgsRasterInterface::bandCount
virtual int bandCount() const =0
Gets number of bands.
QgsRasterInterface::block
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
QgsBrightnessContrastFilter::setGamma
void setGamma(double gamma)
Set gamma value.
Definition: qgsbrightnesscontrastfilter.cpp:176
QgsBrightnessContrastFilter::gamma
double gamma() const
Returns current gamma value.
Definition: qgsbrightnesscontrastfilter.h:101
QgsRasterBlock
Raster data container.
Definition: qgsrasterblock.h:36
QgsRasterInterface::extent
virtual QgsRectangle extent() const
Gets the extent of the interface.
Definition: qgsrasterinterface.h:248
qgsrasterdataprovider.h
qgssize
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:2791