QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsmultibandcolorrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmultibandcolorrenderer.cpp
3  -----------------------------
4  begin : December 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco at sourcepole dot ch
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 
19 #include "qgscontrastenhancement.h"
20 #include "qgsrastertransparency.h"
21 #include "qgsrasterviewport.h"
22 #include <QDomDocument>
23 #include <QDomElement>
24 #include <QImage>
25 #include <QSet>
26 
27 QgsMultiBandColorRenderer::QgsMultiBandColorRenderer( QgsRasterInterface* input, int redBand, int greenBand, int blueBand,
28  QgsContrastEnhancement* redEnhancement,
29  QgsContrastEnhancement* greenEnhancement,
30  QgsContrastEnhancement* blueEnhancement ):
31  QgsRasterRenderer( input, "multibandcolor" ), mRedBand( redBand ), mGreenBand( greenBand ), mBlueBand( blueBand ),
32  mRedContrastEnhancement( redEnhancement ), mGreenContrastEnhancement( greenEnhancement ), mBlueContrastEnhancement( blueEnhancement )
33 {
34 }
35 
37 {
38  delete mRedContrastEnhancement;
39  delete mGreenContrastEnhancement;
40  delete mBlueContrastEnhancement;
41 }
42 
44 {
45  QgsMultiBandColorRenderer * renderer = new QgsMultiBandColorRenderer( 0, mRedBand, mGreenBand, mBlueBand );
46  if ( mRedContrastEnhancement )
47  {
48  renderer->setRedContrastEnhancement( new QgsContrastEnhancement( *mRedContrastEnhancement ) );
49  }
50  if ( mGreenContrastEnhancement )
51  {
52  renderer->setGreenContrastEnhancement( new QgsContrastEnhancement( *mGreenContrastEnhancement ) );
53  }
54  if ( mBlueContrastEnhancement )
55  {
56  renderer->setBlueContrastEnhancement( new QgsContrastEnhancement( *mBlueContrastEnhancement ) );
57  }
58  renderer->setOpacity( mOpacity );
59  renderer->setAlphaBand( mAlphaBand );
61 
62  return renderer;
63 }
64 
66 {
67  delete mRedContrastEnhancement; mRedContrastEnhancement = ce;
68 }
69 
71 {
72  delete mGreenContrastEnhancement; mGreenContrastEnhancement = ce;
73 }
74 
76 {
77  delete mBlueContrastEnhancement; mBlueContrastEnhancement = ce;
78 }
79 
81 {
82  if ( elem.isNull() )
83  {
84  return 0;
85  }
86 
87  //red band, green band, blue band
88  int redBand = elem.attribute( "redBand", "-1" ).toInt();
89  int greenBand = elem.attribute( "greenBand", "-1" ).toInt();
90  int blueBand = elem.attribute( "blueBand", "-1" ).toInt();
91 
92  //contrast enhancements
94  QDomElement redContrastElem = elem.firstChildElement( "redContrastEnhancement" );
95  if ( !redContrastElem.isNull() )
96  {
97  redContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
98  input->dataType( redBand ) ) );
99  redContrastEnhancement->readXML( redContrastElem );
100  }
101 
103  QDomElement greenContrastElem = elem.firstChildElement( "greenContrastEnhancement" );
104  if ( !greenContrastElem.isNull() )
105  {
106  greenContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
107  input->dataType( greenBand ) ) );
108  greenContrastEnhancement->readXML( greenContrastElem );
109  }
110 
112  QDomElement blueContrastElem = elem.firstChildElement( "blueContrastEnhancement" );
113  if ( !blueContrastElem.isNull() )
114  {
115  blueContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
116  input->dataType( blueBand ) ) );
117  blueContrastEnhancement->readXML( blueContrastElem );
118  }
119 
120  QgsRasterRenderer* r = new QgsMultiBandColorRenderer( input, redBand, greenBand, blueBand, redContrastEnhancement,
121  greenContrastEnhancement, blueContrastEnhancement );
122  r->readXML( elem );
123  return r;
124 }
125 
126 QgsRasterBlock* QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
127 {
128  Q_UNUSED( bandNo );
129  QgsRasterBlock *outputBlock = new QgsRasterBlock();
130  if ( !mInput )
131  {
132  return outputBlock;
133  }
134 
135  //In some (common) cases, we can simplify the drawing loop considerably and save render time
136  bool fastDraw = ( !usesTransparency()
137  && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0
138  && mAlphaBand < 1 && !mRedContrastEnhancement && !mGreenContrastEnhancement && !mBlueContrastEnhancement );
139 
140  QSet<int> bands;
141  if ( mRedBand > 0 )
142  {
143  bands << mRedBand;
144  }
145  if ( mGreenBand > 0 )
146  {
147  bands << mGreenBand;
148  }
149  if ( mBlueBand > 0 )
150  {
151  bands << mBlueBand;
152  }
153  if ( bands.size() < 1 )
154  {
155  // no need to draw anything if no band is set
156  // TODO:: we should probably return default color block
157  return outputBlock;
158  }
159 
160  if ( mAlphaBand > 0 )
161  {
162  bands << mAlphaBand;
163  }
164 
165  QMap<int, QgsRasterBlock*> bandBlocks;
166  QgsRasterBlock* defaultPointer = 0;
167  QSet<int>::const_iterator bandIt = bands.constBegin();
168  for ( ; bandIt != bands.constEnd(); ++bandIt )
169  {
170  bandBlocks.insert( *bandIt, defaultPointer );
171  }
172 
173  QgsRasterBlock* redBlock = 0;
174  QgsRasterBlock* greenBlock = 0;
175  QgsRasterBlock* blueBlock = 0;
176  QgsRasterBlock* alphaBlock = 0;
177 
178  bandIt = bands.constBegin();
179  for ( ; bandIt != bands.constEnd(); ++bandIt )
180  {
181  bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height );
182  if ( !bandBlocks[*bandIt] )
183  {
184  // We should free the alloced mem from block().
185  QgsDebugMsg( "No input band" );
186  --bandIt;
187  for ( ; bandIt != bands.constBegin(); --bandIt )
188  {
189  delete bandBlocks[*bandIt];
190  }
191  return outputBlock;
192  }
193  }
194 
195  if ( mRedBand > 0 )
196  {
197  redBlock = bandBlocks[mRedBand];
198  }
199  if ( mGreenBand > 0 )
200  {
201  greenBlock = bandBlocks[mGreenBand];
202  }
203  if ( mBlueBand > 0 )
204  {
205  blueBlock = bandBlocks[mBlueBand];
206  }
207  if ( mAlphaBand > 0 )
208  {
209  alphaBlock = bandBlocks[mAlphaBand];
210  }
211 
212  if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) )
213  {
214  for ( int i = 0; i < bandBlocks.size(); i++ )
215  {
216  delete bandBlocks.value( i );
217  }
218  return outputBlock;
219  }
220 
221  QRgb myDefaultColor = NODATA_COLOR;
222 
223  for ( qgssize i = 0; i < ( qgssize )width*height; i++ )
224  {
225  if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc.
226  {
227  if ( redBlock->isNoData( i ) ||
228  greenBlock->isNoData( i ) ||
229  blueBlock->isNoData( i ) )
230  {
231  outputBlock->setColor( i, myDefaultColor );
232  }
233  else
234  {
235  int redVal = ( int )redBlock->value( i );
236  int greenVal = ( int )greenBlock->value( i );
237  int blueVal = ( int )blueBlock->value( i );
238  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
239  }
240  continue;
241  }
242 
243  bool isNoData = false;
244  double redVal = 0;
245  double greenVal = 0;
246  double blueVal = 0;
247  if ( mRedBand > 0 )
248  {
249  redVal = redBlock->value( i );
250  if ( redBlock->isNoData( i ) ) isNoData = true;
251  }
252  if ( !isNoData && mGreenBand > 0 )
253  {
254  greenVal = greenBlock->value( i );
255  if ( greenBlock->isNoData( i ) ) isNoData = true;
256  }
257  if ( !isNoData && mBlueBand > 0 )
258  {
259  blueVal = blueBlock->value( i );
260  if ( blueBlock->isNoData( i ) ) isNoData = true;
261  }
262  if ( isNoData )
263  {
264  outputBlock->setColor( i, myDefaultColor );
265  continue;
266  }
267 
268  //apply default color if red, green or blue not in displayable range
269  if (( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) )
270  || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) )
271  || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) )
272  {
273  outputBlock->setColor( i, myDefaultColor );
274  continue;
275  }
276 
277  //stretch color values
278  if ( mRedContrastEnhancement )
279  {
280  redVal = mRedContrastEnhancement->enhanceContrast( redVal );
281  }
282  if ( mGreenContrastEnhancement )
283  {
284  greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal );
285  }
286  if ( mBlueContrastEnhancement )
287  {
288  blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal );
289  }
290 
291  //opacity
292  double currentOpacity = mOpacity;
293  if ( mRasterTransparency )
294  {
295  currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0;
296  }
297  if ( mAlphaBand > 0 )
298  {
299  currentOpacity *= alphaBlock->value( i ) / 255.0;
300  }
301 
302  if ( qgsDoubleNear( currentOpacity, 1.0 ) )
303  {
304  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
305  }
306  else
307  {
308  outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) );
309  }
310  }
311 
312  //delete input blocks
313  QMap<int, QgsRasterBlock*>::const_iterator bandDelIt = bandBlocks.constBegin();
314  for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt )
315  {
316  delete bandDelIt.value();
317  }
318 
319  return outputBlock;
320 }
321 
322 void QgsMultiBandColorRenderer::writeXML( QDomDocument& doc, QDomElement& parentElem ) const
323 {
324  if ( parentElem.isNull() )
325  {
326  return;
327  }
328 
329  QDomElement rasterRendererElem = doc.createElement( "rasterrenderer" );
330  _writeXML( doc, rasterRendererElem );
331  rasterRendererElem.setAttribute( "redBand", mRedBand );
332  rasterRendererElem.setAttribute( "greenBand", mGreenBand );
333  rasterRendererElem.setAttribute( "blueBand", mBlueBand );
334 
335  //contrast enhancement
336  if ( mRedContrastEnhancement )
337  {
338  QDomElement redContrastElem = doc.createElement( "redContrastEnhancement" );
339  mRedContrastEnhancement->writeXML( doc, redContrastElem );
340  rasterRendererElem.appendChild( redContrastElem );
341  }
342  if ( mGreenContrastEnhancement )
343  {
344  QDomElement greenContrastElem = doc.createElement( "greenContrastEnhancement" );
345  mGreenContrastEnhancement->writeXML( doc, greenContrastElem );
346  rasterRendererElem.appendChild( greenContrastElem );
347  }
348  if ( mBlueContrastEnhancement )
349  {
350  QDomElement blueContrastElem = doc.createElement( "blueContrastEnhancement" );
351  mBlueContrastEnhancement->writeXML( doc, blueContrastElem );
352  rasterRendererElem.appendChild( blueContrastElem );
353  }
354  parentElem.appendChild( rasterRendererElem );
355 }
356 
358 {
359  QList<int> bandList;
360  if ( mRedBand != -1 )
361  {
362  bandList << mRedBand;
363  }
364  if ( mGreenBand != -1 )
365  {
366  bandList << mGreenBand;
367  }
368  if ( mBlueBand != -1 )
369  {
370  bandList << mBlueBand;
371  }
372  return bandList;
373 }
void writeXML(QDomDocument &doc, QDomElement &parentElem) const
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
const QgsContrastEnhancement * greenContrastEnhancement() const
void readXML(const QDomElement &elem)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
bool isValueInDisplayableRange(double)
Return true if pixel is in stretable range, false if pixel is outside of range (i.e., clipped)
void writeXML(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
const QgsContrastEnhancement * redContrastEnhancement() const
bool isNoData(int row, int column)
Check if value at position is no data.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:327
void setGreenContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
void readXML(const QDomElement &rendererElem) override
Sets base class members from xml.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
bool usesTransparency() const
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value.
bool setColor(int row, int column, QRgb color)
Set color on position.
static const QRgb NODATA_COLOR
int alphaValue(double, int theGlobalTransparency=255) const
Returns the transparency value for a single value Pixel.
Raster data container.
void setRedContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
double value(int row, int column) const
Read a single value if type of block is numeric.
DataType
Raster data types.
Definition: qgis.h:204
int mAlphaBand
Read alpha value from band.
virtual QGis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
void setAlphaBand(int band)
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:426
const QgsContrastEnhancement * blueContrastEnhancement() const
bool reset(QGis::DataType theDataType, int theWidth, int theHeight)
Reset block.
int enhanceContrast(double)
Apply the contrast enhancement to a value.
void setBlueContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height)=0
Read block of data using given extent and size.
QgsMultiBandColorRenderer(QgsRasterInterface *input, int redBand, int greenBand, int blueBand, QgsContrastEnhancement *redEnhancement=0, QgsContrastEnhancement *greenEnhancement=0, QgsContrastEnhancement *blueEnhancement=0)
Renderer for multiband images with the color components.
double mOpacity
Global alpha value (0-1)
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setOpacity(double opacity)
QgsRasterInterface * clone() const override
Clone itself, create deep copy.
void _writeXML(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXML method of subclasses) ...
QgsRasterInterface * mInput
void setRasterTransparency(QgsRasterTransparency *t)
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height) override
Read block of data using given extent and size.
Raster renderer pipe that applies colors to a raster.