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