QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 );
45  filter->setColorizeOn( mColorizeOn );
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 ( size_t i = 0; i < ( size_t )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  // Get rgb for color
175  myColor.getRgb( &r, &g, &b );
176  if ( alpha != 255 )
177  {
178  // Semi-transparent pixel. We need to adjust the colors since we are using QGis::ARGB32_Premultiplied
179  // and color values have been premultiplied by alpha
180  alphaFactor = alpha / 255.;
181  r /= alphaFactor;
182  g /= alphaFactor;
183  b /= alphaFactor;
184  myColor = QColor::fromRgb( r, g, b );
185  }
186 
187  myColor.getHsl( &h, &s, &l );
188 
189  // Changing saturation?
190  if (( mGrayscaleMode != GrayscaleOff ) || ( mSaturationScale != 1 ) )
191  {
192  processSaturation( r, g, b, h, s, l );
193  }
194 
195  // Colorizing?
196  if ( mColorizeOn )
197  {
198  processColorization( r, g, b, h, s, l );
199  }
200 
201  // Convert back to rgb
202  if ( alpha != 255 )
203  {
204  // Transparent pixel, need to premultiply color components
205  r *= alphaFactor;
206  g *= alphaFactor;
207  b *= alphaFactor;
208  }
209 
210  outputBlock->setColor( i, qRgba( r, g, b, alpha ) );
211  }
212 
213  delete inputBlock;
214  return outputBlock;
215 }
216 
217 // Process a colorization and update resultant HSL & RGB values
218 void QgsHueSaturationFilter::processColorization( int &r, int &g, int &b, int &h, int &s, int &l )
219 {
220  QColor myColor;
221 
222  // Overwrite hue and saturation with values from colorize color
223  h = mColorizeH;
224  s = mColorizeS;
225 
226 
227  QColor colorizedColor = QColor::fromHsl( h, s, l );
228 
229  if ( mColorizeStrength == 100 )
230  {
231  // Full strength
232  myColor = colorizedColor;
233 
234  // RGB may have changed, update them
235  myColor.getRgb( &r, &g, &b );
236  }
237  else
238  {
239  // Get rgb for colorized color
240  int colorizedR, colorizedG, colorizedB;
241  colorizedColor.getRgb( &colorizedR, &colorizedG, &colorizedB );
242 
243  // Now, linearly scale by colorize strength
244  double p = ( double ) mColorizeStrength / 100.;
245  r = p * colorizedR + ( 1 - p ) * r;
246  g = p * colorizedG + ( 1 - p ) * g;
247  b = p * colorizedB + ( 1 - p ) * b;
248 
249  // RGB changed, so update HSL values
250  myColor = QColor::fromRgb( r, g, b );
251  myColor.getHsl( &h, &s, &l );
252  }
253 }
254 
255 // Process a change in saturation and update resultant HSL & RGB values
256 void QgsHueSaturationFilter::processSaturation( int &r, int &g, int &b, int &h, int &s, int &l )
257 {
258 
259  QColor myColor;
260 
261  // Are we converting layer to grayscale?
262  switch ( mGrayscaleMode )
263  {
264  case GrayscaleLightness:
265  {
266  // Lightness mode, set saturation to zero
267  s = 0;
268 
269  // Saturation changed, so update rgb values
270  myColor = QColor::fromHsl( h, s, l );
271  myColor.getRgb( &r, &g, &b );
272  return;
273  }
274  case GrayscaleLuminosity:
275  {
276  // Grayscale by weighted rgb components
277  int luminosity = 0.21 * r + 0.72 * g + 0.07 * b;
278  r = g = b = luminosity;
279 
280  // RGB changed, so update HSL values
281  myColor = QColor::fromRgb( r, g, b );
282  myColor.getHsl( &h, &s, &l );
283  return;
284  }
285  case GrayscaleAverage:
286  {
287  // Grayscale by average of rgb components
288  int average = ( r + g + b ) / 3;
289  r = g = b = average;
290 
291  // RGB changed, so update HSL values
292  myColor = QColor::fromRgb( r, g, b );
293  myColor.getHsl( &h, &s, &l );
294  return;
295  }
296  case GrayscaleOff:
297  {
298  // Not being made grayscale, do saturation change
299  if ( mSaturationScale < 1 )
300  {
301  // Lowering the saturation. Use a simple linear relationship
302  s = qMin(( int )( s * mSaturationScale ), 255 );
303  }
304  else
305  {
306  // Raising the saturation. Use a saturation curve to prevent
307  // clipping at maximum saturation with ugly results.
308  s = qMin(( int )( 255. * ( 1 - pow( 1 - ( s / 255. ) , pow( mSaturationScale, 2 ) ) ) ), 255 );
309  }
310 
311  // Saturation changed, so update rgb values
312  myColor = QColor::fromHsl( h, s, l );
313  myColor.getRgb( &r, &g, &b );
314  return;
315  }
316  }
317 }
318 
320 {
321  mSaturation = qBound( -100, saturation, 100 );
322 
323  // Scale saturation value to [0-2], where 0 = desaturated
324  mSaturationScale = (( double ) mSaturation / 100 ) + 1;
325 }
326 
327 void QgsHueSaturationFilter::setColorizeColor( QColor colorizeColor )
328 {
330 
331  // Get hue, saturation for colorized color
332  mColorizeH = mColorizeColor.hue();
333  mColorizeS = mColorizeColor.saturation();
334 }
335 
336 void QgsHueSaturationFilter::writeXML( QDomDocument& doc, QDomElement& parentElem ) const
337 {
338  if ( parentElem.isNull() )
339  {
340  return;
341  }
342 
343  QDomElement filterElem = doc.createElement( "huesaturation" );
344 
345  filterElem.setAttribute( "saturation", QString::number( mSaturation ) );
346  filterElem.setAttribute( "grayscaleMode", QString::number( mGrayscaleMode ) );
347  filterElem.setAttribute( "colorizeOn", QString::number( mColorizeOn ) );
348  filterElem.setAttribute( "colorizeRed", QString::number( mColorizeColor.red() ) );
349  filterElem.setAttribute( "colorizeGreen", QString::number( mColorizeColor.green() ) );
350  filterElem.setAttribute( "colorizeBlue", QString::number( mColorizeColor.blue() ) );
351  filterElem.setAttribute( "colorizeStrength", QString::number( mColorizeStrength ) );
352 
353  parentElem.appendChild( filterElem );
354 }
355 
356 void QgsHueSaturationFilter::readXML( const QDomElement& filterElem )
357 {
358  if ( filterElem.isNull() )
359  {
360  return;
361  }
362 
363  setSaturation( filterElem.attribute( "saturation", "0" ).toInt() );
364  mGrayscaleMode = ( QgsHueSaturationFilter::GrayscaleMode )filterElem.attribute( "grayscaleMode", "0" ).toInt();
365 
366  mColorizeOn = ( bool )filterElem.attribute( "colorizeOn", "0" ).toInt();
367  int mColorizeRed = filterElem.attribute( "colorizeRed", "255" ).toInt();
368  int mColorizeGreen = filterElem.attribute( "colorizeGreen", "128" ).toInt();
369  int mColorizeBlue = filterElem.attribute( "colorizeBlue", "128" ).toInt();
370  setColorizeColor( QColor::fromRgb( mColorizeRed, mColorizeGreen, mColorizeBlue ) );
371  mColorizeStrength = filterElem.attribute( "colorizeStrength", "100" ).toInt();
372 
373 }