QGIS API Documentation  2.99.0-Master (6a61179)
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  QgsRasterBlock *outputBlock = new QgsRasterBlock();
137  if ( !mInput )
138  {
139  return outputBlock;
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.size() < 1 )
161  {
162  // no need to draw anything if no band is set
163  // TODO:: we should probably return default color block
164  return outputBlock;
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;
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;
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;
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:35
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
Get the extent of the interface.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int alphaValue(double, int theGlobalTransparency=255) const
Returns the transparency value for a single value Pixel.
bool isValueInDisplayableRange(double)
Return true if pixel is in stretable range, false if pixel is outside of range (i.e., clipped)
virtual QgsRasterInterface * input() const
Current input.
void copyCommonProperties(const QgsRasterRenderer *other)
Copies common properties like opacity / transparency data from other renderer.
DataType
Raster data types.
Definition: qgis.h:60
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.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:196
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.
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:75
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) ...
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.
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:336
bool reset(Qgis::DataType theDataType, int theWidth, int theHeight)
Reset block.
const QgsContrastEnhancement * redContrastEnhancement() const
const QgsContrastEnhancement * greenContrastEnhancement() const
int enhanceContrast(double)
Apply the contrast enhancement to a value. Return values are 0 - 254, -1 means the pixel was clipped ...
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.