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
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QGis::DataType dataType(int bandNo) const
Returns data type for the band specified by number.
QgsHueSaturationFilter(QgsRasterInterface *input=0)
virtual QgsRasterInterface * input() const
Current input.
void readXML(const QDomElement &filterElem)
Sets base class members from xml.
bool setColor(int row, int column, QRgb color)
Set color on position.
Raster data container.
bool setInput(QgsRasterInterface *input)
Set input.
void writeXML(QDomDocument &doc, QDomElement &parentElem) const
Write base class members to xml.
void setColorizeColor(QColor colorizeColor)
int bandCount() const
Get number of bands.
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.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height)
Read block of data using given extent and size.
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.
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.
QgsRasterInterface * clone() const
Clone itself, create deep copy.
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.