QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
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 QgsDebugError( QStringLiteral( "No input band" ) );
91 return false;
92 }
93
96 {
97 QgsDebugError( QStringLiteral( "Unknown input data type" ) );
98 return false;
99 }
100
101 mInput = input;
102 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
103 return true;
104}
105
106QgsRasterBlock *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 QgsDebugError( 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
181int 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
205void 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
220void 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:269
@ 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 int bandCount() const =0
Gets number of bands.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
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:5747
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38