QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgshuesaturationfilter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgshuesaturationfilter.cpp
3  ---------------------
4  begin : February 2013
5  copyright : (C) 2013 by Alexander Bruy, Nyall Dawson
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"
19 #include "qgshuesaturationfilter.h"
20 
21 #include <QDomDocument>
22 #include <QDomElement>
23 
24 
26  : QgsRasterInterface( input ),
27  mSaturation( 0 ),
28  mGrayscaleMode( QgsHueSaturationFilter::GrayscaleOff ),
29  mColorizeOn( false ),
30  mColorizeColor( QColor::fromRgb( 255, 128, 128 ) ),
31  mColorizeStrength( 100 )
32 {
33 }
34 
36 {
37 }
38 
40 {
41  QgsDebugMsg( "Entered hue/saturation filter" );
43  filter->setSaturation( mSaturation );
44  filter->setGrayscaleMode( mGrayscaleMode );
45  filter->setColorizeOn( mColorizeOn );
46  filter->setColorizeColor( mColorizeColor );
47  filter->setColorizeStrength( mColorizeStrength );
48  return filter;
49 }
50 
52 {
53  if ( mOn )
54  {
55  return 1;
56  }
57 
58  if ( mInput )
59  {
60  return mInput->bandCount();
61  }
62 
63  return 0;
64 }
65 
67 {
68  if ( mOn )
69  {
71  }
72 
73  if ( mInput )
74  {
75  return mInput->dataType( bandNo );
76  }
77 
78  return QGis::UnknownDataType;
79 }
80 
82 {
83  QgsDebugMsg( "Entered" );
84 
85  // Hue/saturation filter can only work with single band ARGB32_Premultiplied
86  if ( !input )
87  {
88  QgsDebugMsg( "No input" );
89  return false;
90  }
91 
92  if ( !mOn )
93  {
94  // In off mode we can connect to anything
95  QgsDebugMsg( "OK" );
96  mInput = input;
97  return true;
98  }
99 
100  if ( input->bandCount() < 1 )
101  {
102  QgsDebugMsg( "No input band" );
103  return false;
104  }
105 
106  if ( input->dataType( 1 ) != QGis::ARGB32_Premultiplied &&
107  input->dataType( 1 ) != QGis::ARGB32 )
108  {
109  QgsDebugMsg( "Unknown input data type" );
110  return false;
111  }
112 
113  mInput = input;
114  QgsDebugMsg( "OK" );
115  return true;
116 }
117 
118 QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
119 {
120  Q_UNUSED( bandNo );
121  QgsDebugMsg( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ) );
122 
123  QgsRasterBlock *outputBlock = new QgsRasterBlock();
124  if ( !mInput )
125  {
126  return outputBlock;
127  }
128 
129  // At this moment we know that we read rendered image
130  int bandNumber = 1;
131  QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height );
132  if ( !inputBlock || inputBlock->isEmpty() )
133  {
134  QgsDebugMsg( "No raster data!" );
135  delete inputBlock;
136  return outputBlock;
137  }
138 
139  if ( mSaturation == 0 && mGrayscaleMode == GrayscaleOff && !mColorizeOn )
140  {
141  QgsDebugMsg( "No hue/saturation change." );
142  delete outputBlock;
143  return inputBlock;
144  }
145 
146  if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) )
147  {
148  delete inputBlock;
149  return outputBlock;
150  }
151 
152  // adjust image
153  QRgb myNoDataColor = qRgba( 0, 0, 0, 0 );
154  QRgb myRgb;
155  QColor myColor;
156  int h, s, l;
157  int r, g, b, alpha;
158  double alphaFactor = 1.0;
159 
160  for ( qgssize i = 0; i < ( qgssize )width*height; i++ )
161  {
162  if ( inputBlock->color( i ) == myNoDataColor )
163  {
164  outputBlock->setColor( i, myNoDataColor );
165  continue;
166  }
167 
168  myRgb = inputBlock->color( i );
169  myColor = QColor( myRgb );
170 
171  // Alpha must be taken from QRgb, since conversion from QRgb->QColor loses alpha
172  alpha = qAlpha( myRgb );
173 
174  if ( alpha == 0 )
175  {
176  // totally transparent, no changes required
177  outputBlock->setColor( i, myRgb );
178  continue;
179  }
180 
181  // Get rgb for color
182  myColor.getRgb( &r, &g, &b );
183  if ( alpha != 255 )
184  {
185  // Semi-transparent pixel. We need to adjust the colors since we are using QGis::ARGB32_Premultiplied
186  // and color values have been premultiplied by alpha
187  alphaFactor = alpha / 255.;
188  r /= alphaFactor;
189  g /= alphaFactor;
190  b /= alphaFactor;
191  myColor = QColor::fromRgb( r, g, b );
192  }
193 
194  myColor.getHsl( &h, &s, &l );
195 
196  // Changing saturation?
197  if (( mGrayscaleMode != GrayscaleOff ) || ( mSaturationScale != 1 ) )
198  {
199  processSaturation( r, g, b, h, s, l );
200  }
201 
202  // Colorizing?
203  if ( mColorizeOn )
204  {
205  processColorization( r, g, b, h, s, l );
206  }
207 
208  // Convert back to rgb
209  if ( alpha != 255 )
210  {
211  // Transparent pixel, need to premultiply color components
212  r *= alphaFactor;
213  g *= alphaFactor;
214  b *= alphaFactor;
215  }
216 
217  outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
218  }
219 
220  delete inputBlock;
221  return outputBlock;
222 }
223 
224 // Process a colorization and update resultant HSL & RGB values
225 void QgsHueSaturationFilter::processColorization( int &r, int &g, int &b, int &h, int &s, int &l )
226 {
227  QColor myColor;
228 
229  // Overwrite hue and saturation with values from colorize color
230  h = mColorizeH;
231  s = mColorizeS;
232 
233 
234  QColor colorizedColor = QColor::fromHsl( h, s, l );
235 
236  if ( mColorizeStrength == 100 )
237  {
238  // Full strength
239  myColor = colorizedColor;
240 
241  // RGB may have changed, update them
242  myColor.getRgb( &r, &g, &b );
243  }
244  else
245  {
246  // Get rgb for colorized color
247  int colorizedR, colorizedG, colorizedB;
248  colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
249 
250  // Now, linearly scale by colorize strength
251  double p = ( double ) mColorizeStrength / 100.;
252  r = p * colorizedR + ( 1 - p ) * r;
253  g = p * colorizedG + ( 1 - p ) * g;
254  b = p * colorizedB + ( 1 - p ) * b;
255 
256  // RGB changed, so update HSL values
257  myColor = QColor::fromRgb( r, g, b );
258  myColor.getHsl( &h, &s, &l );
259  }
260 }
261 
262 // Process a change in saturation and update resultant HSL & RGB values
263 void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, int &s, int &l )
264 {
265 
266  QColor myColor;
267 
268  // Are we converting layer to grayscale?
269  switch ( mGrayscaleMode )
270  {
271  case GrayscaleLightness:
272  {
273  // Lightness mode, set saturation to zero
274  s = 0;
275 
276  // Saturation changed, so update rgb values
277  myColor = QColor::fromHsl( h, s, l );
278  myColor.getRgb( &r, &g, &b );
279  return;
280  }
281  case GrayscaleLuminosity:
282  {
283  // Grayscale by weighted rgb components
284  int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
285  r = g = b = luminosity;
286 
287  // RGB changed, so update HSL values
288  myColor = QColor::fromRgb( r, g, b );
289  myColor.getHsl( &h, &s, &l );
290  return;
291  }
292  case GrayscaleAverage:
293  {
294  // Grayscale by average of rgb components
295  int average = ( r + g + b ) / 3;
296  r = g = b = average;
297 
298  // RGB changed, so update HSL values
299  myColor = QColor::fromRgb( r, g, b );
300  myColor.getHsl( &h, &s, &l );
301  return;
302  }
303  case GrayscaleOff:
304  {
305  // Not being made grayscale, do saturation change
306  if ( mSaturationScale < 1 )
307  {
308  // Lowering the saturation. Use a simple linear relationship
309  s = qMin(( int )( s * mSaturationScale ), 255 );
310  }
311  else
312  {
313  // Raising the saturation. Use a saturation curve to prevent
314  // clipping at maximum saturation with ugly results.
315  s = qMin(( int )( 255. * ( 1 - pow( 1 - ( s / 255. ), pow( mSaturationScale, 2 ) ) ) ), 255 );
316  }
317 
318  // Saturation changed, so update rgb values
319  myColor = QColor::fromHsl( h, s, l );
320  myColor.getRgb( &r, &g, &b );
321  return;
322  }
323  }
324 }
325 
327 {
328  mSaturation = qBound( -100, saturation, 100 );
329 
330  // Scale saturation value to [0-2], where 0 = desaturated
331  mSaturationScale = (( double ) mSaturation / 100 ) + 1;
332 }
333 
334 void QgsHueSaturationFilter::setColorizeColor( QColor colorizeColor )
335 {
336  mColorizeColor = colorizeColor;
337 
338  // Get hue, saturation for colorized color
339  mColorizeH = mColorizeColor.hue();
340  mColorizeS = mColorizeColor.saturation();
341 }
342 
343 void QgsHueSaturationFilter::writeXML( QDomDocument& doc, QDomElement& parentElem ) const
344 {
345  if ( parentElem.isNull() )
346  {
347  return;
348  }
349 
350  QDomElement filterElem = doc.createElement( "huesaturation" );
351 
352  filterElem.setAttribute( "saturation", QString::number( mSaturation ) );
353  filterElem.setAttribute( "grayscaleMode", QString::number( mGrayscaleMode ) );
354  filterElem.setAttribute( "colorizeOn", QString::number( mColorizeOn ) );
355  filterElem.setAttribute( "colorizeRed", QString::number( mColorizeColor.red() ) );
356  filterElem.setAttribute( "colorizeGreen", QString::number( mColorizeColor.green() ) );
357  filterElem.setAttribute( "colorizeBlue", QString::number( mColorizeColor.blue() ) );
358  filterElem.setAttribute( "colorizeStrength", QString::number( mColorizeStrength ) );
359 
360  parentElem.appendChild( filterElem );
361 }
362 
363 void QgsHueSaturationFilter::readXML( const QDomElement& filterElem )
364 {
365  if ( filterElem.isNull() )
366  {
367  return;
368  }
369 
370  setSaturation( filterElem.attribute( "saturation", "0" ).toInt() );
371  mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( "grayscaleMode", "0" ).toInt();
372 
373  mColorizeOn = ( bool )filterElem.attribute( "colorizeOn", "0" ).toInt();
374  int mColorizeRed = filterElem.attribute( "colorizeRed", "255" ).toInt();
375  int mColorizeGreen = filterElem.attribute( "colorizeGreen", "128" ).toInt();
376  int mColorizeBlue = filterElem.attribute( "colorizeBlue", "128" ).toInt();
377  setColorizeColor( QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue ) );
378  mColorizeStrength = filterElem.attribute( "colorizeStrength", "100" ).toInt();
379 
380 }
virtual int bandCount() const =0
Get number of bands.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void writeXML(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
bool setInput(QgsRasterInterface *input) override
Set input.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height) override
Read block of data using given extent and size.
QgsHueSaturationFilter(QgsRasterInterface *input=0)
virtual QgsRasterInterface * input() const
Current input.
int bandCount() const override
Get number of bands.
QGis::DataType dataType(int bandNo) const override
Returns data type for the band specified by number.
bool setColor(int row, int column, QRgb color)
Set color on position.
Raster data container.
QgsRasterInterface * clone() const override
Clone itself, create deep copy.
void setColorizeColor(QColor colorizeColor)
DataType
Raster data types.
Definition: qgis.h:204
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
void setColorizeOn(bool colorizeOn)
Base class for processing filters like renderers, reprojector, resampler etc.
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:424
bool reset(QGis::DataType theDataType, int theWidth, int theHeight)
Reset block.
void readXML(const QDomElement &filterElem) override
Sets base class members from xml.
Color and saturation filter pipe for rasters.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height)=0
Read block of data using given extent and size.
void setColorizeStrength(int colorizeStrength)
QRgb color(int row, int column) const
Read a single color.
QgsRasterInterface * mInput
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
void setGrayscaleMode(QgsHueSaturationFilter::GrayscaleMode grayscaleMode)
void setSaturation(int saturation)
bool isEmpty() const
Returns true if block is empty, i.e.