QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 }
DataType
Raster data types.
Definition: qgis.h:120
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Brightness/contrast and gamma correction filter pipe for rasters.
int contrast() const
Returns current contrast level.
int bandCount() const override
Gets number of bands.
QgsBrightnessContrastFilter * clone() const override
Clone itself, create deep copy.
bool setInput(QgsRasterInterface *input) override
Set input.
QgsBrightnessContrastFilter(QgsRasterInterface *input=nullptr)
int brightness() const
Returns current brightness level.
double gamma() const
Returns current gamma value.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
void setGamma(double gamma)
Set gamma value.
void setContrast(int contrast)
Set contrast level.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
void setBrightness(int brightness)
Set brightness level.
Qgis::DataType dataType(int bandNo) const override
Returns data type for the band specified by number.
Feedback object tailored for raster block reading.
Raster data container.
Base class for processing filters like renderers, reprojector, resampler etc.
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.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
virtual QgsRasterInterface * input() const
Current input.
virtual int bandCount() const =0
Gets number of bands.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
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:1713
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38