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