QGIS API Documentation  2.99.0-Master (8ec3eaf)
qgsrasterblock.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterblock.cpp - Class representing a block of raster data
3  --------------------------------------
4  Date : Oct 9, 2012
5  Copyright : (C) 2012 by Radim Blazek
6  email : radim dot blazek at gmail dot com
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 
18 #include <limits>
19 
20 #include <QByteArray>
21 #include <QColor>
22 
23 #include "qgslogger.h"
24 #include "qgsrasterblock.h"
25 #include "qgsrectangle.h"
26 
27 // See #9101 before any change of NODATA_COLOR!
28 const QRgb QgsRasterBlock::mNoDataColor = qRgba( 0, 0, 0, 0 );
29 
31  : mValid( true )
32  , mDataType( Qgis::UnknownDataType )
33  , mTypeSize( 0 )
34  , mWidth( 0 )
35  , mHeight( 0 )
36  , mHasNoDataValue( false )
37  , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
38  , mData( nullptr )
39  , mImage( nullptr )
40  , mNoDataBitmap( nullptr )
41  , mNoDataBitmapWidth( 0 )
42  , mNoDataBitmapSize( 0 )
43 {
44 }
45 
46 QgsRasterBlock::QgsRasterBlock( Qgis::DataType theDataType, int theWidth, int theHeight )
47  : mValid( true )
48  , mDataType( theDataType )
49  , mTypeSize( 0 )
50  , mWidth( theWidth )
51  , mHeight( theHeight )
52  , mHasNoDataValue( false )
53  , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
54  , mData( nullptr )
55  , mImage( nullptr )
56  , mNoDataBitmap( nullptr )
57  , mNoDataBitmapWidth( 0 )
58  , mNoDataBitmapSize( 0 )
59 {
60  ( void )reset( mDataType, mWidth, mHeight );
61 }
62 
63 QgsRasterBlock::QgsRasterBlock( Qgis::DataType theDataType, int theWidth, int theHeight, double theNoDataValue )
64  : mValid( true )
65  , mDataType( theDataType )
66  , mTypeSize( 0 )
67  , mWidth( theWidth )
68  , mHeight( theHeight )
69  , mHasNoDataValue( true )
70  , mNoDataValue( theNoDataValue )
71  , mData( nullptr )
72  , mImage( nullptr )
73  , mNoDataBitmap( nullptr )
74  , mNoDataBitmapWidth( 0 )
75  , mNoDataBitmapSize( 0 )
76 {
77  ( void )reset( mDataType, mWidth, mHeight, mNoDataValue );
78 }
79 
81 {
82  QgsDebugMsgLevel( QString( "mData = %1" ).arg( reinterpret_cast< ulong >( mData ) ), 4 );
83  qgsFree( mData );
84  delete mImage;
85  qgsFree( mNoDataBitmap );
86 }
87 
88 bool QgsRasterBlock::reset( Qgis::DataType theDataType, int theWidth, int theHeight )
89 {
90  QgsDebugMsgLevel( QString( "theWidth= %1 theHeight = %2 theDataType = %3" ).arg( theWidth ).arg( theHeight ).arg( theDataType ), 4 );
91  if ( !reset( theDataType, theWidth, theHeight, std::numeric_limits<double>::quiet_NaN() ) )
92  {
93  return false;
94  }
95  mHasNoDataValue = false;
96  // the mNoDataBitmap is created only if necessary (usually, it is not) in setIsNoData()
97  return true;
98 }
99 
100 bool QgsRasterBlock::reset( Qgis::DataType theDataType, int theWidth, int theHeight, double theNoDataValue )
101 {
102  QgsDebugMsgLevel( QString( "theWidth= %1 theHeight = %2 theDataType = %3 theNoDataValue = %4" ).arg( theWidth ).arg( theHeight ).arg( theDataType ).arg( theNoDataValue ), 4 );
103 
104  qgsFree( mData );
105  mData = nullptr;
106  delete mImage;
107  mImage = nullptr;
108  qgsFree( mNoDataBitmap );
109  mNoDataBitmap = nullptr;
110  mDataType = Qgis::UnknownDataType;
111  mTypeSize = 0;
112  mWidth = 0;
113  mHeight = 0;
114  mHasNoDataValue = false;
115  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
116  mValid = false;
117 
118  if ( typeIsNumeric( theDataType ) )
119  {
120  QgsDebugMsgLevel( "Numeric type", 4 );
121  qgssize tSize = typeSize( theDataType );
122  QgsDebugMsgLevel( QString( "allocate %1 bytes" ).arg( tSize * theWidth * theHeight ), 4 );
123  mData = qgsMalloc( tSize * theWidth * theHeight );
124  if ( !mData )
125  {
126  QgsDebugMsg( QString( "Couldn't allocate data memory of %1 bytes" ).arg( tSize * theWidth * theHeight ) );
127  return false;
128  }
129  }
130  else if ( typeIsColor( theDataType ) )
131  {
132  QgsDebugMsgLevel( "Color type", 4 );
133  QImage::Format format = imageFormat( theDataType );
134  mImage = new QImage( theWidth, theHeight, format );
135  }
136  else
137  {
138  QgsDebugMsg( "Wrong data type" );
139  return false;
140  }
141 
142  mValid = true;
143  mDataType = theDataType;
144  mTypeSize = QgsRasterBlock::typeSize( mDataType );
145  mWidth = theWidth;
146  mHeight = theHeight;
147  mHasNoDataValue = true;
148  mNoDataValue = theNoDataValue;
149  QgsDebugMsgLevel( QString( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
150  .arg( reinterpret_cast< ulong >( mData ) ).arg( reinterpret_cast< ulong >( mImage ) ), 4 );
151  return true;
152 }
153 
154 QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType theDataType )
155 {
156  if ( theDataType == Qgis::ARGB32 )
157  {
158  return QImage::Format_ARGB32;
159  }
160  else if ( theDataType == Qgis::ARGB32_Premultiplied )
161  {
162  return QImage::Format_ARGB32_Premultiplied;
163  }
164  return QImage::Format_Invalid;
165 }
166 
167 Qgis::DataType QgsRasterBlock::dataType( QImage::Format theFormat )
168 {
169  if ( theFormat == QImage::Format_ARGB32 )
170  {
171  return Qgis::ARGB32;
172  }
173  else if ( theFormat == QImage::Format_ARGB32_Premultiplied )
174  {
176  }
177  return Qgis::UnknownDataType;
178 }
179 
181 {
182  QgsDebugMsgLevel( QString( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
183  .arg( reinterpret_cast< ulong >( mData ) ).arg( reinterpret_cast< ulong >( mImage ) ), 4 );
184  if ( mWidth == 0 || mHeight == 0 ||
185  ( typeIsNumeric( mDataType ) && !mData ) ||
186  ( typeIsColor( mDataType ) && !mImage ) )
187  {
188  return true;
189  }
190  return false;
191 }
192 
194 {
195  switch ( dataType )
196  {
197  case Qgis::Byte:
198  case Qgis::UInt16:
199  case Qgis::Int16:
200  case Qgis::UInt32:
201  case Qgis::Int32:
202  case Qgis::Float32:
203  case Qgis::CInt16:
204  case Qgis::Float64:
205  case Qgis::CInt32:
206  case Qgis::CFloat32:
207  case Qgis::CFloat64:
208  return true;
209 
211  case Qgis::ARGB32:
213  return false;
214  }
215  return false;
216 }
217 
219 {
220  switch ( dataType )
221  {
222  case Qgis::ARGB32:
224  return true;
225 
227  case Qgis::Byte:
228  case Qgis::UInt16:
229  case Qgis::Int16:
230  case Qgis::UInt32:
231  case Qgis::Int32:
232  case Qgis::Float32:
233  case Qgis::CInt16:
234  case Qgis::Float64:
235  case Qgis::CInt32:
236  case Qgis::CFloat32:
237  case Qgis::CFloat64:
238  return false;
239  }
240  return false;
241 }
242 
244 {
245  Qgis::DataType newDataType;
246 
247  switch ( dataType )
248  {
249  case Qgis::Byte:
250  *noDataValue = -32768.0;
251  newDataType = Qgis::Int16;
252  break;
253  case Qgis::Int16:
254  *noDataValue = -2147483648.0;
255  newDataType = Qgis::Int32;
256  break;
257  case Qgis::UInt16:
258  *noDataValue = -2147483648.0;
259  newDataType = Qgis::Int32;
260  break;
261  case Qgis::UInt32:
262  case Qgis::Int32:
263  case Qgis::Float32:
264  case Qgis::Float64:
265  *noDataValue = std::numeric_limits<double>::max() * -1.0;
266  newDataType = Qgis::Float64;
267  break;
268  default:
269  QgsDebugMsg( QString( "Unknown data type %1" ).arg( dataType ) );
270  return Qgis::UnknownDataType;
271  }
272  QgsDebugMsgLevel( QString( "newDataType = %1 noDataValue = %2" ).arg( newDataType ).arg( *noDataValue ), 4 );
273  return newDataType;
274 }
275 
277 {
278  return mHasNoDataValue || mNoDataBitmap;
279 }
280 
281 bool QgsRasterBlock::isNoDataValue( double value, double noDataValue )
282 {
283  // TODO: optimize no data value test by memcmp()
284  // More precise would be qIsNaN(value) && qIsNaN(noDataValue(bandNo)), but probably
285  // not important and slower
286  if ( qIsNaN( value ) ||
287  qgsDoubleNear( value, noDataValue ) )
288  {
289  return true;
290  }
291  return false;
292 }
293 
294 double QgsRasterBlock::value( int row, int column ) const
295 {
296  return value( static_cast< qgssize >( row ) *mWidth + column );
297 }
298 
300 {
301  int row = floor( static_cast< double >( index ) / mWidth );
302  int column = index % mWidth;
303  return color( row, column );
304 }
305 
306 QRgb QgsRasterBlock::color( int row, int column ) const
307 {
308  if ( !mImage ) return mNoDataColor;
309 
310  return mImage->pixel( column, row );
311 }
312 
314 {
315  if ( !mHasNoDataValue && !mNoDataBitmap ) return false;
316  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
317  {
318  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
319  return true; // we consider no data if outside
320  }
321  if ( mHasNoDataValue )
322  {
323  double value = readValue( mData, mDataType, index );
324  return isNoDataValue( value );
325  }
326  // use no data bitmap
327  if ( !mNoDataBitmap )
328  {
329  // no data are not defined
330  return false;
331  }
332  // TODO: optimize
333  int row = static_cast< int >( index ) / mWidth;
334  int column = index % mWidth;
335  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
336  int bit = column % 8;
337  int mask = 0x80 >> bit;
338  //int x = mNoDataBitmap[byte] & mask;
339  //QgsDebugMsg ( QString("byte = %1 bit = %2 mask = %3 nodata = %4 is nodata = %5").arg(byte).arg(bit).arg(mask, 0, 2 ).arg( x, 0, 2 ).arg( (bool)(x) ) );
340  return mNoDataBitmap[byte] & mask;
341 }
342 
343 bool QgsRasterBlock::isNoData( int row, int column )
344 {
345  return isNoData( static_cast< qgssize >( row )*mWidth + column );
346 }
347 
349 {
350  if ( !mData )
351  {
352  QgsDebugMsg( "Data block not allocated" );
353  return false;
354  }
355  if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
356  {
357  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
358  return false;
359  }
360  writeValue( mData, mDataType, index, value );
361  return true;
362 }
363 
364 bool QgsRasterBlock::setValue( int row, int column, double value )
365 {
366  return setValue( static_cast< qgssize >( row )*mWidth + column, value );
367 }
368 
369 bool QgsRasterBlock::setColor( int row, int column, QRgb color )
370 {
371  return setColor( static_cast< qgssize >( row ) *mWidth + column, color );
372 }
373 
375 {
376  if ( !mImage )
377  {
378  QgsDebugMsg( "Image not allocated" );
379  return false;
380  }
381 
382  if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
383  {
384  QgsDebugMsg( QString( "index %1 out of range" ).arg( index ) );
385  return false;
386  }
387 
388  // setPixel() is slow, see Qt doc -> use direct access
389  QRgb* bits = reinterpret_cast< QRgb* >( mImage->bits() );
390  bits[index] = color;
391  return true;
392 }
393 
394 bool QgsRasterBlock::setIsNoData( int row, int column )
395 {
396  return setIsNoData( static_cast< qgssize >( row )*mWidth + column );
397 }
398 
400 {
401  if ( mHasNoDataValue )
402  {
403  return setValue( index, mNoDataValue );
404  }
405  else
406  {
407  if ( !mNoDataBitmap )
408  {
409  if ( !createNoDataBitmap() )
410  {
411  return false;
412  }
413  }
414  // TODO: optimize
415  int row = static_cast< int >( index ) / mWidth;
416  int column = index % mWidth;
417  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
418  int bit = column % 8;
419  int nodata = 0x80 >> bit;
420  //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
421  mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
422  return true;
423  }
424 }
425 
427 {
428  QgsDebugMsgLevel( "Entered", 4 );
429  if ( typeIsNumeric( mDataType ) )
430  {
431  if ( mHasNoDataValue )
432  {
433  if ( !mData )
434  {
435  QgsDebugMsg( "Data block not allocated" );
436  return false;
437  }
438 
439  QgsDebugMsgLevel( "set mData to mNoDataValue", 4 );
440  int dataTypeSize = typeSize( mDataType );
441  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
442 
443  char *nodata = noDataByteArray.data();
444  for ( qgssize i = 0; i < static_cast< qgssize >( mWidth )*mHeight; i++ )
445  {
446  memcpy( reinterpret_cast< char* >( mData ) + i*dataTypeSize, nodata, dataTypeSize );
447  }
448  }
449  else
450  {
451  // use bitmap
452  if ( !mNoDataBitmap )
453  {
454  if ( !createNoDataBitmap() )
455  {
456  return false;
457  }
458  }
459  QgsDebugMsgLevel( "set mNoDataBitmap to 1", 4 );
460  memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
461  }
462  return true;
463  }
464  else
465  {
466  // image
467  if ( !mImage )
468  {
469  QgsDebugMsg( "Image not allocated" );
470  return false;
471  }
472  QgsDebugMsgLevel( "Fill image", 4 );
473  mImage->fill( mNoDataColor );
474  return true;
475  }
476 }
477 
478 bool QgsRasterBlock::setIsNoDataExcept( QRect theExceptRect )
479 {
480  int top = theExceptRect.top();
481  int bottom = theExceptRect.bottom();
482  int left = theExceptRect.left();
483  int right = theExceptRect.right();
484  top = qMin( qMax( top, 0 ), mHeight - 1 );
485  left = qMin( qMax( left, 0 ), mWidth - 1 );
486  bottom = qMax( 0, qMin( bottom, mHeight - 1 ) );
487  right = qMax( 0, qMin( right, mWidth - 1 ) );
488 
489  QgsDebugMsgLevel( "Entered", 4 );
490  if ( typeIsNumeric( mDataType ) )
491  {
492  if ( mHasNoDataValue )
493  {
494  if ( !mData )
495  {
496  QgsDebugMsg( "Data block not allocated" );
497  return false;
498  }
499 
500  QgsDebugMsgLevel( "set mData to mNoDataValue", 4 );
501  int dataTypeSize = typeSize( mDataType );
502  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
503 
504  char *nodata = noDataByteArray.data();
505  char *nodataRow = new char[mWidth*dataTypeSize]; // full row of no data
506  for ( int c = 0; c < mWidth; c++ )
507  {
508  memcpy( nodataRow + c*dataTypeSize, nodata, dataTypeSize );
509  }
510 
511  // top and bottom
512  for ( int r = 0; r < mHeight; r++ )
513  {
514  if ( r >= top && r <= bottom ) continue; // middle
515  qgssize i = static_cast< qgssize >( r ) * mWidth;
516  memcpy( reinterpret_cast< char* >( mData ) + i*dataTypeSize, nodataRow, dataTypeSize*mWidth );
517  }
518  // middle
519  for ( int r = top; r <= bottom; r++ )
520  {
521  qgssize i = static_cast< qgssize >( r ) * mWidth;
522  // middle left
523  memcpy( reinterpret_cast< char* >( mData ) + i*dataTypeSize, nodataRow, dataTypeSize*left );
524  // middle right
525  i += right + 1;
526  int w = mWidth - right - 1;
527  memcpy( reinterpret_cast< char* >( mData ) + i*dataTypeSize, nodataRow, dataTypeSize*w );
528  }
529  delete [] nodataRow;
530  }
531  else
532  {
533  // use bitmap
534  if ( !mNoDataBitmap )
535  {
536  if ( !createNoDataBitmap() )
537  {
538  return false;
539  }
540  }
541  QgsDebugMsgLevel( "set mNoDataBitmap to 1", 4 );
542 
543  char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
544  // TODO: we can simply set all bytes to 11111111 (~0) I think
545  memset( nodataRow, 0, mNoDataBitmapWidth );
546  for ( int c = 0; c < mWidth; c ++ )
547  {
548  int byte = c / 8;
549  int bit = c % 8;
550  char nodata = 0x80 >> bit;
551  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
552  }
553 
554  // top and bottom
555  for ( int r = 0; r < mHeight; r++ )
556  {
557  if ( r >= top && r <= bottom ) continue; // middle
558  qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
559  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
560  }
561  // middle
562  memset( nodataRow, 0, mNoDataBitmapWidth );
563  for ( int c = 0; c < mWidth; c ++ )
564  {
565  if ( c >= left && c <= right ) continue; // middle
566  int byte = c / 8;
567  int bit = c % 8;
568  char nodata = 0x80 >> bit;
569  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
570  }
571  for ( int r = top; r <= bottom; r++ )
572  {
573  qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
574  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
575  }
576  delete [] nodataRow;
577  }
578  return true;
579  }
580  else
581  {
582  // image
583  if ( !mImage )
584  {
585  QgsDebugMsg( "Image not allocated" );
586  return false;
587  }
588 
589  if ( mImage->width() != mWidth || mImage->height() != mHeight )
590  {
591  QgsDebugMsg( "Image and block size differ" );
592  return false;
593  }
594 
595  QgsDebugMsgLevel( QString( "Fill image depth = %1" ).arg( mImage->depth() ), 4 );
596 
597  // TODO: support different depths
598  if ( mImage->depth() != 32 )
599  {
600  QgsDebugMsg( "Unsupported image depth" );
601  return false;
602  }
603 
604  QRgb nodataRgba = mNoDataColor;
605  QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
606  int rgbSize = sizeof( QRgb );
607  for ( int c = 0; c < mWidth; c ++ )
608  {
609  nodataRow[c] = nodataRgba;
610  }
611 
612  // top and bottom
613  for ( int r = 0; r < mHeight; r++ )
614  {
615  if ( r >= top && r <= bottom ) continue; // middle
616  qgssize i = static_cast< qgssize >( r ) * mWidth;
617  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*mWidth );
618  }
619  // middle
620  for ( int r = top; r <= bottom; r++ )
621  {
622  qgssize i = static_cast< qgssize >( r ) * mWidth;
623  // middle left
624  if ( left > 0 )
625  {
626  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*( left - 1 ) );
627  }
628  // middle right
629  i += right + 1;
630  int w = mWidth - right - 1;
631  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*w );
632  }
633  delete [] nodataRow;
634  return true;
635  }
636 }
637 
638 void QgsRasterBlock::setIsData( int row, int column )
639 {
640  setIsData( static_cast< qgssize >( row )*mWidth + column );
641 }
642 
644 {
645  if ( mHasNoDataValue )
646  {
647  //no data value set, so mNoDataBitmap is not being used
648  return;
649  }
650 
651  if ( !mNoDataBitmap )
652  {
653  return;
654  }
655 
656  // TODO: optimize
657  int row = static_cast< int >( index ) / mWidth;
658  int column = index % mWidth;
659  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
660  int bit = column % 8;
661  int nodata = 0x80 >> bit;
662  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
663 }
664 
666 {
667  // Not testing type to avoid too much overhead because this method is called per pixel
668  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
669  {
670  QgsDebugMsgLevel( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
671  return nullptr;
672  }
673  if ( mData )
674  {
675  return reinterpret_cast< char* >( mData ) + index * mTypeSize;
676  }
677  if ( mImage && mImage->bits() )
678  {
679  return reinterpret_cast< char* >( mImage->bits() + index * 4 );
680  }
681 
682  return nullptr;
683 }
684 
685 char * QgsRasterBlock::bits( int row, int column )
686 {
687  return bits( static_cast< qgssize >( row )*mWidth + column );
688 }
689 
691 {
692  if ( mData )
693  {
694  return reinterpret_cast< char* >( mData );
695  }
696  if ( mImage && mImage->bits() )
697  {
698  return reinterpret_cast< char* >( mImage->bits() );
699  }
700 
701  return nullptr;
702 }
703 
705 {
706  if ( isEmpty() ) return false;
707  if ( destDataType == mDataType ) return true;
708 
709  if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
710  {
711  void *data = convert( mData, mDataType, destDataType, static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight ) );
712 
713  if ( !data )
714  {
715  QgsDebugMsg( "Cannot convert raster block" );
716  return false;
717  }
718  qgsFree( mData );
719  mData = data;
720  mDataType = destDataType;
721  mTypeSize = typeSize( mDataType );
722  }
723  else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
724  {
725  QImage::Format format = imageFormat( destDataType );
726  QImage image = mImage->convertToFormat( format );
727  *mImage = image;
728  mDataType = destDataType;
729  mTypeSize = typeSize( mDataType );
730  }
731  else
732  {
733  return false;
734  }
735 
736  return true;
737 }
738 
739 void QgsRasterBlock::applyScaleOffset( double scale, double offset )
740 {
741  if ( isEmpty() ) return;
742  if ( !typeIsNumeric( mDataType ) ) return;
743  if ( scale == 1.0 && offset == 0.0 ) return;
744 
745  qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
746  for ( qgssize i = 0; i < size; ++i )
747  {
748  if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
749  }
750  return;
751 }
752 
754 {
755  if ( rangeList.isEmpty() )
756  {
757  return;
758  }
759 
760  qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
761  for ( qgssize i = 0; i < size; ++i )
762  {
763  double val = value( i );
764  if ( QgsRasterRange::contains( val, rangeList ) )
765  {
766  //setValue( i, mNoDataValue );
767  setIsNoData( i );
768  }
769  }
770 }
771 
772 QImage QgsRasterBlock::image() const
773 {
774  if ( mImage )
775  {
776  return QImage( *mImage );
777  }
778  return QImage();
779 }
780 
781 bool QgsRasterBlock::setImage( const QImage * image )
782 {
783  qgsFree( mData );
784  mData = nullptr;
785  delete mImage;
786  mImage = nullptr;
787  mImage = new QImage( *image );
788  mWidth = mImage->width();
789  mHeight = mImage->height();
790  mDataType = dataType( mImage->format() );
791  mTypeSize = QgsRasterBlock::typeSize( mDataType );
792  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
793  return true;
794 }
795 
797 {
798  /*
799  * IEEE 754 double has 15-17 significant digits. It specifies:
800  *
801  * "If a decimal string with at most 15 significant decimal is converted to
802  * IEEE 754 double precision and then converted back to the same number of
803  * significant decimal, then the final string should match the original;
804  * and if an IEEE 754 double precision is converted to a decimal string with at
805  * least 17 significant decimal and then converted back to double, then the final
806  * number must match the original."
807  *
808  * If printing only 15 digits, some precision could be lost. Printing 17 digits may
809  * add some confusing digits.
810  *
811  * Default 'g' precision on linux is 6 digits, not all significant digits like
812  * some sprintf manuals say.
813  *
814  * We need to ensure that the number printed and used in QLineEdit or XML will
815  * give the same number when parsed.
816  *
817  * Is there a better solution?
818  */
819 
820  QString s;
821 
822  for ( int i = 15; i <= 17; i++ )
823  {
824  s.setNum( value, 'g', i );
825  if ( qgsDoubleNear( s.toDouble(), value ) )
826  {
827  return s;
828  }
829  }
830  // Should not happen
831  QgsDebugMsg( "Cannot correctly parse printed value" );
832  return s;
833 }
834 
836 {
837  /*
838  * IEEE 754 double has 6-9 significant digits. See printValue(double)
839  */
840 
841  QString s;
842 
843  for ( int i = 6; i <= 9; i++ )
844  {
845  s.setNum( value, 'g', i );
846  if ( qgsFloatNear( s.toFloat(), value ) )
847  {
848  return s;
849  }
850  }
851  // Should not happen
852  QgsDebugMsg( "Cannot correctly parse printed value" );
853  return s;
854 }
855 
856 void * QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
857 {
858  int destDataTypeSize = typeSize( destDataType );
859  void *destData = qgsMalloc( destDataTypeSize * size );
860  for ( qgssize i = 0; i < size; i++ )
861  {
862  double value = readValue( srcData, srcDataType, i );
863  writeValue( destData, destDataType, i, value );
864  //double newValue = readValue( destData, destDataType, i );
865  //QgsDebugMsg( QString("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) );
866  }
867  return destData;
868 }
869 
870 QByteArray QgsRasterBlock::valueBytes( Qgis::DataType theDataType, double theValue )
871 {
872  qgssize size = QgsRasterBlock::typeSize( theDataType );
873  QByteArray ba;
874  ba.resize( static_cast< int >( size ) );
875  char * data = ba.data();
876  quint8 uc;
877  quint16 us;
878  qint16 s;
879  quint32 ui;
880  qint32 i;
881  float f;
882  double d;
883  switch ( theDataType )
884  {
885  case Qgis::Byte:
886  uc = static_cast< quint8 >( theValue );
887  memcpy( data, &uc, size );
888  break;
889  case Qgis::UInt16:
890  us = static_cast< quint16 >( theValue );
891  memcpy( data, &us, size );
892  break;
893  case Qgis::Int16:
894  s = static_cast< qint16 >( theValue );
895  memcpy( data, &s, size );
896  break;
897  case Qgis::UInt32:
898  ui = static_cast< quint32 >( theValue );
899  memcpy( data, &ui, size );
900  break;
901  case Qgis::Int32:
902  i = static_cast< qint32 >( theValue );
903  memcpy( data, &i, size );
904  break;
905  case Qgis::Float32:
906  f = static_cast< float >( theValue );
907  memcpy( data, &f, size );
908  break;
909  case Qgis::Float64:
910  d = static_cast< double >( theValue );
911  memcpy( data, &d, size );
912  break;
913  default:
914  QgsDebugMsg( "Data type is not supported" );
915  }
916  return ba;
917 }
918 
919 bool QgsRasterBlock::createNoDataBitmap()
920 {
921  mNoDataBitmapWidth = mWidth / 8 + 1;
922  mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
923  QgsDebugMsgLevel( QString( "allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
924  mNoDataBitmap = reinterpret_cast< char* >( qgsMalloc( mNoDataBitmapSize ) );
925  if ( !mNoDataBitmap )
926  {
927  QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
928  return false;
929  }
930  memset( mNoDataBitmap, 0, mNoDataBitmapSize );
931  return true;
932 }
933 
935 {
936  return QStringLiteral( "dataType = %1 width = %2 height = %3" )
937  .arg( mDataType ).arg( mWidth ).arg( mHeight );
938 }
939 
940 QRect QgsRasterBlock::subRect( const QgsRectangle & theExtent, int theWidth, int theHeight, const QgsRectangle & theSubExtent )
941 {
942  QgsDebugMsgLevel( "theExtent = " + theExtent.toString(), 4 );
943  QgsDebugMsgLevel( "theSubExtent = " + theSubExtent.toString(), 4 );
944  double xRes = theExtent.width() / theWidth;
945  double yRes = theExtent.height() / theHeight;
946 
947  QgsDebugMsgLevel( QString( "theWidth = %1 theHeight = %2 xRes = %3 yRes = %4" ).arg( theWidth ).arg( theHeight ).arg( xRes ).arg( yRes ), 4 );
948 
949  int top = 0;
950  int bottom = theHeight - 1;
951  int left = 0;
952  int right = theWidth - 1;
953 
954  if ( theSubExtent.yMaximum() < theExtent.yMaximum() )
955  {
956  top = qRound(( theExtent.yMaximum() - theSubExtent.yMaximum() ) / yRes );
957  }
958  if ( theSubExtent.yMinimum() > theExtent.yMinimum() )
959  {
960  bottom = qRound(( theExtent.yMaximum() - theSubExtent.yMinimum() ) / yRes ) - 1;
961  }
962 
963  if ( theSubExtent.xMinimum() > theExtent.xMinimum() )
964  {
965  left = qRound(( theSubExtent.xMinimum() - theExtent.xMinimum() ) / xRes );
966  }
967  if ( theSubExtent.xMaximum() < theExtent.xMaximum() )
968  {
969  right = qRound(( theSubExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
970  }
971  QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
972  QgsDebugMsgLevel( QString( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
973  return subRect;
974 }
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Thirty two bit signed integer (qint32)
Definition: qgis.h:67
static QString printValue(double value)
Print double value with all necessary significant digits.
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
Definition: qgis.cpp:105
bool setIsNoData()
Set the whole block to no data.
static bool contains(double value, const QgsRasterRangeList &rangeList)
Test if value is within the list of ranges.
static double readValue(void *data, Qgis::DataType type, qgssize index)
Qgis::DataType dataType() const
Returns data type.
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition: qgis.h:206
static Qgis::DataType typeWithNoDataValue(Qgis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
static bool typeIsColor(Qgis::DataType type)
Returns true if data type is color.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void applyNoDataValues(const QgsRasterRangeList &rangeList)
bool setValue(int row, int column, double value)
Set value on position.
static bool typeIsNumeric(Qgis::DataType type)
Returns true if data type is numeric.
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:66
DataType
Raster data types.
Definition: qgis.h:60
static QByteArray valueBytes(Qgis::DataType theDataType, double theValue)
Get byte array representing a value.
Thirty two bit floating point (float)
Definition: qgis.h:68
Sixteen bit signed integer (qint16)
Definition: qgis.h:65
bool isNoData(int row, int column)
Check if value at position is no data.
Complex Int16.
Definition: qgis.h:70
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:196
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:40
Sixty four bit floating point (double)
Definition: qgis.h:69
bool setColor(int row, int column, QRgb color)
Set color on position.
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:75
virtual ~QgsRasterBlock()
bool convert(Qgis::DataType destDataType)
Convert data to different type.
bool setImage(const QImage *image)
set image.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:211
Complex Float32.
Definition: qgis.h:72
QRgb color(int row, int column) const
Read a single color.
bool setIsNoDataExcept(QRect theExceptRect)
Set the whole block to no data except specified rectangle.
void setIsData(int row, int column)
Remove no data flag on pixel.
char * bits()
Get pointer to data.
Unknown or unspecified type.
Definition: qgis.h:62
Complex Int32.
Definition: qgis.h:71
static int typeSize(int dataType)
bool isEmpty() const
Returns true if block is empty, i.e.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:64
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:437
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
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:206
bool reset(Qgis::DataType theDataType, int theWidth, int theHeight)
Reset block.
QImage image() const
Get image if type is color.
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:191
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
QList< QgsRasterRange > QgsRasterRangeList
void applyScaleOffset(double scale, double offset)
Apply band scale and offset to raster block values @note added in 2.3.
double value(int row, int column) const
Read a single value if type of block is numeric.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:196
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:201
int dataTypeSize() const
static QRect subRect(const QgsRectangle &theExtent, int theWidth, int theHeight, const QgsRectangle &theSubExtent)
For theExtent and theWidht, theHeight find rectangle covered by subextent.
Complex Float64.
Definition: qgis.h:73
bool hasNoData() const
Returns true if the block may contain no data.
double noDataValue() const
Return no data value.
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:135
Eight bit unsigned integer (quint8)
Definition: qgis.h:63
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:74
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:216
QString toString() const