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