QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
66  return Qgis::UnknownDataType;
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 
95  input->dataType( 1 ) != Qgis::ARGB32 )
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::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 
166 int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor, double gammaCorrection ) const
167 {
168  if ( alpha == 255 )
169  {
170  // Opaque pixel, do simpler math
171  return qBound( 0, ( int )( 255 * std::pow( ( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255.0, gammaCorrection ) ), 255 );
172  }
173  else if ( alpha == 0 )
174  {
175  // Totally transparent pixel
176  return 0;
177  }
178  else
179  {
180  // Semi-transparent pixel. We need to adjust the math since we are using Qgis::ARGB32_Premultiplied
181  // and color values have been premultiplied by alpha
182  double alphaFactor = alpha / 255.;
183  double adjustedColor = colorComponent / alphaFactor;
184 
185  // Make sure to return a premultiplied color
186  return alphaFactor * qBound( 0., 255 * std::pow( ( ( ( ( ( ( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255, gammaCorrection ), 255. );
187  }
188 }
189 
190 void QgsBrightnessContrastFilter::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
191 {
192  if ( parentElem.isNull() )
193  {
194  return;
195  }
196 
197  QDomElement filterElem = doc.createElement( QStringLiteral( "brightnesscontrast" ) );
198 
199  filterElem.setAttribute( QStringLiteral( "brightness" ), QString::number( mBrightness ) );
200  filterElem.setAttribute( QStringLiteral( "contrast" ), QString::number( mContrast ) );
201  filterElem.setAttribute( QStringLiteral( "gamma" ), QString::number( mGamma ) );
202  parentElem.appendChild( filterElem );
203 }
204 
205 void QgsBrightnessContrastFilter::readXml( const QDomElement &filterElem )
206 {
207  if ( filterElem.isNull() )
208  {
209  return;
210  }
211 
212  mBrightness = filterElem.attribute( QStringLiteral( "brightness" ), QStringLiteral( "0" ) ).toInt();
213  mContrast = filterElem.attribute( QStringLiteral( "contrast" ), QStringLiteral( "0" ) ).toInt();
214  mGamma = filterElem.attribute( QStringLiteral( "gamma" ), QStringLiteral( "1" ) ).toDouble();
215 }
DataType
Raster data types.
Definition: qgis.h:102
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:116
@ UnknownDataType
Unknown or unspecified type.
Definition: qgis.h:103
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:115
Brightness/contrast and gamma correction filter pipe for rasters.
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.
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:769
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38