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