QGIS API Documentation  2.11.0-Master
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 
338 {
339  mColorizeColor = colorizeColor;
340 
341  // Get hue, saturation for colorized color
342  mColorizeH = mColorizeColor.hue();
343  mColorizeS = mColorizeColor.saturation();
344 }
345 
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 
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 }
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.
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
#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.
int hue() const
void getHsl(int *h, int *s, int *l, int *a) const
QColor fromHsl(int h, int s, int l, int a)
bool setColor(int row, int column, QRgb color)
Set color on position.
QColor fromRgb(QRgb rgb)
QString number(int n, int base)
Raster data container.
int red() const
void setAttribute(const QString &name, const QString &value)
QgsRasterInterface * clone() const override
Clone itself, create deep copy.
int toInt(bool *ok, int base) const
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.
int green() const
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:449
bool reset(QGis::DataType theDataType, int theWidth, int theHeight)
Reset block.
bool isNull() const
int blue() const
Unknown or unspecified type.
Definition: qgis.h:206
void readXML(const QDomElement &filterElem) override
Sets base class members from xml.
void getRgb(int *r, int *g, int *b, int *a) const
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)
int saturation() const
QRgb color(int row, int column) const
Read a single color.
QDomElement createElement(const QString &tagName)
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:219
QgsRasterInterface * mInput
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:221
void setGrayscaleMode(QgsHueSaturationFilter::GrayscaleMode grayscaleMode)
void setSaturation(int saturation)
bool isEmpty() const
Returns true if block is empty, i.e.