QGIS API Documentation  2.14.0-Essen
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" )
32  , mRedBand( redBand )
33  , mGreenBand( greenBand )
34  , mBlueBand( blueBand )
35  , mRedContrastEnhancement( redEnhancement )
36  , mGreenContrastEnhancement( greenEnhancement )
37  , mBlueContrastEnhancement( blueEnhancement )
38 {
39 }
40 
42 {
43  delete mRedContrastEnhancement;
44  delete mGreenContrastEnhancement;
45  delete mBlueContrastEnhancement;
46 }
47 
49 {
50  QgsMultiBandColorRenderer * renderer = new QgsMultiBandColorRenderer( nullptr, mRedBand, mGreenBand, mBlueBand );
51  if ( mRedContrastEnhancement )
52  {
53  renderer->setRedContrastEnhancement( new QgsContrastEnhancement( *mRedContrastEnhancement ) );
54  }
55  if ( mGreenContrastEnhancement )
56  {
57  renderer->setGreenContrastEnhancement( new QgsContrastEnhancement( *mGreenContrastEnhancement ) );
58  }
59  if ( mBlueContrastEnhancement )
60  {
61  renderer->setBlueContrastEnhancement( new QgsContrastEnhancement( *mBlueContrastEnhancement ) );
62  }
63  renderer->setOpacity( mOpacity );
64  renderer->setAlphaBand( mAlphaBand );
66 
67  return renderer;
68 }
69 
71 {
72  delete mRedContrastEnhancement;
73  mRedContrastEnhancement = ce;
74 }
75 
77 {
78  delete mGreenContrastEnhancement;
79  mGreenContrastEnhancement = ce;
80 }
81 
83 {
84  delete mBlueContrastEnhancement;
85  mBlueContrastEnhancement = ce;
86 }
87 
89 {
90  if ( elem.isNull() )
91  {
92  return nullptr;
93  }
94 
95  //red band, green band, blue band
96  int redBand = elem.attribute( "redBand", "-1" ).toInt();
97  int greenBand = elem.attribute( "greenBand", "-1" ).toInt();
98  int blueBand = elem.attribute( "blueBand", "-1" ).toInt();
99 
100  //contrast enhancements
102  QDomElement redContrastElem = elem.firstChildElement( "redContrastEnhancement" );
103  if ( !redContrastElem.isNull() )
104  {
105  redContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
106  input->dataType( redBand ) ) );
107  redContrastEnhancement->readXML( redContrastElem );
108  }
109 
111  QDomElement greenContrastElem = elem.firstChildElement( "greenContrastEnhancement" );
112  if ( !greenContrastElem.isNull() )
113  {
114  greenContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
115  input->dataType( greenBand ) ) );
116  greenContrastEnhancement->readXML( greenContrastElem );
117  }
118 
120  QDomElement blueContrastElem = elem.firstChildElement( "blueContrastEnhancement" );
121  if ( !blueContrastElem.isNull() )
122  {
123  blueContrastEnhancement = new QgsContrastEnhancement(( QGis::DataType )(
124  input->dataType( blueBand ) ) );
125  blueContrastEnhancement->readXML( blueContrastElem );
126  }
127 
128  QgsRasterRenderer* r = new QgsMultiBandColorRenderer( input, redBand, greenBand, blueBand, redContrastEnhancement,
129  greenContrastEnhancement, blueContrastEnhancement );
130  r->readXML( elem );
131  return r;
132 }
133 
134 QgsRasterBlock* QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
135 {
136  Q_UNUSED( bandNo );
137  QgsRasterBlock *outputBlock = new QgsRasterBlock();
138  if ( !mInput )
139  {
140  return outputBlock;
141  }
142 
143  //In some (common) cases, we can simplify the drawing loop considerably and save render time
144  bool fastDraw = ( !usesTransparency()
145  && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0
146  && mAlphaBand < 1 && !mRedContrastEnhancement && !mGreenContrastEnhancement && !mBlueContrastEnhancement );
147 
148  QSet<int> bands;
149  if ( mRedBand > 0 )
150  {
151  bands << mRedBand;
152  }
153  if ( mGreenBand > 0 )
154  {
155  bands << mGreenBand;
156  }
157  if ( mBlueBand > 0 )
158  {
159  bands << mBlueBand;
160  }
161  if ( bands.size() < 1 )
162  {
163  // no need to draw anything if no band is set
164  // TODO:: we should probably return default color block
165  return outputBlock;
166  }
167 
168  if ( mAlphaBand > 0 )
169  {
170  bands << mAlphaBand;
171  }
172 
173  QMap<int, QgsRasterBlock*> bandBlocks;
174  QgsRasterBlock* defaultPointer = nullptr;
175  QSet<int>::const_iterator bandIt = bands.constBegin();
176  for ( ; bandIt != bands.constEnd(); ++bandIt )
177  {
178  bandBlocks.insert( *bandIt, defaultPointer );
179  }
180 
181  QgsRasterBlock* redBlock = nullptr;
182  QgsRasterBlock* greenBlock = nullptr;
183  QgsRasterBlock* blueBlock = nullptr;
184  QgsRasterBlock* alphaBlock = nullptr;
185 
186  bandIt = bands.constBegin();
187  for ( ; bandIt != bands.constEnd(); ++bandIt )
188  {
189  bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height );
190  if ( !bandBlocks[*bandIt] )
191  {
192  // We should free the alloced mem from block().
193  QgsDebugMsg( "No input band" );
194  --bandIt;
195  for ( ; bandIt != bands.constBegin(); --bandIt )
196  {
197  delete bandBlocks[*bandIt];
198  }
199  return outputBlock;
200  }
201  }
202 
203  if ( mRedBand > 0 )
204  {
205  redBlock = bandBlocks[mRedBand];
206  }
207  if ( mGreenBand > 0 )
208  {
209  greenBlock = bandBlocks[mGreenBand];
210  }
211  if ( mBlueBand > 0 )
212  {
213  blueBlock = bandBlocks[mBlueBand];
214  }
215  if ( mAlphaBand > 0 )
216  {
217  alphaBlock = bandBlocks[mAlphaBand];
218  }
219 
220  if ( !outputBlock->reset( QGis::ARGB32_Premultiplied, width, height ) )
221  {
222  for ( int i = 0; i < bandBlocks.size(); i++ )
223  {
224  delete bandBlocks.value( i );
225  }
226  return outputBlock;
227  }
228 
229  QRgb myDefaultColor = NODATA_COLOR;
230 
231  for ( qgssize i = 0; i < ( qgssize )width*height; i++ )
232  {
233  if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc.
234  {
235  if ( redBlock->isNoData( i ) ||
236  greenBlock->isNoData( i ) ||
237  blueBlock->isNoData( i ) )
238  {
239  outputBlock->setColor( i, myDefaultColor );
240  }
241  else
242  {
243  int redVal = ( int )redBlock->value( i );
244  int greenVal = ( int )greenBlock->value( i );
245  int blueVal = ( int )blueBlock->value( i );
246  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
247  }
248  continue;
249  }
250 
251  bool isNoData = false;
252  double redVal = 0;
253  double greenVal = 0;
254  double blueVal = 0;
255  if ( mRedBand > 0 )
256  {
257  redVal = redBlock->value( i );
258  if ( redBlock->isNoData( i ) ) isNoData = true;
259  }
260  if ( !isNoData && mGreenBand > 0 )
261  {
262  greenVal = greenBlock->value( i );
263  if ( greenBlock->isNoData( i ) ) isNoData = true;
264  }
265  if ( !isNoData && mBlueBand > 0 )
266  {
267  blueVal = blueBlock->value( i );
268  if ( blueBlock->isNoData( i ) ) isNoData = true;
269  }
270  if ( isNoData )
271  {
272  outputBlock->setColor( i, myDefaultColor );
273  continue;
274  }
275 
276  //apply default color if red, green or blue not in displayable range
277  if (( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) )
278  || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) )
279  || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) )
280  {
281  outputBlock->setColor( i, myDefaultColor );
282  continue;
283  }
284 
285  //stretch color values
286  if ( mRedContrastEnhancement )
287  {
288  redVal = mRedContrastEnhancement->enhanceContrast( redVal );
289  }
290  if ( mGreenContrastEnhancement )
291  {
292  greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal );
293  }
294  if ( mBlueContrastEnhancement )
295  {
296  blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal );
297  }
298 
299  //opacity
300  double currentOpacity = mOpacity;
301  if ( mRasterTransparency )
302  {
303  currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0;
304  }
305  if ( mAlphaBand > 0 )
306  {
307  currentOpacity *= alphaBlock->value( i ) / 255.0;
308  }
309 
310  if ( qgsDoubleNear( currentOpacity, 1.0 ) )
311  {
312  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
313  }
314  else
315  {
316  outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) );
317  }
318  }
319 
320  //delete input blocks
322  for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt )
323  {
324  delete bandDelIt.value();
325  }
326 
327  return outputBlock;
328 }
329 
331 {
332  if ( parentElem.isNull() )
333  {
334  return;
335  }
336 
337  QDomElement rasterRendererElem = doc.createElement( "rasterrenderer" );
338  _writeXML( doc, rasterRendererElem );
339  rasterRendererElem.setAttribute( "redBand", mRedBand );
340  rasterRendererElem.setAttribute( "greenBand", mGreenBand );
341  rasterRendererElem.setAttribute( "blueBand", mBlueBand );
342 
343  //contrast enhancement
344  if ( mRedContrastEnhancement )
345  {
346  QDomElement redContrastElem = doc.createElement( "redContrastEnhancement" );
347  mRedContrastEnhancement->writeXML( doc, redContrastElem );
348  rasterRendererElem.appendChild( redContrastElem );
349  }
350  if ( mGreenContrastEnhancement )
351  {
352  QDomElement greenContrastElem = doc.createElement( "greenContrastEnhancement" );
353  mGreenContrastEnhancement->writeXML( doc, greenContrastElem );
354  rasterRendererElem.appendChild( greenContrastElem );
355  }
356  if ( mBlueContrastEnhancement )
357  {
358  QDomElement blueContrastElem = doc.createElement( "blueContrastEnhancement" );
359  mBlueContrastEnhancement->writeXML( doc, blueContrastElem );
360  rasterRendererElem.appendChild( blueContrastElem );
361  }
362  parentElem.appendChild( rasterRendererElem );
363 }
364 
366 {
367  QList<int> bandList;
368  if ( mRedBand != -1 )
369  {
370  bandList << mRedBand;
371  }
372  if ( mGreenBand != -1 )
373  {
374  bandList << mGreenBand;
375  }
376  if ( mBlueBand != -1 )
377  {
378  bandList << mBlueBand;
379  }
380  return bandList;
381 }
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)
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int size() const
bool isValueInDisplayableRange(double)
Return true if pixel is in stretable range, false if pixel is outside of range (i.e., clipped)
const_iterator constBegin() const
virtual QgsRasterInterface * input() const
Current input.
void writeXML(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
const QgsContrastEnhancement * redContrastEnhancement() const
QgsMultiBandColorRenderer * clone() const override
Clone itself, create deep copy.
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:285
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.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:146
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.
void setAttribute(const QString &name, const QString &value)
int toInt(bool *ok, int base) const
const_iterator constEnd() const
const T & value() const
const_iterator constEnd() const
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:392
const QgsContrastEnhancement * blueContrastEnhancement() const
bool reset(QGis::DataType theDataType, int theWidth, int theHeight)
Reset block.
const_iterator constBegin() const
bool isNull() const
virtual QgsRectangle extent()
Get the extent of the interface.
int enhanceContrast(double)
Apply the contrast enhancement to a value.
QDomElement firstChildElement(const QString &tagName) const
DataType
Raster data types.
Definition: qgis.h:129
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.
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...
iterator insert(const Key &key, const T &value)
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setOpacity(double opacity)
QgsMultiBandColorRenderer(QgsRasterInterface *input, int redBand, int greenBand, int blueBand, QgsContrastEnhancement *redEnhancement=nullptr, QgsContrastEnhancement *greenEnhancement=nullptr, QgsContrastEnhancement *blueEnhancement=nullptr)
QDomElement createElement(const QString &tagName)
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.
int size() const
const T value(const Key &key) const