QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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
19
21
22#include <QDomDocument>
23#include <QDomElement>
24
29
31{
32 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
34 filter->setBrightness( mBrightness );
35 filter->setContrast( mContrast );
36 filter->setGamma( mGamma );
37 return filter;
38}
39
41{
42 if ( mOn )
43 {
44 return 1;
45 }
46
47 if ( mInput )
48 {
49 return mInput->bandCount();
50 }
51
52 return 0;
53}
54
56{
57 if ( mOn )
58 {
60 }
61
62 if ( mInput )
63 {
64 return mInput->dataType( bandNo );
65 }
66
68}
69
71{
72 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
73
74 // Brightness filter can only work with single band ARGB32_Premultiplied
75 if ( !input )
76 {
77 QgsDebugMsgLevel( QStringLiteral( "No input" ), 4 );
78 return false;
79 }
80
81 if ( !mOn )
82 {
83 // In off mode we can connect to anything
84 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
85 mInput = input;
86 return true;
87 }
88
89 if ( input->bandCount() < 1 )
90 {
91 QgsDebugError( QStringLiteral( "No input band" ) );
92 return false;
93 }
94
95 if ( input->dataType( 1 ) != Qgis::DataType::ARGB32_Premultiplied &&
96 input->dataType( 1 ) != Qgis::DataType::ARGB32 )
97 {
98 QgsDebugError( QStringLiteral( "Unknown input data type" ) );
99 return false;
100 }
101
102 mInput = input;
103 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
104 return true;
105}
106
108{
109 Q_UNUSED( bandNo )
110 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 );
111
112 auto outputBlock = std::make_unique<QgsRasterBlock>();
113 if ( !mInput )
114 {
115 return outputBlock.release();
116 }
117
118 // At this moment we know that we read rendered image
119 int bandNumber = 1;
120 std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, extent, width, height, feedback ) );
121 if ( !inputBlock || inputBlock->isEmpty() )
122 {
123 QgsDebugError( QStringLiteral( "No raster data!" ) );
124 return outputBlock.release();
125 }
126
127 if ( mBrightness == 0 && mContrast == 0 && mGamma == 1.0 )
128 {
129 QgsDebugMsgLevel( QStringLiteral( "No brightness/contrast/gamma changes." ), 4 );
130 return inputBlock.release();
131 }
132
133 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
134 {
135 return outputBlock.release();
136 }
137
138 // adjust image
139 QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
140 QRgb myColor;
141
142 int r, g, b, alpha;
143 double f = std::pow( ( mContrast + 100 ) / 100.0, 2 );
144 double gammaCorrection = 1.0 / mGamma;
145
146 for ( qgssize i = 0; i < ( qgssize )width * height; i++ )
147 {
148 if ( inputBlock->color( i ) == myNoDataColor )
149 {
150 outputBlock->setColor( i, myNoDataColor );
151 continue;
152 }
153
154 myColor = inputBlock->color( i );
155 alpha = qAlpha( myColor );
156
157 r = adjustColorComponent( qRed( myColor ), alpha, mBrightness, f, gammaCorrection );
158 g = adjustColorComponent( qGreen( myColor ), alpha, mBrightness, f, gammaCorrection );
159 b = adjustColorComponent( qBlue( myColor ), alpha, mBrightness, f, gammaCorrection );
160
161 outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
162 }
163
164 return outputBlock.release();
165}
166
168{
169 mBrightness = std::clamp( brightness, -255, 255 );
170}
171
173{
174 mContrast = std::clamp( contrast, -100, 100 );
175}
176
178{
179 mGamma = std::clamp( gamma, 0.1, 10.0 );
180}
181
182int QgsBrightnessContrastFilter::adjustColorComponent( int colorComponent, int alpha, int brightness, double contrastFactor, double gammaCorrection ) const
183{
184 if ( alpha == 255 )
185 {
186 // Opaque pixel, do simpler math
187 return std::clamp( ( int )( 255 * std::pow( ( ( ( ( ( ( colorComponent / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255.0, gammaCorrection ) ), 0, 255 );
188 }
189 else if ( alpha == 0 )
190 {
191 // Totally transparent pixel
192 return 0;
193 }
194 else
195 {
196 // Semi-transparent pixel. We need to adjust the math since we are using Qgis::DataType::ARGB32_Premultiplied
197 // and color values have been premultiplied by alpha
198 double alphaFactor = alpha / 255.;
199 double adjustedColor = colorComponent / alphaFactor;
200
201 // Make sure to return a premultiplied color
202 return alphaFactor * std::clamp( 255 * std::pow( ( ( ( ( ( ( adjustedColor / 255.0 ) - 0.5 ) * contrastFactor ) + 0.5 ) * 255 ) + brightness ) / 255, gammaCorrection ), 0., 255. );
203 }
204}
205
206void QgsBrightnessContrastFilter::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
207{
208 if ( parentElem.isNull() )
209 {
210 return;
211 }
212
213 QDomElement filterElem = doc.createElement( QStringLiteral( "brightnesscontrast" ) );
214
215 filterElem.setAttribute( QStringLiteral( "brightness" ), QString::number( mBrightness ) );
216 filterElem.setAttribute( QStringLiteral( "contrast" ), QString::number( mContrast ) );
217 filterElem.setAttribute( QStringLiteral( "gamma" ), QString::number( mGamma ) );
218 parentElem.appendChild( filterElem );
219}
220
221void QgsBrightnessContrastFilter::readXml( const QDomElement &filterElem )
222{
223 if ( filterElem.isNull() )
224 {
225 return;
226 }
227
228 mBrightness = filterElem.attribute( QStringLiteral( "brightness" ), QStringLiteral( "0" ) ).toInt();
229 mContrast = filterElem.attribute( QStringLiteral( "contrast" ), QStringLiteral( "0" ) ).toInt();
230 mGamma = filterElem.attribute( QStringLiteral( "gamma" ), QStringLiteral( "1" ) ).toDouble();
231}
DataType
Raster data types.
Definition qgis.h:372
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:387
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:373
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:386
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.
QgsRasterInterface(QgsRasterInterface *input=nullptr)
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
A rectangle specified with double values.
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:7142
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57