QGIS API Documentation  2.99.0-Master (90ae728)
qgspalettedrasterrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspalettedrasterrenderer.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 "qgsrastertransparency.h"
20 #include "qgsrasterviewport.h"
21 
22 #include <QColor>
23 #include <QDomDocument>
24 #include <QDomElement>
25 #include <QImage>
26 #include <QVector>
27 #include <memory>
28 
30  QColor* colorArray, int nColors, const QVector<QString>& labels ):
31  QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mNColors( nColors ), mLabels( labels )
32 {
33  mColors = new QRgb[nColors];
34  for ( int i = 0; i < nColors; ++i )
35  {
36  mColors[i] = qPremultiply( colorArray[i].rgba() );
37  }
38  delete[] colorArray;
39 }
40 
41 QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface* input, int bandNumber, QRgb* colorArray, int nColors, const QVector<QString>& labels ):
42  QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mColors( colorArray ), mNColors( nColors ), mLabels( labels )
43 {
44 }
45 
47 {
48  delete[] mColors;
49 }
50 
52 {
53  QgsPalettedRasterRenderer * renderer = new QgsPalettedRasterRenderer( nullptr, mBand, rgbArray(), mNColors );
54  renderer->copyCommonProperties( this );
55 
56  renderer->mLabels = mLabels;
57  return renderer;
58 }
59 
61 {
62  if ( elem.isNull() )
63  {
64  return nullptr;
65  }
66 
67  int bandNumber = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
68  int nColors = 0;
69  QRgb* colors = nullptr;
70  QVector<QString> labels;
71 
72  QDomElement paletteElem = elem.firstChildElement( QStringLiteral( "colorPalette" ) );
73  if ( !paletteElem.isNull() )
74  {
75  QDomNodeList paletteEntries = paletteElem.elementsByTagName( QStringLiteral( "paletteEntry" ) );
76 
77  QDomElement entryElem;
78  int value;
79  nColors = 0;
80 
81  // We cannot believe that data are correct, check first max value
82  for ( int i = 0; i < paletteEntries.size(); ++i )
83  {
84  entryElem = paletteEntries.at( i ).toElement();
85  // Could be written as doubles (with .0000) in old project files
86  value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
87  if ( value >= nColors && value <= 10000 ) nColors = value + 1;
88  }
89  QgsDebugMsgLevel( QString( "nColors = %1" ).arg( nColors ), 4 );
90 
91  colors = new QRgb[ nColors ];
92 
93  for ( int i = 0; i < nColors; ++i )
94  {
95  entryElem = paletteEntries.at( i ).toElement();
96  value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
97  QgsDebugMsgLevel( entryElem.attribute( "color", "#000000" ), 4 );
98  if ( value >= 0 && value < nColors )
99  {
100  QColor color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
101  color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
102  colors[value] = qPremultiply( color.rgba() );
103  QString label = entryElem.attribute( QStringLiteral( "label" ) );
104  if ( !label.isEmpty() )
105  {
106  if ( value >= labels.size() ) labels.resize( value + 1 );
107  labels[value] = label;
108  }
109  }
110  else
111  {
112  QgsDebugMsg( QString( "value %1 out of range" ).arg( value ) );
113  }
114  }
115  }
116  QgsPalettedRasterRenderer* r = new QgsPalettedRasterRenderer( input, bandNumber, colors, nColors, labels );
117  r->readXml( elem );
118  return r;
119 }
120 
122 {
123  if ( mNColors < 1 )
124  {
125  return nullptr;
126  }
127  QColor* colorArray = new QColor[ mNColors ];
128  for ( int i = 0; i < mNColors; ++i )
129  {
130  colorArray[i] = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
131  }
132  return colorArray;
133 }
134 
135 QRgb* QgsPalettedRasterRenderer::rgbArray() const
136 {
137  if ( mNColors < 1 )
138  {
139  return nullptr;
140  }
141  QRgb* rgbValues = new QRgb[mNColors];
142  for ( int i = 0; i < mNColors; ++i )
143  {
144  rgbValues[i] = mColors[i];
145  }
146  return rgbValues;
147 }
148 
149 void QgsPalettedRasterRenderer::setLabel( int idx, const QString& label )
150 {
151  if ( idx >= mLabels.size() )
152  {
153  mLabels.resize( idx + 1 );
154  }
155  mLabels[idx] = label;
156 }
157 
158 QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
159 {
160  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
161  if ( !mInput || mNColors == 0 )
162  {
163  return outputBlock.release();
164  }
165 
166  std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNo, extent, width, height, feedback ) );
167 
168  if ( !inputBlock || inputBlock->isEmpty() )
169  {
170  QgsDebugMsg( "No raster data!" );
171  return outputBlock.release();
172  }
173 
174  double currentOpacity = mOpacity;
175 
176  //rendering is faster without considering user-defined transparency
177  bool hasTransparency = usesTransparency();
178 
179  std::shared_ptr< QgsRasterBlock > alphaBlock;
180 
181  if ( mAlphaBand > 0 && mAlphaBand != mBand )
182  {
183  alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
184  if ( !alphaBlock || alphaBlock->isEmpty() )
185  {
186  return outputBlock.release();
187  }
188  }
189  else if ( mAlphaBand == mBand )
190  {
191  alphaBlock = inputBlock;
192  }
193 
194  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
195  {
196  return outputBlock.release();
197  }
198 
199  QRgb myDefaultColor = NODATA_COLOR;
200 
201  //use direct data access instead of QgsRasterBlock::setValue
202  //because of performance
203  unsigned int* outputData = ( unsigned int* )( outputBlock->bits() );
204 
205  qgssize rasterSize = ( qgssize )width * height;
206  for ( qgssize i = 0; i < rasterSize; ++i )
207  {
208  if ( inputBlock->isNoData( i ) )
209  {
210  outputData[i] = myDefaultColor;
211  continue;
212  }
213  int val = ( int ) inputBlock->value( i );
214 
215  if ( !hasTransparency )
216  {
217  outputData[i] = mColors[val];
218  }
219  else
220  {
221  currentOpacity = mOpacity;
222  if ( mRasterTransparency )
223  {
224  currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
225  }
226  if ( mAlphaBand > 0 )
227  {
228  currentOpacity *= alphaBlock->value( i ) / 255.0;
229  }
230 
231  QRgb c = mColors[val];
232  outputData[i] = qRgba( currentOpacity * qRed( c ), currentOpacity * qGreen( c ), currentOpacity * qBlue( c ), currentOpacity * qAlpha( c ) );
233  }
234  }
235 
236  return outputBlock.release();
237 }
238 
239 void QgsPalettedRasterRenderer::writeXml( QDomDocument& doc, QDomElement& parentElem ) const
240 {
241  if ( parentElem.isNull() )
242  {
243  return;
244  }
245 
246  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
247  _writeXml( doc, rasterRendererElem );
248 
249  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
250  QDomElement colorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
251  for ( int i = 0; i < mNColors; ++i )
252  {
253  QColor color = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
254  QDomElement colorElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
255  colorElem.setAttribute( QStringLiteral( "value" ), i );
256  colorElem.setAttribute( QStringLiteral( "color" ), color.name() );
257  colorElem.setAttribute( QStringLiteral( "alpha" ), color.alpha() );
258  if ( !label( i ).isEmpty() )
259  {
260  colorElem.setAttribute( QStringLiteral( "label" ), label( i ) );
261  }
262  colorPaletteElem.appendChild( colorElem );
263  }
264  rasterRendererElem.appendChild( colorPaletteElem );
265 
266  parentElem.appendChild( rasterRendererElem );
267 }
268 
269 void QgsPalettedRasterRenderer::legendSymbologyItems( QList< QPair< QString, QColor > >& symbolItems ) const
270 {
271  for ( int i = 0; i < mNColors; ++i )
272  {
273  QString lab = label( i ).isEmpty() ? QString::number( i ) : label( i );
274  symbolItems.push_back( qMakePair( lab, QColor::fromRgba( qUnpremultiply( mColors[i] ) ) ) );
275  }
276 }
277 
279 {
280  QList<int> bandList;
281  if ( mBand != -1 )
282  {
283  bandList << mBand;
284  }
285  return bandList;
286 }
int alphaValue(double, int globalTransparency=255) const
Returns the transparency value for a single value Pixel.
A rectangle specified with double values.
Definition: qgsrectangle.h:36
Renderer for paletted raster images.
virtual QgsRectangle extent() const
Get the extent of the interface.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
virtual QgsRasterInterface * input() const
Current input.
void setLabel(int idx, const QString &label)
Set category label.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
int nColors() const
Returns number of colors.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
QgsPalettedRasterRenderer(QgsRasterInterface *input, int bandNumber, QColor *colorArray, int nColors, const QVector< QString > &labels=QVector< QString >())
Renderer owns color array.
static const QRgb NODATA_COLOR
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:76
Raster data container.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
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 usesTransparency() const
QString label(int idx) const
Return optional category label.
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:338
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
QColor * colors() const
Returns copy of color array (caller takes ownership)
QgsPalettedRasterRenderer * clone() const override
Clone itself, create deep copy.
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
Get symbology items if provided by renderer.
double mOpacity
Global alpha value (0-1)
QgsRasterInterface * mInput
Feedback object tailored for raster block reading.
Raster renderer pipe that applies colors to a raster.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.