QGIS API Documentation  3.2.0-Bonn (bc43194)
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, QStringLiteral( "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( QStringLiteral( "redBand" ), QStringLiteral( "-1" ) ).toInt();
96  int greenBand = elem.attribute( QStringLiteral( "greenBand" ), QStringLiteral( "-1" ) ).toInt();
97  int blueBand = elem.attribute( QStringLiteral( "blueBand" ), QStringLiteral( "-1" ) ).toInt();
98 
99  //contrast enhancements
101  QDomElement redContrastElem = elem.firstChildElement( QStringLiteral( "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( QStringLiteral( "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( QStringLiteral( "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, QgsRasterBlockFeedback *feedback )
134 {
135  Q_UNUSED( bandNo );
136  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
137  if ( !mInput )
138  {
139  return outputBlock.release();
140  }
141 
142  //In some (common) cases, we can simplify the drawing loop considerably and save render time
143  bool fastDraw = ( !usesTransparency()
144  && mRedBand > 0 && mGreenBand > 0 && mBlueBand > 0
145  && mAlphaBand < 1 && !mRedContrastEnhancement && !mGreenContrastEnhancement && !mBlueContrastEnhancement );
146 
147  QSet<int> bands;
148  if ( mRedBand > 0 )
149  {
150  bands << mRedBand;
151  }
152  if ( mGreenBand > 0 )
153  {
154  bands << mGreenBand;
155  }
156  if ( mBlueBand > 0 )
157  {
158  bands << mBlueBand;
159  }
160  if ( bands.empty() )
161  {
162  // no need to draw anything if no band is set
163  // TODO:: we should probably return default color block
164  return outputBlock.release();
165  }
166 
167  if ( mAlphaBand > 0 )
168  {
169  bands << mAlphaBand;
170  }
171 
172  QMap<int, QgsRasterBlock *> bandBlocks;
173  QgsRasterBlock *defaultPointer = nullptr;
174  QSet<int>::const_iterator bandIt = bands.constBegin();
175  for ( ; bandIt != bands.constEnd(); ++bandIt )
176  {
177  bandBlocks.insert( *bandIt, defaultPointer );
178  }
179 
180  QgsRasterBlock *redBlock = nullptr;
181  QgsRasterBlock *greenBlock = nullptr;
182  QgsRasterBlock *blueBlock = nullptr;
183  QgsRasterBlock *alphaBlock = nullptr;
184 
185  bandIt = bands.constBegin();
186  for ( ; bandIt != bands.constEnd(); ++bandIt )
187  {
188  bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height, feedback );
189  if ( !bandBlocks[*bandIt] )
190  {
191  // We should free the alloced mem from block().
192  QgsDebugMsg( "No input band" );
193  --bandIt;
194  for ( ; bandIt != bands.constBegin(); --bandIt )
195  {
196  delete bandBlocks[*bandIt];
197  }
198  return outputBlock.release();
199  }
200  }
201 
202  if ( mRedBand > 0 )
203  {
204  redBlock = bandBlocks[mRedBand];
205  }
206  if ( mGreenBand > 0 )
207  {
208  greenBlock = bandBlocks[mGreenBand];
209  }
210  if ( mBlueBand > 0 )
211  {
212  blueBlock = bandBlocks[mBlueBand];
213  }
214  if ( mAlphaBand > 0 )
215  {
216  alphaBlock = bandBlocks[mAlphaBand];
217  }
218 
219  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
220  {
221  for ( int i = 0; i < bandBlocks.size(); i++ )
222  {
223  delete bandBlocks.value( i );
224  }
225  return outputBlock.release();
226  }
227 
228  QRgb myDefaultColor = NODATA_COLOR;
229 
230  for ( qgssize i = 0; i < ( qgssize )width * height; i++ )
231  {
232  if ( fastDraw ) //fast rendering if no transparency, stretching, color inversion, etc.
233  {
234  if ( redBlock->isNoData( i ) ||
235  greenBlock->isNoData( i ) ||
236  blueBlock->isNoData( i ) )
237  {
238  outputBlock->setColor( i, myDefaultColor );
239  }
240  else
241  {
242  int redVal = ( int )redBlock->value( i );
243  int greenVal = ( int )greenBlock->value( i );
244  int blueVal = ( int )blueBlock->value( i );
245  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
246  }
247  continue;
248  }
249 
250  bool isNoData = false;
251  double redVal = 0;
252  double greenVal = 0;
253  double blueVal = 0;
254  if ( mRedBand > 0 )
255  {
256  redVal = redBlock->value( i );
257  if ( redBlock->isNoData( i ) ) isNoData = true;
258  }
259  if ( !isNoData && mGreenBand > 0 )
260  {
261  greenVal = greenBlock->value( i );
262  if ( greenBlock->isNoData( i ) ) isNoData = true;
263  }
264  if ( !isNoData && mBlueBand > 0 )
265  {
266  blueVal = blueBlock->value( i );
267  if ( blueBlock->isNoData( i ) ) isNoData = true;
268  }
269  if ( isNoData )
270  {
271  outputBlock->setColor( i, myDefaultColor );
272  continue;
273  }
274 
275  //apply default color if red, green or blue not in displayable range
276  if ( ( mRedContrastEnhancement && !mRedContrastEnhancement->isValueInDisplayableRange( redVal ) )
277  || ( mGreenContrastEnhancement && !mGreenContrastEnhancement->isValueInDisplayableRange( redVal ) )
278  || ( mBlueContrastEnhancement && !mBlueContrastEnhancement->isValueInDisplayableRange( redVal ) ) )
279  {
280  outputBlock->setColor( i, myDefaultColor );
281  continue;
282  }
283 
284  //stretch color values
285  if ( mRedContrastEnhancement )
286  {
287  redVal = mRedContrastEnhancement->enhanceContrast( redVal );
288  }
289  if ( mGreenContrastEnhancement )
290  {
291  greenVal = mGreenContrastEnhancement->enhanceContrast( greenVal );
292  }
293  if ( mBlueContrastEnhancement )
294  {
295  blueVal = mBlueContrastEnhancement->enhanceContrast( blueVal );
296  }
297 
298  //opacity
299  double currentOpacity = mOpacity;
300  if ( mRasterTransparency )
301  {
302  currentOpacity = mRasterTransparency->alphaValue( redVal, greenVal, blueVal, mOpacity * 255 ) / 255.0;
303  }
304  if ( mAlphaBand > 0 )
305  {
306  currentOpacity *= alphaBlock->value( i ) / 255.0;
307  }
308 
309  if ( qgsDoubleNear( currentOpacity, 1.0 ) )
310  {
311  outputBlock->setColor( i, qRgba( redVal, greenVal, blueVal, 255 ) );
312  }
313  else
314  {
315  outputBlock->setColor( i, qRgba( currentOpacity * redVal, currentOpacity * greenVal, currentOpacity * blueVal, currentOpacity * 255 ) );
316  }
317  }
318 
319  //delete input blocks
320  QMap<int, QgsRasterBlock *>::const_iterator bandDelIt = bandBlocks.constBegin();
321  for ( ; bandDelIt != bandBlocks.constEnd(); ++bandDelIt )
322  {
323  delete bandDelIt.value();
324  }
325 
326  return outputBlock.release();
327 }
328 
329 void QgsMultiBandColorRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
330 {
331  if ( parentElem.isNull() )
332  {
333  return;
334  }
335 
336  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
337  _writeXml( doc, rasterRendererElem );
338  rasterRendererElem.setAttribute( QStringLiteral( "redBand" ), mRedBand );
339  rasterRendererElem.setAttribute( QStringLiteral( "greenBand" ), mGreenBand );
340  rasterRendererElem.setAttribute( QStringLiteral( "blueBand" ), mBlueBand );
341 
342  //contrast enhancement
343  if ( mRedContrastEnhancement )
344  {
345  QDomElement redContrastElem = doc.createElement( QStringLiteral( "redContrastEnhancement" ) );
346  mRedContrastEnhancement->writeXml( doc, redContrastElem );
347  rasterRendererElem.appendChild( redContrastElem );
348  }
349  if ( mGreenContrastEnhancement )
350  {
351  QDomElement greenContrastElem = doc.createElement( QStringLiteral( "greenContrastEnhancement" ) );
352  mGreenContrastEnhancement->writeXml( doc, greenContrastElem );
353  rasterRendererElem.appendChild( greenContrastElem );
354  }
355  if ( mBlueContrastEnhancement )
356  {
357  QDomElement blueContrastElem = doc.createElement( QStringLiteral( "blueContrastEnhancement" ) );
358  mBlueContrastEnhancement->writeXml( doc, blueContrastElem );
359  rasterRendererElem.appendChild( blueContrastElem );
360  }
361  parentElem.appendChild( rasterRendererElem );
362 }
363 
365 {
366  QList<int> bandList;
367  if ( mRedBand != -1 )
368  {
369  bandList << mRedBand;
370  }
371  if ( mGreenBand != -1 )
372  {
373  bandList << mGreenBand;
374  }
375  if ( mBlueBand != -1 )
376  {
377  bandList << mBlueBand;
378  }
379  return bandList;
380 }
A rectangle specified with double values.
Definition: qgsrectangle.h:40
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
const QgsContrastEnhancement * blueContrastEnhancement() const
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
virtual QgsRectangle extent() const
Gets the extent of the interface.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
virtual QgsRasterInterface * input() const
Current input.
DataType
Raster data types.
Definition: qgis.h:91
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
QgsMultiBandColorRenderer * clone() const override
Clone itself, create deep copy.
bool isNoData(int row, int column)
Check if value at position is no data.
void setGreenContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
static const QRgb NODATA_COLOR
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:106
Raster data container.
void setRedContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses) ...
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
bool isValueInDisplayableRange(double value)
Returns true if a pixel value is in displayable range, false if pixel is outside of range (i...
bool usesTransparency() const
void readXml(const QDomElement &elem)
int mAlphaBand
Read alpha value from band.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Base class for processing filters like renderers, reprojector, resampler etc.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
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:510
const QgsContrastEnhancement * redContrastEnhancement() const
const QgsContrastEnhancement * greenContrastEnhancement() const
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.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const
double mOpacity
Global alpha value (0-1)
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
QgsMultiBandColorRenderer(QgsRasterInterface *input, int redBand, int greenBand, int blueBand, QgsContrastEnhancement *redEnhancement=nullptr, QgsContrastEnhancement *greenEnhancement=nullptr, QgsContrastEnhancement *blueEnhancement=nullptr)
QgsRasterInterface * mInput
Feedback object tailored for raster block reading.
Raster renderer pipe that applies colors to a raster.
int enhanceContrast(double value)
Applies the contrast enhancement to a value.